// Package utils 提供配置文件加载工具，支持从INI文件加载配置到结构体
// 支持类型: int, string, bool, float, time.Duration 及其切片类型
// 特性:
//   - 支持默认值标签 `default`
//   - 支持INI路径标签 `ini:"section.key"`
//   - 支持时间单位转换标签 `multiplier`
//   - 自动处理切片类型（逗号分隔）
package main

import (
	"log"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"time"

	"github.com/go-ini/ini"
)

// LoadConfig 从INI文件加载配置到结构体
// 参数:
//
//	configPtr: 指向配置结构体的指针（必须是结构体指针）
//	filename: INI配置文件的路径
//
// 返回:
//
//	error: 加载成功返回nil，失败返回ConfigError详细错误信息
//
// 用法说明:
//  1. 定义配置结构体，使用标签声明INI映射关系和默认值
//  2. 调用LoadConfig(&config, "config.ini")
//  3. 检查错误并应用配置
//
// 示例结构体:
//
//	type AppConfig struct {
//	    Port     int           `ini:"server.port" default:"8080"`
//	    Timeout  time.Duration `ini:"server.timeout" multiplier:"1s"`
//	    Features []string      `ini:"server.features"`
//	}
func LoadConfig(configPtr interface{}, filename string) error {
	// 验证输入必须是指针
	if reflect.TypeOf(configPtr).Kind() != reflect.Ptr {
		return &ConfigError{Message: "configPtr必须是指向结构体的指针"} // 返回错误
	}

	// 验证输入必须指向结构体
	configValue := reflect.ValueOf(configPtr).Elem()
	// 确保输入是结构体
	if configValue.Kind() != reflect.Struct {
		return &ConfigError{Message: "configPtr必须指向结构体"} // 返回错误
	}

	// 设置默认值（如果结构体有默认值）
	setDefaultValues(configValue)

	// 检查配置文件是否存在
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		log.Printf("配置文件不存在: %s, 使用默认值", filename) // 打印信息
		return nil                                             // 返回错误
	}

	// 加载INI文件
	iniCfg, err := ini.Load(filename)
	// 处理错误
	if err != nil {
		return &ConfigError{Message: "加载配置文件失败", Cause: err} // 返回错误
	}

	// 映射配置到结构体
	if err := mapConfig(iniCfg, configValue); err != nil {
		return err // 返回错误
	}

	// 处理特殊类型（如time.Duration）
	processSpecialTypes(configValue)

	// 返回成功
	return nil
}

// setDefaultValues 设置结构体字段的默认值
// 遍历结构体字段，检测`default`标签并设置初始值
// setDefaultValues 设置结构体字段的默认值
func setDefaultValues(configValue reflect.Value) {
	// 获取结构体类型
	configType := configValue.Type()

	// 遍历字段
	for i := 0; i < configType.NumField(); i++ {
		// 获取字段信息
		field := configType.Field(i)
		// 获取字段值
		fieldValue := configValue.Field(i)

		// 如果字段是结构体，递归处理
		if fieldValue.Kind() == reflect.Struct {
			setDefaultValues(fieldValue)
			continue
		}

		// 如果字段已经设置了值，跳过
		if !fieldValue.IsZero() {
			continue // 跳过
		}

		// 检查是否有默认值标签
		if defaultValue, ok := field.Tag.Lookup("default"); ok {
			setValueFromString(fieldValue, defaultValue) // 设置字段值
		}
	}
}

