// Package config holds all global configuration parameters for sese-engine.
// config 包存放 sese-engine 的所有全局配置参数。
package config

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

	"gopkg.in/yaml.v3"
)

// configMu 保护 Global 的运行时修改（动态调参场景）。
var configMu sync.RWMutex

// Config 是完整的配置结构体
type Config struct {
	Index      IndexConfig      `yaml:"index"`
	Crawler    CrawlerConfig    `yaml:"crawler"`
	Search     SearchConfig     `yaml:"search"`
	Backlink   BacklinkConfig   `yaml:"backlink"`
	Storage    StorageConfig    `yaml:"storage"`
	MySQL      MySQLConfig      `yaml:"mysql"`
	Redis      RedisConfig      `yaml:"redis"`
	Prometheus PrometheusConfig `yaml:"prometheus"`
}

// IndexConfig 索引/存储相关限制
type IndexConfig struct {
	MaxURLsPerKey       int `yaml:"max_urls_per_key"`
	MaxSameDomainPerKey int `yaml:"max_same_domain_per_key"`
	BigCleanThreshold   int `yaml:"big_clean_threshold"`
	MaxNewURLsPerKey    int `yaml:"max_new_urls_per_key"`
	MinURLsForNewKey    int `yaml:"min_urls_for_new_key"`
}

// CrawlerConfig 爬虫行为相关配置
type CrawlerConfig struct {
	SpiderName           string  `yaml:"spider_name"`
	Cooldown             int     `yaml:"cooldown"`
	Workers              int     `yaml:"workers"`
	CrawlFocus           float64 `yaml:"crawl_focus"`
	MaxKeywordsPerPage   int     `yaml:"max_keywords_per_page"`
	MaxEpoch             int     `yaml:"max_epoch"`
	ExpectedProsperRatio float64 `yaml:"expected_prosper_ratio"`
	EntryURL             string  `yaml:"entry_url"`
	MaxPageSize          int     `yaml:"max_page_size"` // 单个页面最大抓取字节数（0=不限，默认 5MB）
	RecrawlMaxAge        int     `yaml:"recrawl_max_age"`         // URL 过期时间（秒），超过此时间的 URL 允许被重爬，默认 30 天
	RecrawlCheckInterval int     `yaml:"recrawl_check_interval"`  // 运行期间检查过期 URL 的间隔（秒），默认 1 小时
	RecrawlBatchSize     int     `yaml:"recrawl_batch_size"`      // 每次检查最多释放多少个过期 URL，默认 500
	MaxPriorityChildren  int     `yaml:"max_priority_children"`   // priorityChildren 队列的最大链接数，默认 100
}

// SearchConfig 搜索结果排序权重配置
type SearchConfig struct {
	UseOnlineSnippet      bool    `yaml:"use_online_snippet"`
	OnlineSnippetTimeout  int     `yaml:"online_snippet_timeout"`
	WeightDailyDecay      float64 `yaml:"weight_daily_decay"`
	LanguageWeight        float64 `yaml:"language_weight"`
	ConsecutiveKeyWeight  float64 `yaml:"consecutive_key_weight"`
	BacklinkWeight        float64 `yaml:"backlink_weight"`
	ServerPort            int     `yaml:"server_port"`
	FlushIntervalSeconds  int     `yaml:"flush_interval_seconds"`
	StatsRefreshInterval  int     `yaml:"stats_refresh_interval"` // 统计缓存刷新间隔（秒），默认 30
	MissPenalty           float64 `yaml:"miss_penalty"`           // 缺词惩罚系数（0=不惩罚，1=完全忽略缺词URL），默认 0.15
	UnixSocket            string  `yaml:"unix_socket"`            // Unix socket 路径（仅 Linux/macOS），空字符串表示不启用
}

// BacklinkConfig 反向链接计算相关配置
type BacklinkConfig struct {
	Baseline int `yaml:"baseline"`
}

// StorageConfig 存储配置
type StorageConfig struct {
	Path string `yaml:"path"`
}

