package config

import (
	"fmt"
	"os"

	"gopkg.in/yaml.v3"
)

type DBConfig struct {
	Host     string `yaml:"host"`
	Port     string `yaml:"port"`
	User     string `yaml:"user"`
	Password string `yaml:"password"`
	DBName   string `yaml:"database"`
	Charset  string `yaml:"charset"`
}

type ServerConfig struct {
	Port string `yaml:"port"`
}

type Config struct {
	DB     DBConfig     `yaml:"mysql"`
	Server ServerConfig `yaml:"server"`
}

func Load() *Config {
	cfg := &Config{}

	data, err := os.ReadFile("config.yaml")
	if err != nil {
		fmt.Println("config.yaml not found, using env defaults")
		cfg.DB.Host = getEnv("DB_HOST", "127.0.0.1")
		cfg.DB.Port = getEnv("DB_PORT", "3306")
		cfg.DB.User = getEnv("DB_USER", "root")
		cfg.DB.Password = getEnv("DB_PASSWORD", "")
		cfg.DB.DBName = getEnv("DB_NAME", "ashare")
		cfg.DB.Charset = "utf8mb4"
		cfg.Server.Port = getEnv("SERVER_PORT", "8080")
		return cfg
	}

	if err := yaml.Unmarshal(data, cfg); err != nil {
		fmt.Printf("parse config.yaml failed: %v, using env defaults\n", err)
		cfg.DB.Host = getEnv("DB_HOST", "127.0.0.1")
		cfg.DB.Port = getEnv("DB_PORT", "3306")
		cfg.DB.User = getEnv("DB_USER", "root")
		cfg.DB.Password = getEnv("DB_PASSWORD", "")
		cfg.DB.DBName = getEnv("DB_NAME", "ashare")
		cfg.DB.Charset = "utf8mb4"
		cfg.Server.Port = getEnv("SERVER_PORT", "8080")
		return cfg
	}

	// Environment variables override config file
	if v := os.Getenv("DB_HOST"); v != "" {
		cfg.DB.Host = v
	}
	if v := os.Getenv("DB_PORT"); v != "" {
		cfg.DB.Port = v
	}
	if v := os.Getenv("DB_USER"); v != "" {
		cfg.DB.User = v
	}
	if v := os.Getenv("DB_PASSWORD"); v != "" {
		cfg.DB.Password = v
	}
	if v := os.Getenv("DB_NAME"); v != "" {
		cfg.DB.DBName = v
	}
	if v := os.Getenv("SERVER_PORT"); v != "" {
		cfg.Server.Port = v
	}

	return cfg
}

func (c *DBConfig) DSN() string {
	charset := c.Charset
	if charset == "" {
		charset = "utf8mb4"
	}
	return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local",
		c.User, c.Password, c.Host, c.Port, c.DBName, charset)
}

func getEnv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}