// mapConfig 将INI配置映射到结构体字段
// 解析`ini`标签获取section和key，读取对应配置值
func mapConfig(iniCfg *ini.File, configValue reflect.Value) error {
	// 获取结构体类型
	configType := configValue.Type()

	// 遍历字段
	for i := 0; i < configType.NumField(); i++ {
		field := configType.Field(i)       // 获取字段信息
		fieldValue := configValue.Field(i) // 获取字段值

		// 如果字段是结构体，递归处理
		if fieldValue.Kind() == reflect.Struct {
			if err := mapConfig(iniCfg, fieldValue); err != nil {
				return err
			}
			continue
		}

		// 获取INI标签
		iniTag, ok := field.Tag.Lookup("ini")
		// 如果没有INI标签，跳过这个字段
		if !ok || iniTag == "" {
			continue // 跳过
		}

		// 解析INI键名（支持section.key格式）
		sectionName, keyName := parseIniTag(iniTag)

		// 获取INI值
		section, err := iniCfg.GetSection(sectionName)
		// 如果section不存在
		if err != nil {
			continue // 跳过这个字段
		}

		// 获取INI键值
		key, err := section.GetKey(keyName)
		// 如果key不存在
		if err != nil {
			continue //跳过这个字段
		}

		// 设置结构体字段值
		if err := setFieldValue(fieldValue, key); err != nil {
			// 返回错误
			return &ConfigError{
				Message: "设置字段值失败", // 返回错误信息
				Cause:   err,              // 返回错误原因
				Field:   field.Name,       // 返回字段名称
				Tag:     iniTag,           // 返回标签
			}
		}
	}

	return nil
}

// parseIniTag 解析INI标签格式
// 输入: "section.key" 格式的字符串
// 返回: (section名称, key名称)
func parseIniTag(tag string) (section, key string) {
	// 默认section
	section = "DEFAULT"
	// 默认key
	key = tag

	// 检查是否有section前缀
	if parts := regexp.MustCompile(`^(\w+)\.(\w+)$`).FindStringSubmatch(tag); len(parts) == 3 {
		section = parts[1] // 设置section
		key = parts[2]     // 设置key
	}

	// 返回section和key
	return section, key
}

// setFieldValue 根据INI键值设置结构体字段值
// 自动处理基础类型和time.Duration类型转换
func setFieldValue(fieldValue reflect.Value, key *ini.Key) error {
	// 检查字段类型
	switch fieldValue.Kind() {
	case reflect.String: // 字符串类型
		fieldValue.SetString(key.String()) // 设置字段值
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 整数类型
		// 特殊处理time.Duration类型
		if fieldValue.Type() == reflect.TypeOf(time.Duration(0)) {
			duration, err := time.ParseDuration(key.String()) // 解析字符串为time.Duration
			// 处理错误
			if err != nil {
				return err // 返回错误
			}
			fieldValue.SetInt(int64(duration)) // 设置字段值
		} else {
			intValue, err := key.Int() // 获取整数值
			// 处理错误
			if err != nil {
				return err // 返回错误
			}
			fieldValue.SetInt(int64(intValue)) // 设置字段值
		}
	case reflect.Bool: // 布尔类型
		boolValue, err := key.Bool() // 获取布尔值
		// 处理错误
		if err != nil {
			return err // 返回错误
		}
		fieldValue.SetBool(boolValue) // 设置字段值
	case reflect.Float32, reflect.Float64: // 浮点类型
		floatValue, err := key.Float64() // 获取浮点值
		// 处理错误
		if err != nil {
			return err // 返回错误
		}
		fieldValue.SetFloat(floatValue) // 设置字段值
	case reflect.Slice: // 切片类型
		return setSliceValue(fieldValue, key) // 处理切片类型字段
	default: // 其他类型
		return &ConfigError{Message: "不支持的字段类型", FieldType: fieldValue.Type().String()} // 返回错误
	}

	// 返回成功
	return nil
}