// MySQLConfig MySQL数据库连接配置
// 支持两种连接方式：Unix Socket 和 TCP
// 优先级：UnixSocket > TCP（如果UnixSocket非空则优先使用）
type MySQLConfig struct {
	// 是否启用 MySQL（默认关闭）
	Enabled bool `yaml:"enabled"`
	// 连接方式: "socket" 或 "tcp"（自动推断，可不填）
	Network string `yaml:"network"`
	// Unix Socket 路径（Linux/macOS），优先使用
	// 示例: "/var/run/mysqld/mysqld.sock" 或 "/tmp/mysql.sock"
	UnixSocket string `yaml:"unix_socket"`
	// TCP 连接方式：服务器地址
	Host string `yaml:"host"`
	// TCP 连接方式：端口号
	Port int `yaml:"port"`
	// 用户名
	User string `yaml:"user"`
	// 密码
	Password string `yaml:"password"`
	// 数据库名
	Database string `yaml:"database"`
	// 连接超时时间（秒）
	ConnMaxLifetime int `yaml:"conn_max_lifetime"` // 秒
	// 最大空闲连接数
	MaxIdleConns int `yaml:"max_idle_conns"`
	// 最大打开连接数
	MaxOpenConns int `yaml:"max_open_conns"`
}

// RedisConfig Redis连接配置
// 支持两种连接方式：Unix Socket 和 TCP
// 优先级：UnixSocket > TCP（如果UnixSocket非空则优先使用）
type RedisConfig struct {
	// 连接方式: "socket" 或 "tcp"（自动推断，可不填）
	Network string `yaml:"network"`
	// Unix Socket 路径，优先使用
	// 示例: "/var/run/redis/redis.sock" 或 "/tmp/redis.sock"
	UnixSocket string `yaml:"unix_socket"`
	// TCP 连接方式：服务器地址
	Host string `yaml:"host"`
	// TCP 连接方式：端口号
	Port int `yaml:"port"`
	// 密码（无密码则留空）
	Password string `yaml:"password"`
	// 数据库编号（0-15），默认 15
	DB int `yaml:"db"`
	// 池大小（最大连接数）
	PoolSize int `yaml:"pool_size"`
	// 最小空闲连接数
	MinIdleConns int `yaml:"min_idle_conns"`
	// 读超时时间（毫秒）
	ReadTimeout int `yaml:"read_timeout"` // 毫秒
	// 写超时时间（毫秒）
	WriteTimeout int `yaml:"write_timeout"` // 毫秒
}

// PrometheusConfig Prometheus监控端口配置
type PrometheusConfig struct {
	CrawlerPort  int `yaml:"crawler_port"`
	BacklinkPort int `yaml:"backlink_port"`
	SearchPort   int `yaml:"search_port"`
}

// Global 全局配置实例，加载后可通过此变量访问
var Global Config

// Load 从指定路径加载配置文件，并自动补全缺失的字段。
// 流程：读取 YAML → 与默认值合并 → 写回 config.yml → 赋值 Global
func Load(configPath string) error {
	data, err := os.ReadFile(configPath)
	if err != nil {
		return fmt.Errorf("failed to read config file: %v", err)
	}

	// 先拿到当前 YAML 内容，用于判断哪些字段实际存在于文件中
	var yamlOnly Config
	if err := yaml.Unmarshal(data, &yamlOnly); err != nil {
		return fmt.Errorf("failed to parse config file: %v", err)
	}

	// 从默认值开始，YAML 中有值的字段会被覆盖
	merged := GetDefaultConfig()
	mergeConfig(&merged, &yamlOnly)

	// 写回 config.yml（自动补全缺失字段）
	yamlOut, err := yaml.Marshal(&merged)
	if err != nil {
		return fmt.Errorf("failed to marshal config: %v", err)
	}
	if err := os.WriteFile(configPath, yamlOut, 0644); err != nil {
		return fmt.Errorf("failed to write config file: %v", err)
	}

	Global = merged
	return nil
}

