package config

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/yaml.v3"
)

// Config holds the full agent configuration loaded from config.yaml.
type Config struct {
	Site          string `yaml:"site"`           // LUD or BAR
	LogLevel      string `yaml:"log_level"`      // debug | info | warn | error
	SetupComplete bool   `yaml:"setup_complete"` // false → show setup wizard on first run

	DB struct {
		Path string `yaml:"path"`
	} `yaml:"db"`

	Web struct {
		Addr  string `yaml:"addr"`
		Token string `yaml:"token"`
	} `yaml:"web"`

	Nexus struct {
		URL    string `yaml:"url"`
		APIKey string `yaml:"api_key"`
	} `yaml:"nexus"`

	OfflineAfter time.Duration `yaml:"offline_after"` // mark hosts offline after this duration

	Alert AlertConfig `yaml:"alert"`

	Modules struct {
		ARPDiscovery   ARPConfig     `yaml:"arp_discovery"`
		ADSync         ADConfig      `yaml:"ad_sync"`
		SiteMonitoring SiteMonConfig `yaml:"site_monitoring"`
		PortScan       PortScanConfig `yaml:"port_scan"`
		SNMP           SNMPConfig    `yaml:"snmp"`
		NexusReporter  ReporterConfig `yaml:"nexus_reporter"`
	} `yaml:"modules"`
}

// ADConfig holds settings for the ad_sync module.
type ADConfig struct {
	Enabled      bool          `yaml:"enabled"`
	Interval     time.Duration `yaml:"interval"`
	Server       string        `yaml:"server"`       // DC hostname or IP
	Port         int           `yaml:"port"`         // default 389
	BindDN       string        `yaml:"bind_dn"`      // svc-scanner@winkel.local
	BindPassword string        `yaml:"bind_password"`
	SearchBase   string        `yaml:"search_base"`  // DC=winkel,DC=local
	TLS          bool          `yaml:"tls"`          // use LDAPS
}

// SiteMonConfig holds settings for the site_monitoring module.
type SiteMonConfig struct {
	Enabled  bool          `yaml:"enabled"`
	Interval time.Duration `yaml:"interval"`
}

// PortScanConfig holds settings for the port_scan module.
type PortScanConfig struct {
	Enabled  bool          `yaml:"enabled"`
	Interval time.Duration `yaml:"interval"`
	Ports    []int         `yaml:"ports"`   // empty = use defaults
	Timeout  time.Duration `yaml:"timeout"` // per-port connect timeout
}

// SNMPConfig holds settings for the snmp module.
type SNMPConfig struct {
	Enabled   bool          `yaml:"enabled"`
	Interval  time.Duration `yaml:"interval"`
	Community string        `yaml:"community"` // default community string
}

// ReporterConfig holds settings for the nexus_reporter module.
type ReporterConfig struct {
	Enabled  bool          `yaml:"enabled"`
	Interval time.Duration `yaml:"interval"`
}

// AlertConfig holds notification settings.
type AlertConfig struct {
	SMTP struct {
		Enabled  bool   `yaml:"enabled"`
		Host     string `yaml:"host"`
		Port     int    `yaml:"port"`
		Username string `yaml:"username"`
		Password string `yaml:"password"`
		From     string `yaml:"from"`
		To       string `yaml:"to"`
	} `yaml:"smtp"`
	NexusEnabled bool `yaml:"nexus_enabled"`
}

// ARPConfig holds settings for the arp_discovery module.
type ARPConfig struct {
	Enabled   bool          `yaml:"enabled"`
	Interval  time.Duration `yaml:"interval"`
	Subnets   []string      `yaml:"subnets"`
	Interface string        `yaml:"interface"`
}

// Load reads and validates the YAML config at path.
func Load(path string) (*Config, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, fmt.Errorf("open config %q: %w", path, err)
	}
	defer f.Close()

	var cfg Config
	if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
		return nil, fmt.Errorf("decode config: %w", err)
	}

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

	return &cfg, nil
}

// Save writes the config back to path in YAML format.
func Save(path string, cfg *Config) error {
	f, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("create config %q: %w", path, err)
	}
	defer f.Close()
	if err := yaml.NewEncoder(f).Encode(cfg); err != nil {
		return fmt.Errorf("encode config: %w", err)
	}
	return nil
}

func (c *Config) applyDefaults() error {
	if c.Site == "" {
		return fmt.Errorf("site is required (LUD or BAR)")
	}
	if c.Site != "LUD" && c.Site != "BAR" {
		return fmt.Errorf("site must be LUD or BAR, got %q", c.Site)
	}
	if c.LogLevel == "" {
		c.LogLevel = "info"
	}
	if c.DB.Path == "" {
		c.DB.Path = "/var/lib/nexus-scanner/scanner.db"
	}
	if c.Web.Addr == "" {
		c.Web.Addr = ":8080"
	}
	if c.Modules.ARPDiscovery.Interval == 0 {
		c.Modules.ARPDiscovery.Interval = 5 * time.Minute
	}
	if c.Modules.ARPDiscovery.Interface == "" {
		c.Modules.ARPDiscovery.Interface = "eth0"
	}
	if c.Modules.ADSync.Port == 0 {
		c.Modules.ADSync.Port = 389
	}
	if c.Modules.ADSync.Interval == 0 {
		c.Modules.ADSync.Interval = 30 * time.Minute
	}
	if c.Modules.ADSync.SearchBase == "" && c.Modules.ADSync.Server != "" {
		c.Modules.ADSync.SearchBase = "DC=winkel,DC=local"
	}
	if c.Modules.SiteMonitoring.Interval == 0 {
		c.Modules.SiteMonitoring.Interval = time.Minute
	}
	if c.Modules.PortScan.Interval == 0 {
		c.Modules.PortScan.Interval = 15 * time.Minute
	}
	if c.Modules.PortScan.Timeout == 0 {
		c.Modules.PortScan.Timeout = 500 * time.Millisecond
	}
	if c.Modules.SNMP.Interval == 0 {
		c.Modules.SNMP.Interval = 5 * time.Minute
	}
	if c.Modules.SNMP.Community == "" {
		c.Modules.SNMP.Community = "public"
	}
	if c.Modules.NexusReporter.Interval == 0 {
		c.Modules.NexusReporter.Interval = 10 * time.Minute
	}
	if c.OfflineAfter == 0 {
		c.OfflineAfter = 15 * time.Minute
	}
	if c.Alert.SMTP.Port == 0 {
		c.Alert.SMTP.Port = 587
	}
	return nil
}
