package config

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

	"gopkg.in/yaml.v3"
)

type InterfaceConfig struct {
	Label   string            `yaml:"label,omitempty"`
	Auto    bool              `yaml:"auto"`
	Mode    string            `yaml:"mode"`
	Address string            `yaml:"address,omitempty"`
	Netmask string            `yaml:"netmask,omitempty"`
	Gateway  string            `yaml:"gateway,omitempty"`
	DNS     []string          `yaml:"dns,omitempty"`
	Extra   map[string]string `yaml:"extra,omitempty"`
}

type DHCPPool struct {
	Interface  string   `yaml:"interface"`
	Enabled    bool     `yaml:"enabled"`
	Subnet     string   `yaml:"subnet"`
	Netmask    string   `yaml:"netmask"`
	RangeStart string   `yaml:"range_start"`
	RangeEnd   string   `yaml:"range_end"`
	Router     string   `yaml:"router"`
	DNS        []string `yaml:"dns"`
	LeaseTime  int      `yaml:"lease_time"`
}

type DHCPConfig struct {
	Enabled bool       `yaml:"enabled"`
	Pools   []DHCPPool `yaml:"pools"`
}

type NATConfig struct {
	Interfaces []string `yaml:"interfaces"`
}

type KnownDevice struct {
	IP       string `yaml:"ip"`
	MAC      string `yaml:"mac"`
	Hostname string `yaml:"hostname"`
	Blocked  bool   `yaml:"blocked,omitempty"`
	StaticIP string `yaml:"static_ip,omitempty"`
}

type MihomoConfig struct {
	Enabled bool `yaml:"enabled"`
}

type FirewallRule struct {
	ID       string `yaml:"id"        json:"id"`
	Enabled  bool   `yaml:"enabled"   json:"enabled"`
	Action   string `yaml:"action"    json:"action"`
	Protocol string `yaml:"protocol"  json:"protocol"`
	SrcAddr  string `yaml:"src_addr"  json:"src_addr"`
	SrcPort  string `yaml:"src_port"  json:"src_port"`
	DstAddr  string `yaml:"dst_addr"  json:"dst_addr"`
	DstPort  string `yaml:"dst_port"  json:"dst_port"`
	InIface  string `yaml:"in_iface"  json:"in_iface"`
	OutIface string `yaml:"out_iface" json:"out_iface"`
	Comment  string `yaml:"comment"   json:"comment"`
}

type FirewallConfig struct {
	Rules         []FirewallRule `yaml:"rules"          json:"rules"`
	VLANIsolation bool           `yaml:"vlan_isolation" json:"vlan_isolation"`
}

type AppConfig struct {
	Interfaces   map[string]*InterfaceConfig `yaml:"interfaces"`
	DHCP         DHCPConfig                  `yaml:"dhcp"`
	NAT          NATConfig                   `yaml:"nat"`
	Firewall     FirewallConfig              `yaml:"firewall"`
	KnownDevices []KnownDevice               `yaml:"known_devices"`
	Mihomo       MihomoConfig                `yaml:"mihomo"`
}

var (
	mu       sync.Mutex
	filePath string
)

func init() {
	dir := executableDir()
	filePath = filepath.Join(dir, "config.yaml")
}

func executableDir() string {
	exe, err := os.Executable()
	if err != nil {
		return "."
	}
	// Resolve symlinks — /proc/self/exe on Linux points to real path
	if resolved, err := filepath.EvalSymlinks(exe); err == nil {
		exe = resolved
	}
	return filepath.Dir(exe)
}

func SetPath(p string) {
	mu.Lock()
	defer mu.Unlock()
	filePath = p
}

func GetPath() string {
	mu.Lock()
	defer mu.Unlock()
	return filePath
}

func Load() (*AppConfig, error) {
	mu.Lock()
	defer mu.Unlock()

	data, err := os.ReadFile(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return defaultConfig(), nil
		}
		return nil, fmt.Errorf("read config: %w", err)
	}

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

	EnsureDefaults(&cfg)
	return &cfg, nil
}

func Save(cfg *AppConfig) error {
	mu.Lock()
	defer mu.Unlock()

	EnsureDefaults(cfg)

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

	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return fmt.Errorf("mkdir config dir: %w", err)
	}

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

	if err := os.Rename(tmp, filePath); err != nil {
		return fmt.Errorf("rename config: %w", err)
	}

	return nil
}

func LoadInto(dst interface{}) error {
	mu.Lock()
	defer mu.Unlock()

	data, err := os.ReadFile(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return fmt.Errorf("read config: %w", err)
	}
	return yaml.Unmarshal(data, dst)
}

func defaultConfig() *AppConfig {
	return &AppConfig{
		Interfaces:   map[string]*InterfaceConfig{},
		DHCP:         DHCPConfig{Pools: []DHCPPool{}},
		NAT:          NATConfig{Interfaces: []string{}},
		KnownDevices: []KnownDevice{},
		Mihomo:       MihomoConfig{Enabled: false},
	}
}

func EnsureDefaults(cfg *AppConfig) {
	if cfg.Interfaces == nil {
		cfg.Interfaces = map[string]*InterfaceConfig{}
	}
	if cfg.DHCP.Pools == nil {
		cfg.DHCP.Pools = []DHCPPool{}
	}
	if cfg.NAT.Interfaces == nil {
		cfg.NAT.Interfaces = []string{}
	}
	if cfg.Firewall.Rules == nil {
		cfg.Firewall.Rules = []FirewallRule{}
	}
	if cfg.KnownDevices == nil {
		cfg.KnownDevices = []KnownDevice{}
	}
}

func UpdateKnownDevices(seen []KnownDevice) error {
	cfg, err := Load()
	if err != nil {
		return err
	}

	existing := make(map[string]KnownDevice)
	for _, d := range cfg.KnownDevices {
		key := d.MAC
		if key == "" {
			key = d.IP
		}
		existing[key] = d
	}

	for _, d := range seen {
		key := d.MAC
		if key == "" {
			key = d.IP
		}
		if existingDev, ok := existing[key]; ok {
			if d.Hostname != "" {
				existingDev.Hostname = d.Hostname
			}
			if d.IP != "" {
				existingDev.IP = d.IP
			}
			if d.MAC != "" {
				existingDev.MAC = d.MAC
			}
			if d.StaticIP != "" {
				existingDev.StaticIP = d.StaticIP
			}
			existing[key] = existingDev
		} else {
			existing[key] = d
		}
	}

	cfg.KnownDevices = make([]KnownDevice, 0, len(existing))
	for _, d := range existing {
		cfg.KnownDevices = append(cfg.KnownDevices, d)
	}

	return Save(cfg)
}