// mergeConfig 将 src 中的非零字段合并到 dst（原地修改 dst）。
// 用于把 YAML 实际配置值覆盖到默认值结构上。
func mergeConfig(dst, src interface{}) {
	if dst == nil || src == nil {
		return
	}
	dstVal := reflect.ValueOf(dst).Elem()
	srcVal := reflect.ValueOf(src).Elem()

		for i := 0; i < dstVal.NumField(); i++ {
		dstField := dstVal.Field(i)
		srcField := srcVal.Field(i)

		switch dstField.Kind() {
		case reflect.Struct:
			// 递归合并嵌套 struct
			mergeConfig(dstField.Addr().Interface(), srcField.Addr().Interface())
		case reflect.Slice:
			// slice：仅当 src 非空时才覆盖（避免覆盖用户显式设置的长 0 slice）
			if srcField.Len() > 0 {
				dstField.Set(srcField)
			}
		default:
			// 其他类型：src 为零值则保留 dst 原值（默认值）
			if !isZero(srcField) {
				dstField.Set(srcField)
			}
		}
	}
}

// isZero 检查 reflect.Value 是否为该类型的零值。
func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return math.Float64bits(v.Float()) == 0
	case reflect.String:
		return v.String() == ""
	case reflect.Ptr, reflect.Interface:
		return v.IsNil()
	}
	return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}

// LoadFromSavedata 从 savedata 目录加载 config.yml
func LoadFromSavedata() error {
	configPath := filepath.Join("savedata", "config.yml")
	return Load(configPath)
}

// GetDefaultConfig 返回默认配置
func GetDefaultConfig() Config {
	return Config{
		Index: IndexConfig{
			MaxURLsPerKey:       11000,
			MaxSameDomainPerKey: 20,
			BigCleanThreshold:   2000000,
			MaxNewURLsPerKey:    10000,
			MinURLsForNewKey:    3,
		},
		Crawler: CrawlerConfig{
			SpiderName:           "Haibara_AI_spider",
			Cooldown:             3,
			Workers:              22,
			CrawlFocus:           0.7,
			MaxKeywordsPerPage:   250,
			MaxEpoch:             100,
			ExpectedProsperRatio: 0.6,
			EntryURL:             "https://haibara.ai/",
			MaxPageSize:          5 * 1024 * 1024,
			RecrawlMaxAge:        30 * 86400, // 30 天
			RecrawlCheckInterval: 3600,       // 1 小时
			RecrawlBatchSize:     500,
			MaxPriorityChildren:  100,
		},
		Search: SearchConfig{
			UseOnlineSnippet:     true,
			OnlineSnippetTimeout: 3,
			WeightDailyDecay:     0.996,
			LanguageWeight:       0.5,
			ConsecutiveKeyWeight: 1.3,
			BacklinkWeight:       1.0,
			ServerPort:           50082,
			FlushIntervalSeconds: 300,
			StatsRefreshInterval: 30,
			MissPenalty:          0.15,
		},
		Backlink: BacklinkConfig{
			Baseline: 200000,
		},
		Storage: StorageConfig{
			Path: "./savedata",
		},
		MySQL: MySQLConfig{
			Enabled:         false,
			Network:         "tcp",
			UnixSocket:      "",
			Host:            "localhost",
			Port:            3306,
			User:            "root",
			Password:        "",
			Database:        "sese_engine",
			ConnMaxLifetime: 3600, // 1小时
			MaxIdleConns:    10,
			MaxOpenConns:    100,
		},
		Redis: RedisConfig{
			Network:       "tcp",
			UnixSocket:    "",
			Host:          "localhost",
			Port:          6379,
			Password:      "",
			DB:            15, // 默认使用15号数据库
			PoolSize:      100,
			MinIdleConns:  10,
			ReadTimeout:   500,  // 毫秒
			WriteTimeout:  500,  // 毫秒
		},
		Prometheus: PrometheusConfig{
			CrawlerPort:  14950,
			BacklinkPort: 14952,
			SearchPort:   14953,
		},
	}
}

// 以下是向后兼容的常量定义，使用 Global 变量的值
// 在 Init() 被调用后，这些函数会返回加载的配置值

func init() {
	// 初始化时设置默认值
	Global = GetDefaultConfig()
}

// MaxURLsPerKey 返回配置值
func MaxURLsPerKey() int { return Global.Index.MaxURLsPerKey }

// MaxSameDomainPerKey 返回配置值
func MaxSameDomainPerKey() int { return Global.Index.MaxSameDomainPerKey }

