package control

import (
	"encoding/json"
	"fmt"
	"os"
	"sync"
)

var (
	global   *Control
	mu       sync.RWMutex
	filename string
)

// Control represents the game control settings
type Control struct {
	WorldSize     int    `json:"world_size"`
	Open          int    `json:"open"`
	AdminEmail    string `json:"admin_email"`
	EmailMode     string `json:"email_mode"`      // "file" or "smtp"
	EmailFilePath string `json:"email_file_path"` // path for file mode
	SMTPHost      string `json:"smtp_host"`
	SMTPPort      string `json:"smtp_port"`
	SMTPUsername  string `json:"smtp_username"`
	SMTPPassword  string `json:"smtp_password"`
}

// Init loads control settings from the specified JSON file
func Init(jsonFile string) error {
	mu.Lock()
	defer mu.Unlock()

	filename = jsonFile

	// Try to load from file
	if data, err := os.ReadFile(filename); err == nil {
		var ctrl Control
		if err := json.Unmarshal(data, &ctrl); err != nil {
			return fmt.Errorf("failed to parse JSON: %w", err)
		}

		// Apply defaults for any missing fields
		defaults := New()
		if ctrl.WorldSize == 0 {
			ctrl.WorldSize = defaults.WorldSize
		}
		if ctrl.EmailMode == "" {
			ctrl.EmailMode = defaults.EmailMode
		}
		if ctrl.EmailFilePath == "" {
			ctrl.EmailFilePath = defaults.EmailFilePath
		}

		global = &ctrl
	} else {
		// Create default control settings if file doesn't exist
		global = New()
		if err := save(); err != nil {
			return fmt.Errorf("failed to create default config file: %w", err)
		}
	}

	return global.Validate()
}

// save writes the current control settings to the JSON file (internal use)
func save() error {
	if filename == "" {
		return fmt.Errorf("no filename set")
	}

	data, err := json.MarshalIndent(global, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal JSON: %w", err)
	}

	return os.WriteFile(filename, data, 0644)
}

// Save writes the current control settings to the JSON file (public)
func Save() error {
	mu.RLock()
	defer mu.RUnlock()

	if global == nil {
		return fmt.Errorf("control not initialized")
	}

	return save()
}

// New creates a new Control with sensible defaults
func New() *Control {
	return &Control{
		WorldSize:     200,
		Open:          1,
		AdminEmail:    "",
		EmailMode:     "file",
		EmailFilePath: "emails.txt",
		SMTPHost:      "",
		SMTPPort:      "587",
		SMTPUsername:  "",
		SMTPPassword:  "",
	}
}

// Get returns the global control instance (thread-safe)
func Get() *Control {
	mu.RLock()
	defer mu.RUnlock()
	if global == nil {
		panic("control not initialized - call Init first")
	}
	return global
}

// Set updates the global control instance and saves to file (thread-safe)
func Set(control *Control) error {
	mu.Lock()
	defer mu.Unlock()

	if err := control.Validate(); err != nil {
		return err
	}

	global = control
	return save()
}

// Update updates specific fields of the control settings and saves to file
func Update(updater func(*Control)) error {
	mu.Lock()
	defer mu.Unlock()

	// Create a copy to work with
	updated := *global
	updater(&updated)

	if err := updated.Validate(); err != nil {
		return err
	}

	global = &updated
	return save()
}

// UpdateNoSave updates specific fields without saving (useful for batch updates)
func UpdateNoSave(updater func(*Control)) error {
	mu.Lock()
	defer mu.Unlock()

	// Create a copy to work with
	updated := *global
	updater(&updated)

	if err := updated.Validate(); err != nil {
		return err
	}

	global = &updated
	return nil
}

// Validate checks if control settings have valid values
func (c *Control) Validate() error {
	if c.WorldSize <= 0 || c.WorldSize > 10000 {
		return fmt.Errorf("WorldSize must be between 1 and 10000")
	}
	if c.Open != 0 && c.Open != 1 {
		return fmt.Errorf("Open must be 0 or 1")
	}
	if c.EmailMode != "file" && c.EmailMode != "smtp" {
		return fmt.Errorf("EmailMode must be 'file' or 'smtp'")
	}
	if c.EmailMode == "smtp" {
		if c.SMTPHost == "" {
			return fmt.Errorf("SMTPHost required when EmailMode is 'smtp'")
		}
		if c.SMTPUsername == "" {
			return fmt.Errorf("SMTPUsername required when EmailMode is 'smtp'")
		}
		if c.SMTPPassword == "" {
			return fmt.Errorf("SMTPPassword required when EmailMode is 'smtp'")
		}
	}
	return nil
}

// IsOpen returns true if the game world is open for new players
func (c *Control) IsOpen() bool {
	return c.Open == 1
}

// SetOpen sets whether the game world is open for new players
func SetOpen(open bool) error {
	return Update(func(c *Control) {
		if open {
			c.Open = 1
		} else {
			c.Open = 0
		}
	})
}

// HasAdminEmail returns true if an admin email is configured
func (c *Control) HasAdminEmail() bool {
	return c.AdminEmail != ""
}

// IsEmailConfigured returns true if email is properly configured
func (c *Control) IsEmailConfigured() bool {
	if c.EmailMode == "file" {
		return c.EmailFilePath != ""
	}
	return c.SMTPHost != "" && c.SMTPUsername != "" && c.SMTPPassword != ""
}

// IsWorldSizeValid returns true if the world size is within reasonable bounds
func (c *Control) IsWorldSizeValid() bool {
	return c.WorldSize > 0 && c.WorldSize <= 10000
}

// GetWorldRadius returns the world radius (half the world size)
func (c *Control) GetWorldRadius() int {
	return c.WorldSize / 2
}

// IsWithinWorldBounds returns true if the given coordinates are within world bounds
func (c *Control) IsWithinWorldBounds(x, y int) bool {
	radius := c.GetWorldRadius()
	return x >= -radius && x <= radius && y >= -radius && y <= radius
}

// GetWorldBounds returns the minimum and maximum coordinates for the world
func (c *Control) GetWorldBounds() (minX, minY, maxX, maxY int) {
	radius := c.GetWorldRadius()
	return -radius, -radius, radius, radius
}
