// Package config loads and validates the claude-failover YAML configuration.
package config

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

	"gopkg.in/yaml.v3"
)

// Config is the top-level configuration structure, mapping config.example.yaml.
type Config struct {
	Accounts      []AccountConfig     `yaml:"accounts"`
	Pool          PoolConfig          `yaml:"pool"`
	Quota         QuotaConfig         `yaml:"quota"`
	Checkpoint    CheckpointConfig    `yaml:"checkpoint"`
	MCPHTTP       MCPHTTPConfig       `yaml:"mcp_http"`
	Notifications NotificationsConfig `yaml:"notifications"`
	Dispatcher    DispatcherConfig    `yaml:"dispatcher"`
	Watcher       WatcherConfig       `yaml:"watcher"`
	Janitor       JanitorConfig       `yaml:"janitor"`
}

// AccountConfig describes a single Anthropic account available to the daemon.
type AccountConfig struct {
	Name     string        `yaml:"name"`
	Home     string        `yaml:"home"`
	Limits   AccountLimits `yaml:"limits"`
	Priority int           `yaml:"priority"`
}

// AccountLimits defines soft usage thresholds for an account.
type AccountLimits struct {
	HourlyMsgs int `yaml:"hourly_msgs"`
	WeeklyMsgs int `yaml:"weekly_msgs"`
}

// PoolConfig describes the session pool configuration.
type PoolConfig struct {
	Dedicated          []DedicatedSession `yaml:"dedicated"`
	Autonomous         AutonomousConfig   `yaml:"autonomous"`
	SharedProjectsDir  string             `yaml:"shared_projects_dir"`
}

// DedicatedSession is a named tmux session bound to a specific project directory.
type DedicatedSession struct {
	Name    string `yaml:"name"`
	Project string `yaml:"project"`
}

// AutonomousConfig controls the autoscaling inbox-dispatcher session pool.
type AutonomousConfig struct {
	Prefix string `yaml:"prefix"`
	Min    int    `yaml:"min"`
	Max    int    `yaml:"max"`
}

// QuotaConfig defines quota monitoring parameters.
type QuotaConfig struct {
	PollInterval        Duration `yaml:"poll_interval"`
	Window5hThreshold   float64  `yaml:"window_5h_threshold"`
	WindowWeekThreshold float64  `yaml:"window_week_threshold"`
	ReactivateCooldown  Duration `yaml:"reactivate_cooldown"`
}

// CheckpointConfig controls session context snapshotting.
type CheckpointConfig struct {
	Dir      string   `yaml:"dir"`
	Interval Duration `yaml:"interval"`
	Keep     int      `yaml:"keep"`
}

// MCPHTTPConfig defines the HTTP control-plane endpoint.
type MCPHTTPConfig struct {
	Listen         string `yaml:"listen"`
	BearerTokenEnv string `yaml:"bearer_token_env"`
	EnableTrigger  bool   `yaml:"enable_trigger"`
}

// NotificationsConfig holds environment variable names for alert credentials.
// Actual secrets are read from env at runtime — never stored in config files.
type NotificationsConfig struct {
	TelegramTokenEnv  string `yaml:"telegram_token_env"`
	TelegramChatIDEnv string `yaml:"telegram_chat_id_env"`
	ResendAPIKeyEnv   string `yaml:"resend_api_key_env"`
	NotifyEmailEnv    string `yaml:"notify_email_env"`
}

// DispatcherConfig controls the task dispatcher behaviour.
type DispatcherConfig struct {
	ProjectsDir        string   `yaml:"projects_dir"`
	IdleTimeout        Duration `yaml:"idle_timeout"`
	PromptTimeout      Duration `yaml:"prompt_timeout"`
	MaxDispatchPerTask int      `yaml:"max_dispatch_per_task"`
}

// WatcherConfig controls the session watcher behaviour.
type WatcherConfig struct {
	Interval      Duration `yaml:"interval"`
	DoneSignalDir string   `yaml:"done_signal_dir"`
	IdleTimeout   Duration `yaml:"idle_timeout"`
}

// JanitorConfig controls the periodic housekeeping goroutine.
type JanitorConfig struct {
	Interval Duration `yaml:"interval"`
}

// Duration is a time.Duration that unmarshals from YAML strings like "30s", "1h".
type Duration struct {
	time.Duration
}

func (d *Duration) UnmarshalYAML(value *yaml.Node) error {
	var s string
	if err := value.Decode(&s); err != nil {
		return err
	}
	dur, err := time.ParseDuration(s)
	if err != nil {
		return fmt.Errorf("invalid duration %q: %w", s, err)
	}
	d.Duration = dur
	return nil
}

// expandHome replaces a leading "~" with the current user's home directory.
func expandHome(path string) string {
	if strings.HasPrefix(path, "~/") {
		home, err := os.UserHomeDir()
		if err != nil {
			return path
		}
		return filepath.Join(home, path[2:])
	}
	return path
}

// expandPaths resolves ~ in paths that may reference the operator's home dir.
func (c *Config) expandPaths() {
	for i := range c.Accounts {
		c.Accounts[i].Home = expandHome(c.Accounts[i].Home)
	}
	for i := range c.Pool.Dedicated {
		c.Pool.Dedicated[i].Project = expandHome(c.Pool.Dedicated[i].Project)
	}
	c.Pool.SharedProjectsDir = expandHome(c.Pool.SharedProjectsDir)
	c.Checkpoint.Dir = expandHome(c.Checkpoint.Dir)
}

// defaults sets sensible fallback values when fields are zero.
func (c *Config) defaults() {
	if c.MCPHTTP.Listen == "" {
		c.MCPHTTP.Listen = "127.0.0.1:9090"
	}
	if c.Quota.PollInterval.Duration == 0 {
		c.Quota.PollInterval.Duration = 30 * time.Second
	}
	if c.Checkpoint.Interval.Duration == 0 {
		c.Checkpoint.Interval.Duration = 60 * time.Second
	}
	if c.Checkpoint.Keep == 0 {
		c.Checkpoint.Keep = 20
	}
	if c.Pool.Autonomous.Min == 0 {
		c.Pool.Autonomous.Min = 2
	}
	if c.Pool.Autonomous.Max == 0 {
		c.Pool.Autonomous.Max = 10
	}
	if c.Watcher.Interval.Duration == 0 {
		c.Watcher.Interval.Duration = 30 * time.Second
	}
	if c.Watcher.IdleTimeout.Duration == 0 {
		c.Watcher.IdleTimeout.Duration = 60 * time.Minute
	}
	if c.Watcher.DoneSignalDir == "" {
		c.Watcher.DoneSignalDir = "/tmp"
	}
	if c.Janitor.Interval.Duration == 0 {
		c.Janitor.Interval.Duration = 5 * time.Minute
	}
	if c.Dispatcher.IdleTimeout.Duration == 0 {
		c.Dispatcher.IdleTimeout.Duration = 60 * time.Minute
	}
	if c.Dispatcher.PromptTimeout.Duration == 0 {
		c.Dispatcher.PromptTimeout.Duration = 30 * time.Second
	}
	if c.Dispatcher.MaxDispatchPerTask == 0 {
		c.Dispatcher.MaxDispatchPerTask = 3
	}
}

// Load reads the YAML file at path, expands home paths, and applies defaults.
func Load(path string) (*Config, error) {
	path = expandHome(path)

	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("reading config %s: %w", path, err)
	}

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

	cfg.expandPaths()
	cfg.defaults()

	return &cfg, nil
}
