package config

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

	"github.com/BurntSushi/toml"
)

// DatabaseConfig holds database connection settings.
type DatabaseConfig struct {
	Driver string `toml:"driver"`
	DSN    string `toml:"dsn"`
}

// WebConfig holds web server settings.
type WebConfig struct {
	// Addr 监听地址：以 / 开头视为 unix socket，否则视为 TCP 端口
	Addr string `toml:"addr"`
}

// SpeedtestConfig holds speed test limits.
type SpeedtestConfig struct {
	MaxDownloadBytes int64 `toml:"max_download_bytes"` // 单次下载请求最大字节数
	MaxUploadBytes   int64 `toml:"max_upload_bytes"`   // 单次上传请求最大字节数
}

// Config is the top-level configuration structure.
type Config struct {
	Database  DatabaseConfig  `toml:"database"`
	Web       WebConfig       `toml:"web"`
	Speedtest SpeedtestConfig `toml:"speedtest"`
}

func isWindows() bool { return runtime.GOOS == "windows" }

func etcDir() string {
	if isWindows() {
		return WinEtcDir
	}
	return LinuxEtcDir
}

func baseDir() string {
	if isWindows() {
		return WinBaseDir
	}
	return LinuxBaseDir
}

func defaultDSN() string {
	if isWindows() {
		return DefaultDSNWin
	}
	return DefaultDSNLinux
}

// defaultConfig returns a fully populated Config with default values.
func defaultConfig() *Config {
	return &Config{
		Database: DatabaseConfig{
			Driver: DefaultDBDriver,
			DSN:    defaultDSN(),
		},
		Web: WebConfig{
			Addr: DefaultWebAddr,
		},
		Speedtest: SpeedtestConfig{
			MaxDownloadBytes: DefaultMaxDownloadBytes,
			MaxUploadBytes:   DefaultMaxUploadBytes,
		},
	}
}

// configFilePath returns the full path to the configuration file.
// 支持通过环境变量 SPEEDTEST_CONFIG 覆盖（本地调试用）。
func configFilePath() string {
	if p := os.Getenv("SPEEDTEST_CONFIG"); p != "" {
		return p
	}
	return filepath.Join(etcDir(), ConfigFileName)
}

// mergeDefaults overlays default values onto the loaded config for any zero/empty fields.
func mergeDefaults(cfg *Config, defaults *Config) *Config {
	if cfg.Database.Driver == "" {
		cfg.Database.Driver = defaults.Database.Driver
	}
	if cfg.Database.DSN == "" {
		cfg.Database.DSN = defaults.Database.DSN
	}
	if cfg.Web.Addr == "" {
		cfg.Web.Addr = defaults.Web.Addr
	}
	if cfg.Speedtest.MaxDownloadBytes == 0 {
		cfg.Speedtest.MaxDownloadBytes = defaults.Speedtest.MaxDownloadBytes
	}
	if cfg.Speedtest.MaxUploadBytes == 0 {
		cfg.Speedtest.MaxUploadBytes = defaults.Speedtest.MaxUploadBytes
	}
	return cfg
}

// writeConfig writes the configuration to the given file path.
func writeConfig(path string, cfg *Config) error {
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("创建配置目录失败 %s: %w", dir, err)
	}

	f, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("创建配置文件失败 %s: %w", path, err)
	}
	defer f.Close()

	enc := toml.NewEncoder(f)
	if err := enc.Encode(cfg); err != nil {
		return fmt.Errorf("写入配置文件失败: %w", err)
	}
	return nil
}

// LoadConfig loads the configuration from disk.
// If the configuration file does not exist, it creates one with default values.
// If the file exists but has missing fields, they are filled with defaults and the file is updated.
func LoadConfig() (*Config, error) {
	path := configFilePath()
	defaults := defaultConfig()

	if _, err := os.Stat(path); os.IsNotExist(err) {
		if mkErr := writeConfig(path, defaults); mkErr != nil {
			return nil, mkErr
		}
		return defaults, nil
	}

	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("读取配置文件失败 %s: %w", path, err)
	}

	cfg := &Config{}
	if err := toml.Unmarshal(data, cfg); err != nil {
		return nil, fmt.Errorf("解析配置文件失败: %w", err)
	}

	merged := mergeDefaults(cfg, defaults)
	if writeErr := writeConfig(path, merged); writeErr != nil {
		return nil, writeErr
	}

	return merged, nil
}