// BigCleanThreshold 返回配置值
func BigCleanThreshold() int { return Global.Index.BigCleanThreshold }

// MaxNewURLsPerKey 返回配置值
func MaxNewURLsPerKey() int { return Global.Index.MaxNewURLsPerKey }

// MinURLsForNewKey 返回配置值
func MinURLsForNewKey() int { return Global.Index.MinURLsForNewKey }

// SpiderName 返回配置值
func SpiderName() string { return Global.Crawler.SpiderName }

// CrawlerCooldown 返回配置值
func CrawlerCooldown() int { return Global.Crawler.Cooldown }

// CrawlerWorkers 返回配置值
func CrawlerWorkers() int { return Global.Crawler.Workers }

// SetCrawlerWorkers 在运行时动态修改爬虫并发数（线程安全）。
func SetCrawlerWorkers(n int) {
	if n < 1 {
		n = 1
	}
	if n > 500 {
		n = 500
	}
	configMu.Lock()
	Global.Crawler.Workers = n
	configMu.Unlock()
}

// CrawlFocus 返回配置值
func CrawlFocus() float64 { return Global.Crawler.CrawlFocus }

// MaxKeywordsPerPage 返回配置值
func MaxKeywordsPerPage() int { return Global.Crawler.MaxKeywordsPerPage }

// MaxEpoch 返回配置值
func MaxEpoch() int { return Global.Crawler.MaxEpoch }

// ExpectedProsperRatio 返回配置值
func ExpectedProsperRatio() float64 { return Global.Crawler.ExpectedProsperRatio }

// EntryURL 返回配置值
func EntryURL() string { return Global.Crawler.EntryURL }

// MaxPageSize 返回单个页面最大抓取字节数（0=不限）。
func MaxPageSize() int { return Global.Crawler.MaxPageSize }

// RecrawlMaxAge 返回 URL 过期时间（秒），超过此时间的 URL 允许被重爬。
func RecrawlMaxAge() int { return Global.Crawler.RecrawlMaxAge }

// RecrawlCheckInterval 返回运行期间检查过期 URL 的间隔（秒）。
func RecrawlCheckInterval() int { return Global.Crawler.RecrawlCheckInterval }

// RecrawlBatchSize 返回每次检查最多释放的过期 URL 数量。
func RecrawlBatchSize() int { return Global.Crawler.RecrawlBatchSize }

// UseOnlineSnippet 返回配置值
func UseOnlineSnippet() bool { return Global.Search.UseOnlineSnippet }

// OnlineSnippetTimeout 返回配置值
func OnlineSnippetTimeout() int { return Global.Search.OnlineSnippetTimeout }

// WeightDailyDecay 返回配置值
func WeightDailyDecay() float64 { return Global.Search.WeightDailyDecay }

// LanguageWeight 返回配置值
func LanguageWeight() float64 { return Global.Search.LanguageWeight }

// ConsecutiveKeyWeight 返回配置值
func ConsecutiveKeyWeight() float64 { return Global.Search.ConsecutiveKeyWeight }

// BacklinkWeight 返回配置值
func BacklinkWeight() float64 { return Global.Search.BacklinkWeight }

// SearchServerPort 返回配置值
func SearchServerPort() int { return Global.Search.ServerPort }

// FlushIntervalSeconds 返回配置值
func FlushIntervalSeconds() int { return Global.Search.FlushIntervalSeconds }

// StatsRefreshInterval 返回统计缓存刷新间隔（秒），默认 30。
func StatsRefreshInterval() int {
	if Global.Search.StatsRefreshInterval <= 0 {
		return 30
	}
	return Global.Search.StatsRefreshInterval
}

// MissPenalty 返回缺词惩罚系数（0~1），值越大对缺少查询词的 URL 惩罚越重。
func MissPenalty() float64 { return Global.Search.MissPenalty }

// UnixSocket 返回 Unix socket 路径，空字符串表示不启用。
func UnixSocket() string { return Global.Search.UnixSocket }

// BacklinkBaseline 返回配置值
func BacklinkBaseline() int { return Global.Backlink.Baseline }

