package config

import (
	"fmt"
	"os"
	"strings"

	"gopkg.in/yaml.v3"
)

// Config is the top-level configuration structure.
// Contains ONLY infrastructure/customer identity.
// App-specific config lives in per-app app.yaml files.
type Config struct {
	Customer       CustomerConfig       `yaml:"customer"`
	Infrastructure InfrastructureConfig `yaml:"infrastructure"`
	Paths          PathsConfig          `yaml:"paths"`
	Web            WebConfig            `yaml:"web"`
	Git            GitConfig            `yaml:"git"`
	Stacks         StacksConfig         `yaml:"stacks"`
	Backup         BackupConfig         `yaml:"backup"`
	Monitoring     MonitoringConfig     `yaml:"monitoring"`
	SelfUpdate     SelfUpdateConfig     `yaml:"self_update"`
	Notifications  NotificationsConfig  `yaml:"notifications"`
	Logging        LoggingConfig        `yaml:"logging"`
	Assets         AssetsConfig         `yaml:"assets"`
	System         SystemConfig         `yaml:"system"`
}

type SystemConfig struct {
	ReservedMemoryMB int `yaml:"reserved_memory_mb"`
}

type CustomerConfig struct {
	ID             string `yaml:"id"`
	Name           string `yaml:"name"`
	Domain         string `yaml:"domain"`
	Email          string `yaml:"email"`
	TelegramChatID string `yaml:"telegram_chat_id"`
}

type InfrastructureConfig struct {
	CFTunnelToken string `yaml:"cf_tunnel_token"`
	CFAPIToken    string `yaml:"cf_api_token"`
}

type PathsConfig struct {
	StacksDir string `yaml:"stacks_dir"`
	DataDir   string `yaml:"data_dir"`
	BackupDir string `yaml:"backup_dir"`
	DBDumpDir string `yaml:"db_dump_dir"`
	HDDPath   string `yaml:"hdd_path"`
}

type WebConfig struct {
	Listen        string `yaml:"listen"`
	PasswordHash  string `yaml:"password_hash"`
	SessionSecret string `yaml:"session_secret"`
}

type GitConfig struct {
	RepoURL      string `yaml:"repo_url"`
	Branch       string `yaml:"branch"`
	SyncInterval string `yaml:"sync_interval"`
	Username     string `yaml:"username"`
	Token        string `yaml:"token"`
}

type StacksConfig struct {
	Protected      []string `yaml:"protected"`
	UpdateWindow   string   `yaml:"update_window"`
	ComposeCommand string   `yaml:"compose_command"`
}

type BackupConfig struct {
	Enabled            bool            `yaml:"enabled"`
	ResticRepo         string          `yaml:"restic_repo"`
	ResticPasswordFile string          `yaml:"restic_password_file"`
	DBDumpSchedule     string          `yaml:"db_dump_schedule"`
	ResticSchedule     string          `yaml:"restic_schedule"`
	Retention          RetentionConfig `yaml:"retention"`
	PruneSchedule      string          `yaml:"prune_schedule"`
}

type RetentionConfig struct {
	KeepDaily   int `yaml:"keep_daily"`
	KeepWeekly  int `yaml:"keep_weekly"`
	KeepMonthly int `yaml:"keep_monthly"`
}

type MonitoringConfig struct {
	Enabled              bool             `yaml:"enabled"`
	HealthchecksBase     string           `yaml:"healthchecks_base"`
	PingUUIDs            PingUUIDsConfig  `yaml:"ping_uuids"`
	HealthCheckSchedule  string           `yaml:"health_check_schedule"`
	SystemHealthInterval string           `yaml:"system_health_interval"`
	Thresholds           ThresholdsConfig `yaml:"thresholds"`
}

type PingUUIDsConfig struct {
	DBDump       string `yaml:"db_dump"`
	Backup       string `yaml:"backup"`
	SystemHealth string `yaml:"system_health"`
}

type ThresholdsConfig struct {
	DiskWarnPercent        int `yaml:"disk_warn_percent"`
	DiskCritPercent        int `yaml:"disk_crit_percent"`
	BackupMaxAgeHours      int `yaml:"backup_max_age_hours"`
	CPUWarnPercent         int `yaml:"cpu_warn_percent"`
	MemoryWarnPercent      int `yaml:"memory_warn_percent"`
	TemperatureWarnCelsius int `yaml:"temperature_warn_celsius"`
}

type SelfUpdateConfig struct {
	Enabled              bool   `yaml:"enabled"`
	CheckInterval        string `yaml:"check_interval"`
	Image                string `yaml:"image"`
	AutoUpdate           bool   `yaml:"auto_update"`
	HealthTimeoutSeconds int    `yaml:"health_timeout_seconds"`
}

type NotificationsConfig struct {
	CustomerEvents []string `yaml:"customer_events"`
	OperatorEvents []string `yaml:"operator_events"`
}

type LoggingConfig struct {
	Level     string `yaml:"level"`
	File      string `yaml:"file"`
	MaxSizeMB int    `yaml:"max_size_mb"`
	MaxFiles  int    `yaml:"max_files"`
}

type AssetsConfig struct {
	SourceURL string `yaml:"source_url"` // Only used during build, not runtime
}

// Load reads and parses the config file, applies defaults, and validates.
func Load(path string) (*Config, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("reading config file: %w", err)
	}

	// Expand environment variables in the YAML
	expanded := os.ExpandEnv(string(data))

	cfg := &Config{}
	if err := yaml.Unmarshal([]byte(expanded), cfg); err != nil {
		return nil, fmt.Errorf("parsing config file: %w", err)
	}

	applyDefaults(cfg)
	applyEnvOverrides(cfg)

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

	return cfg, nil
}