// setSliceValue 设置切片类型字段值
// 将逗号分隔的字符串解析为指定类型的切片
func setSliceValue(fieldValue reflect.Value, key *ini.Key) error {
	// 获取切片元素类型
	sliceType := fieldValue.Type().Elem()
	// 获取逗号分隔的值
	values := key.Strings(",")

	// 创建新切片
	slice := reflect.MakeSlice(fieldValue.Type(), len(values), len(values))

	// 遍历值
	for i, val := range values {
		elemValue := reflect.New(sliceType).Elem() // 创建新元素

		// 根据切片元素类型设置值
		switch sliceType.Kind() {
		case reflect.String: // 字符串类型
			elemValue.SetString(val) // 设置字段值
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // 整数类型
			intVal, err := strconv.ParseInt(val, 10, 64) // 解析字符串为整数
			// 处理错误
			if err != nil {
				return err // 返回错误
			}
			elemValue.SetInt(intVal) // 设置字段值
		case reflect.Float32, reflect.Float64: // 浮点类型
			floatVal, err := strconv.ParseFloat(val, 64) // 解析字符串为浮点数
			// 处理错误
			if err != nil {
				return err // 返回错误
			}
			elemValue.SetFloat(floatVal) // 设置字段值
		case reflect.Bool: // 布尔类型
			boolVal, err := strconv.ParseBool(val) // 解析字符串为布尔值
			// 处理错误
			if err != nil {
				return err // 返回错误
			}
			elemValue.SetBool(boolVal) // 设置字段值
		default:
			return &ConfigError{Message: "不支持的切片元素类型", FieldType: sliceType.String()} // 返回错误
		}

		slice.Index(i).Set(elemValue) // 设置切片元素
	}

	// 设置切片字段值
	fieldValue.Set(slice)
	// 返回成功
	return nil
}

// setValueFromString 从字符串解析值到结构体字段（用于默认值）
// 支持基础类型转换，不处理复杂类型
func setValueFromString(fieldValue reflect.Value, valueStr string) {
	switch fieldValue.Kind() {
	case reflect.String:
		fieldValue.SetString(valueStr)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if intValue, err := strconv.ParseInt(valueStr, 10, 64); err == nil {
			fieldValue.SetInt(intValue)
		}
	case reflect.Bool:
		if boolValue, err := strconv.ParseBool(valueStr); err == nil {
			fieldValue.SetBool(boolValue)
		}
	case reflect.Float32, reflect.Float64:
		if floatValue, err := strconv.ParseFloat(valueStr, 64); err == nil {
			fieldValue.SetFloat(floatValue)
		}
	case reflect.Slice:
		// 切片类型需要特殊处理，这里简化处理
	default:
		// 保留原panic调用，但修改提示信息
		panic("未处理的默认值类型")
	}
}

// processSpecialTypes 处理特殊类型转换
// 当前支持time.Duration的倍数转换（使用multiplier标签）
// processSpecialTypes 处理特殊类型转换
func processSpecialTypes(configValue reflect.Value) {
	// 获取结构体类型
	configType := configValue.Type()

	// 遍历结构体字段
	for i := 0; i < configType.NumField(); i++ {
		field := configType.Field(i)       // 获取字段
		fieldValue := configValue.Field(i) // 获取字段值

		// 如果字段是结构体，递归处理
		if fieldValue.Kind() == reflect.Struct {
			processSpecialTypes(fieldValue)
			continue
		}

		// 处理time.Duration类型的倍数转换
		if fieldValue.Type() == reflect.TypeOf(time.Duration(0)) {
			// 检查是否存在multiplier标签
			if multiplier, ok := field.Tag.Lookup("multiplier"); ok {
				// 解析multiplier标签
				if mult, err := time.ParseDuration(multiplier); err == nil {
					duration := time.Duration(fieldValue.Int()) // 将字段值转换为time.Duration
					fieldValue.SetInt(int64(duration * mult))   // 将字段值设置为转换后的时间
				}
			}
		}
	}
}

// ConfigError 自定义配置错误类型
// 包含错误原因、字段信息和原始错误
type ConfigError struct {
	Message   string
	Cause     error
	Field     string
	FieldType string
	Tag       string
}

// Error 实现error接口，提供详细错误信息
func (e *ConfigError) Error() string {
	msg := "配置错误: " + e.Message
	if e.Field != "" {
		msg += " [字段: " + e.Field + "]"
	}
	if e.FieldType != "" {
		msg += " [类型: " + e.FieldType + "]"
	}
	if e.Tag != "" {
		msg += " [标签: " + e.Tag + "]"
	}
	if e.Cause != nil {
		msg += " - " + e.Cause.Error()
	}
	return msg
}
