package configx

import (
	"bufio"
	"errors"
	"os"
	"strings"

	"gopkg.in/yaml.v3"
)

// LoadYAML 把指定 YAML 文件反序列化到已有配置对象里。
// 调用方通常会先准备一个带默认值的 struct，再让 YAML 局部覆盖它。
func LoadYAML(path string, out any) error {
	body, err := os.ReadFile(path)
	if err != nil {
		return err
	}

	return yaml.Unmarshal(body, out)
}

// LoadYAMLWithEnv 只展开 ${NAME} 形式的环境变量；普通 $ 字符保留，避免误伤密钥正文。
func LoadYAMLWithEnv(path string, out any) error {
	body, err := os.ReadFile(path)
	if err != nil {
		return err
	}

	return yaml.Unmarshal([]byte(expandBraceEnv(string(body))), out)
}

// LoadDotEnvFile 读取本地运行时 .env 文件；真实环境变量优先，文件里的值只补本地缺省。
func LoadDotEnvFile(path string) error {
	file, err := os.Open(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil
		}
		return err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		key, value, ok := parseDotEnvLine(scanner.Text())
		if !ok {
			continue
		}
		if _, exists := os.LookupEnv(key); exists {
			continue
		}
		if err := os.Setenv(key, value); err != nil {
			return err
		}
	}
	return scanner.Err()
}

func parseDotEnvLine(line string) (string, string, bool) {
	line = strings.TrimSpace(line)
	if line == "" || strings.HasPrefix(line, "#") {
		return "", "", false
	}
	line = strings.TrimPrefix(line, "export ")
	key, value, ok := strings.Cut(line, "=")
	if !ok {
		return "", "", false
	}
	key = strings.TrimSpace(key)
	if key == "" {
		return "", "", false
	}
	value = strings.TrimSpace(value)
	if len(value) >= 2 {
		quote := value[0]
		if (quote == '"' || quote == '\'') && value[len(value)-1] == quote {
			value = value[1 : len(value)-1]
		}
	}
	return key, value, true
}

func expandBraceEnv(input string) string {
	var builder strings.Builder
	for i := 0; i < len(input); {
		if input[i] == '$' && i+1 < len(input) && input[i+1] == '{' {
			end := strings.IndexByte(input[i+2:], '}')
			if end >= 0 {
				key := input[i+2 : i+2+end]
				builder.WriteString(os.Getenv(key))
				i += end + 3
				continue
			}
		}
		builder.WriteByte(input[i])
		i++
	}
	return builder.String()
}