// PromPortCrawler 返回配置值
func PromPortCrawler() int { return Global.Prometheus.CrawlerPort }

// PromPortBacklink 返回配置值
func PromPortBacklink() int { return Global.Prometheus.BacklinkPort }

// PromPortSearch 返回配置值
func PromPortSearch() int { return Global.Prometheus.SearchPort }

// MaxPriorityChildren 返回 priorityChildren 队列的最大链接数（0=不限）。
func MaxPriorityChildren() int {
	if Global.Crawler.MaxPriorityChildren <= 0 {
		return 100 // 默认 100
	}
	return Global.Crawler.MaxPriorityChildren
}

// 为了向后兼容，保留 StoragePath 常量
const StoragePath = "./savedata"

// ---- MySQL 配置访问函数 ----

// MySQLEnabled 返回是否启用 MySQL（默认关闭）
func MySQLEnabled() bool {
	return Global.MySQL.Enabled
}

// MySQLDSN 返回 MySQL 连接字符串（DSN）
// 根据配置自动选择 Unix Socket 或 TCP 方式
func MySQLDSN() string {
	cfg := Global.MySQL
	if cfg.UnixSocket != "" {
		// 使用 Unix Socket 连接（推荐，本地连接性能更好）
		return cfg.User + ":" + cfg.Password + "@unix(" + cfg.UnixSocket + ")/" + cfg.Database + "?parseTime=true&loc=Local"
	}
	// 使用 TCP 连接
	return cfg.User + ":" + cfg.Password + "@tcp(" + cfg.Host + ":" + itoa(cfg.Port) + ")/" + cfg.Database + "?parseTime=true&loc=Local"
}

// MySQLConnMaxLifetime 返回连接最大生命周期（秒）
func MySQLConnMaxLifetime() int {
	if Global.MySQL.ConnMaxLifetime <= 0 {
		return 3600
	}
	return Global.MySQL.ConnMaxLifetime
}

// MySQLMaxIdleConns 返回最大空闲连接数
func MySQLMaxIdleConns() int {
	if Global.MySQL.MaxIdleConns <= 0 {
		return 10
	}
	return Global.MySQL.MaxIdleConns
}

// MySQLMaxOpenConns 返回最大打开连接数
func MySQLMaxOpenConns() int {
	if Global.MySQL.MaxOpenConns <= 0 {
		return 100
	}
	return Global.MySQL.MaxOpenConns
}

// ---- Redis 配置访问函数 ----

// RedisAddr 返回 Redis 连接地址
// 根据配置自动选择 Unix Socket 或 TCP 方式
func RedisAddr() string {
	cfg := Global.Redis
	if cfg.UnixSocket != "" {
		return cfg.UnixSocket
	}
	return cfg.Host + ":" + itoa(cfg.Port)
}

// RedisPoolSize 返回连接池大小
func RedisPoolSize() int {
	if Global.Redis.PoolSize <= 0 {
		return 100
	}
	return Global.Redis.PoolSize
}

// RedisMinIdleConns 返回最小空闲连接数
func RedisMinIdleConns() int {
	if Global.Redis.MinIdleConns <= 0 {
		return 10
	}
	return Global.Redis.MinIdleConns
}

// RedisDB 返回数据库编号
func RedisDB() int {
	return Global.Redis.DB
}

// RedisPassword 返回密码（空字符串表示无密码）
func RedisPassword() string {
	return Global.Redis.Password
}

// RedisReadTimeout 返回读超时时间（毫秒）
func RedisReadTimeout() int {
	if Global.Redis.ReadTimeout <= 0 {
		return 500
	}
	return Global.Redis.ReadTimeout
}

// RedisWriteTimeout 返回写超时时间（毫秒）
func RedisWriteTimeout() int {
	if Global.Redis.WriteTimeout <= 0 {
		return 500
	}
	return Global.Redis.WriteTimeout
}

// itoa 将 int 转换为字符串（避免导入 strconv）
func itoa(n int) string {
	if n == 0 {
		return "0"
	}
	result := ""
	for n > 0 {
		result = string(rune('0'+n%10)) + result
		n /= 10
	}
	return result
}