func applyDefaults(cfg *Config) {
	d := func(val *string, def string) {
		if *val == "" {
			*val = def
		}
	}
	di := func(val *int, def int) {
		if *val == 0 {
			*val = def
		}
	}

	d(&cfg.Paths.StacksDir, "/opt/docker/stacks")
	d(&cfg.Paths.DataDir, "/opt/docker/felhom-controller/data")
	d(&cfg.Paths.BackupDir, "/srv/backups")
	d(&cfg.Paths.DBDumpDir, "/srv/backups/db-dumps")
	d(&cfg.Web.Listen, ":8080")
	d(&cfg.Git.Branch, "main")
	d(&cfg.Git.SyncInterval, "15m")
	d(&cfg.Stacks.UpdateWindow, "03:00-05:00")
	d(&cfg.Backup.ResticRepo, "/srv/backups/restic-repo")
	d(&cfg.Backup.DBDumpSchedule, "02:30")
	d(&cfg.Backup.ResticSchedule, "03:00")
	d(&cfg.Backup.PruneSchedule, "weekly")
	di(&cfg.Backup.Retention.KeepDaily, 7)
	di(&cfg.Backup.Retention.KeepWeekly, 4)
	di(&cfg.Backup.Retention.KeepMonthly, 6)
	d(&cfg.Backup.ResticPasswordFile, "/opt/docker/felhom-controller/data/restic-password")
	d(&cfg.Monitoring.HealthchecksBase, "https://status.felhom.eu")
	d(&cfg.Monitoring.HealthCheckSchedule, "06:00")
	d(&cfg.Monitoring.SystemHealthInterval, "5m")
	di(&cfg.Monitoring.Thresholds.DiskWarnPercent, 80)
	di(&cfg.Monitoring.Thresholds.DiskCritPercent, 90)
	di(&cfg.Monitoring.Thresholds.BackupMaxAgeHours, 36)
	di(&cfg.Monitoring.Thresholds.CPUWarnPercent, 90)
	di(&cfg.Monitoring.Thresholds.MemoryWarnPercent, 85)
	di(&cfg.Monitoring.Thresholds.TemperatureWarnCelsius, 75)
	d(&cfg.SelfUpdate.CheckInterval, "6h")
	di(&cfg.SelfUpdate.HealthTimeoutSeconds, 60)
	d(&cfg.Logging.Level, "info")
	di(&cfg.Logging.MaxSizeMB, 10)
	di(&cfg.Logging.MaxFiles, 3)
	d(&cfg.Assets.SourceURL, "https://felhom.eu")
	di(&cfg.System.ReservedMemoryMB, 384)
}

func applyEnvOverrides(cfg *Config) {
	envStr := func(key string, target *string) {
		if v := os.Getenv(key); v != "" {
			*target = v
		}
	}
	envStr("FELHOM_CUSTOMER_ID", &cfg.Customer.ID)
	envStr("FELHOM_CUSTOMER_DOMAIN", &cfg.Customer.Domain)
	envStr("FELHOM_WEB_LISTEN", &cfg.Web.Listen)
	envStr("FELHOM_WEB_PASSWORD_HASH", &cfg.Web.PasswordHash)
	envStr("FELHOM_PATHS_STACKS_DIR", &cfg.Paths.StacksDir)
	envStr("FELHOM_PATHS_HDD_PATH", &cfg.Paths.HDDPath)
	envStr("FELHOM_LOGGING_LEVEL", &cfg.Logging.Level)
	envStr("FELHOM_MONITORING_SYSTEM_HEALTH_INTERVAL", &cfg.Monitoring.SystemHealthInterval)
}

func validate(cfg *Config) error {
	var errs []string

	if cfg.Customer.ID == "" {
		errs = append(errs, "customer.id is required")
	}
	if cfg.Customer.Domain == "" {
		errs = append(errs, "customer.domain is required")
	}

	switch cfg.Logging.Level {
	case "debug", "info", "warn", "error":
	default:
		errs = append(errs, fmt.Sprintf("logging.level must be debug|info|warn|error, got %q", cfg.Logging.Level))
	}

	if cfg.Monitoring.Thresholds.DiskWarnPercent >= cfg.Monitoring.Thresholds.DiskCritPercent {
		errs = append(errs, "disk_warn_percent must be less than disk_crit_percent")
	}

	if len(errs) > 0 {
		return fmt.Errorf("validation errors:\n  - %s", strings.Join(errs, "\n  - "))
	}

	return nil
}

// IsProtectedStack checks if a stack name is in the protected list.
func (cfg *Config) IsProtectedStack(name string) bool {
	for _, p := range cfg.Stacks.Protected {
		if strings.EqualFold(p, name) {
			return true
		}
	}
	return false
}

// AppLogoURL returns the primary logo URL (SVG). Use AppLogoPNGURL as fallback.
func (cfg *Config) AppLogoURL(slug string) string {
	return fmt.Sprintf("/static/assets/%s-logo.svg", slug)
}

// AppLogoPNGURL returns the PNG fallback logo URL.
func (cfg *Config) AppLogoPNGURL(slug string) string {
	return fmt.Sprintf("/static/assets/%s-logo.png", slug)
}

// AppScreenshotURL returns the local URL for an app's screenshot.
func (cfg *Config) AppScreenshotURL(slug string, index int) string {
	return fmt.Sprintf("/static/assets/%s-screenshot-%d.webp", slug, index)
}

// AppPageURL returns the URL for an app's detail page.
// This links to the local controller-hosted app detail page.
func (cfg *Config) AppPageURL(slug string) string {
	return fmt.Sprintf("/apps/%s", slug)
}
