package sandbox

import (
	"fmt"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v3"
)

// Config holds sandbox configuration, stored at ~/.orama/sandbox.yaml.
type Config struct {
	HetznerAPIToken string      `yaml:"hetzner_api_token"`
	Domain          string      `yaml:"domain"`
	Location        string      `yaml:"location"`    // Hetzner datacenter (default: fsn1)
	ServerType      string      `yaml:"server_type"` // Hetzner server type (default: cx22)
	FloatingIPs     []FloatIP   `yaml:"floating_ips"`
	SSHKey          SSHKeyConfig `yaml:"ssh_key"`
	FirewallID      int64       `yaml:"firewall_id,omitempty"` // Hetzner firewall resource ID
}

// FloatIP holds a Hetzner floating IP reference.
type FloatIP struct {
	ID int64  `yaml:"id"`
	IP string `yaml:"ip"`
}

// SSHKeyConfig holds the wallet vault target and Hetzner resource ID.
type SSHKeyConfig struct {
	HetznerID   int64  `yaml:"hetzner_id"`
	VaultTarget string `yaml:"vault_target"` // e.g. "sandbox/root"
}

// configDir returns ~/.orama/, creating it if needed.
func configDir() (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", fmt.Errorf("get home directory: %w", err)
	}
	dir := filepath.Join(home, ".orama")
	if err := os.MkdirAll(dir, 0700); err != nil {
		return "", fmt.Errorf("create config directory: %w", err)
	}
	return dir, nil
}

// configPath returns the full path to ~/.orama/sandbox.yaml.
func configPath() (string, error) {
	dir, err := configDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, "sandbox.yaml"), nil
}

// LoadConfig reads the sandbox config from ~/.orama/sandbox.yaml.
// Returns an error if the file doesn't exist (user must run setup first).
func LoadConfig() (*Config, error) {
	path, err := configPath()
	if err != nil {
		return nil, err
	}

	data, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("sandbox not configured, run: orama sandbox setup")
		}
		return nil, fmt.Errorf("read config: %w", err)
	}

	var cfg Config
	if err := yaml.Unmarshal(data, &cfg); err != nil {
		return nil, fmt.Errorf("parse config %s: %w", path, err)
	}

	if err := cfg.validate(); err != nil {
		return nil, fmt.Errorf("invalid config: %w", err)
	}

	cfg.Defaults()

	return &cfg, nil
}

// SaveConfig writes the sandbox config to ~/.orama/sandbox.yaml.
func SaveConfig(cfg *Config) error {
	path, err := configPath()
	if err != nil {
		return err
	}

	data, err := yaml.Marshal(cfg)
	if err != nil {
		return fmt.Errorf("marshal config: %w", err)
	}

	if err := os.WriteFile(path, data, 0600); err != nil {
		return fmt.Errorf("write config: %w", err)
	}

	return nil
}

// validate checks that required fields are present.
func (c *Config) validate() error {
	if c.HetznerAPIToken == "" {
		return fmt.Errorf("hetzner_api_token is required")
	}
	if c.Domain == "" {
		return fmt.Errorf("domain is required")
	}
	if len(c.FloatingIPs) < 2 {
		return fmt.Errorf("2 floating IPs required, got %d", len(c.FloatingIPs))
	}
	if c.SSHKey.VaultTarget == "" {
		return fmt.Errorf("ssh_key.vault_target is required (run: orama sandbox setup)")
	}
	return nil
}

// Defaults fills in default values for optional fields.
func (c *Config) Defaults() {
	if c.Location == "" {
		c.Location = "nbg1"
	}
	if c.ServerType == "" {
		c.ServerType = "cx23"
	}
	if c.SSHKey.VaultTarget == "" {
		c.SSHKey.VaultTarget = "sandbox/root"
	}
}
