package config

import (
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	"hyapp-admin-server/internal/platform/logging"

	"gopkg.in/yaml.v3"
)

type Config struct {
	ServiceName           string                   `yaml:"service_name"`
	NodeID                string                   `yaml:"node_id"`
	Environment           string                   `yaml:"environment"`
	Log                   logging.Config           `yaml:"log"`
	HTTPAddr              string                   `yaml:"http_addr"`
	MySQLDSN              string                   `yaml:"mysql_dsn"`
	UserMySQLDSN          string                   `yaml:"user_mysql_dsn"`
	WalletMySQLDSN        string                   `yaml:"wallet_mysql_dsn"`
	RobotProfileSource    RobotProfileSourceConfig `yaml:"robot_profile_source"`
	MySQLAutoMigrate      bool                     `yaml:"mysql_auto_migrate"`
	Migrations            MigrationConfig          `yaml:"migrations"`
	JWTSecret             string                   `yaml:"jwt_secret"`
	AccessTokenTTL        time.Duration            `yaml:"access_token_ttl"`
	RefreshTokenTTL       time.Duration            `yaml:"refresh_token_ttl"`
	CORSAllowedOrigins    []string                 `yaml:"cors_allowed_origins"`
	RefreshCookieSecure   bool                     `yaml:"refresh_cookie_secure"`
	RefreshCookieSameSite string                   `yaml:"refresh_cookie_same_site"`
	Bootstrap             BootstrapConfig          `yaml:"bootstrap"`
	BootstrapPassword     string                   `yaml:"bootstrap_password"`
	UserService           UserServiceConfig        `yaml:"user_service"`
	TencentCOS            TencentCOSConfig         `yaml:"tencent-cos"`
	Redis                 RedisConfig              `yaml:"redis"`
	Jobs                  JobsConfig               `yaml:"jobs"`
	WalletService         WalletServiceConfig      `yaml:"wallet_service"`
	RoomService           RoomServiceConfig        `yaml:"room_service"`
	ActivityService       ActivityServiceConfig    `yaml:"activity_service"`
	GameService           GameServiceConfig        `yaml:"game_service"`
	StatisticsService     StatisticsServiceConfig  `yaml:"statistics_service"`
}

type MigrationConfig struct {
	Enabled             bool   `yaml:"enabled"`
	Dir                 string `yaml:"dir"`
	AllowChecksumRepair bool   `yaml:"allow_checksum_repair"`
}

type UserServiceConfig struct {
	Addr           string        `yaml:"addr"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type WalletServiceConfig struct {
	Addr           string        `yaml:"addr"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type RoomServiceConfig struct {
	Addr           string        `yaml:"addr"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type ActivityServiceConfig struct {
	Addr           string        `yaml:"addr"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type GameServiceConfig struct {
	Addr           string        `yaml:"addr"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type StatisticsServiceConfig struct {
	BaseURL        string        `yaml:"base_url"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type RobotProfileSourceConfig struct {
	MySQLDSN       string        `yaml:"mysql_dsn"`
	RequestTimeout time.Duration `yaml:"request_timeout"`
}

type TencentCOSConfig struct {
	Enabled      bool   `yaml:"enabled"`
	SecretID     string `yaml:"secret-id"`
	SecretKey    string `yaml:"secret-key"`
	BucketName   string `yaml:"bucket-name"`
	Region       string `yaml:"region"`
	AccessURL    string `yaml:"access-url"`
	ObjectPrefix string `yaml:"object-prefix"`
}

type BootstrapConfig struct {
	Enabled      bool   `yaml:"enabled"`
	SeedDemoData bool   `yaml:"seed_demo_data"`
	Username     string `yaml:"username"`
	Name         string `yaml:"name"`
	Team         string `yaml:"team"`
	Password     string `yaml:"password"`
}

type RedisConfig struct {
	Enabled   bool   `yaml:"enabled"`
	Addr      string `yaml:"addr"`
	Password  string `yaml:"password"`
	DB        int    `yaml:"db"`
	KeyPrefix string `yaml:"key_prefix"`
}

type JobsConfig struct {
	Enabled        bool          `yaml:"enabled"`
	WorkerInterval time.Duration `yaml:"worker_interval"`
	LeaseTTL       time.Duration `yaml:"lease_ttl"`
	MaxAttempts    int           `yaml:"max_attempts"`
	ArtifactDir    string        `yaml:"artifact_dir"`
}

func Default() Config {
	return Config{
		ServiceName:    "admin-server",
		NodeID:         "admin-local",
		Environment:    "local",
		Log:            logging.DefaultConfig(),
		HTTPAddr:       ":13100",
		MySQLDSN:       "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_admin?parseTime=true&charset=utf8mb4&loc=UTC",
		UserMySQLDSN:   "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_user?parseTime=true&charset=utf8mb4&loc=UTC",
		WalletMySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_wallet?parseTime=true&charset=utf8mb4&loc=UTC",
		RobotProfileSource: RobotProfileSourceConfig{
			RequestTimeout: 5 * time.Second,
		},
		MySQLAutoMigrate: true,
		Migrations: MigrationConfig{
			Enabled:             true,
			Dir:                 "migrations",
			AllowChecksumRepair: true,
		},
		JWTSecret:       "dev-shared-secret",
		AccessTokenTTL:  12 * time.Hour,
		RefreshTokenTTL: 7 * 24 * time.Hour,
		CORSAllowedOrigins: []string{
			"http://localhost:7001",
			"http://127.0.0.1:7001",
		},
		RefreshCookieSecure:   false,
		RefreshCookieSameSite: "lax",
		Bootstrap: BootstrapConfig{
			Enabled:      true,
			SeedDemoData: true,
			Username:     "admin",
			Name:         "admin",
			Team:         "平台运维",
			Password:     "admin123",
		},
		BootstrapPassword: "admin123",
		UserService: UserServiceConfig{
			Addr:           "127.0.0.1:13005",
			RequestTimeout: 3 * time.Second,
		},
		WalletService: WalletServiceConfig{
			Addr:           "127.0.0.1:13004",
			RequestTimeout: 3 * time.Second,
		},
		RoomService: RoomServiceConfig{
			Addr:           "127.0.0.1:13001",
			RequestTimeout: 3 * time.Second,
		},
		ActivityService: ActivityServiceConfig{
			Addr:           "127.0.0.1:13006",
			RequestTimeout: 3 * time.Second,
		},
		GameService: GameServiceConfig{
			Addr:           "127.0.0.1:13008",
			RequestTimeout: 3 * time.Second,
		},
		StatisticsService: StatisticsServiceConfig{
			BaseURL:        "http://127.0.0.1:13010",
			RequestTimeout: 3 * time.Second,
		},
		TencentCOS: TencentCOSConfig{
			Enabled:      false,
			ObjectPrefix: "admin",
		},
		Redis: RedisConfig{
			Enabled:   true,
			Addr:      "127.0.0.1:13379",
			KeyPrefix: "admin:",
		},
		Jobs: JobsConfig{
			Enabled:        true,
			WorkerInterval: 5 * time.Second,
			LeaseTTL:       2 * time.Minute,
			MaxAttempts:    3,
			ArtifactDir:    "storage/exports",
		},
	}
}

func Load(path string) (Config, error) {
	cfg := Default()
	if path == "" {
		return cfg, nil
	}

	body, err := os.ReadFile(path)
	if err != nil {
		return Config{}, err
	}

	if err := yaml.Unmarshal(body, &cfg); err != nil {
		return Config{}, err
	}

	cfg.Normalize()
	if err := cfg.Validate(); err != nil {
		return Config{}, err
	}

	return cfg, nil
}

func (cfg *Config) Normalize() {
	cfg.Environment = strings.ToLower(strings.TrimSpace(cfg.Environment))
	if cfg.Environment == "" {
		cfg.Environment = "local"
	}
	if cfg.Log.Level == "" {
		cfg.Log.Level = "info"
	}
	if cfg.Log.Format == "" {
		cfg.Log.Format = "json"
	}
	if cfg.Log.MaxPayloadBytes <= 0 {
		cfg.Log.MaxPayloadBytes = 2048
	}
	cfg.RefreshCookieSameSite = strings.ToLower(strings.TrimSpace(cfg.RefreshCookieSameSite))
	if cfg.RefreshCookieSameSite == "" {
		cfg.RefreshCookieSameSite = "lax"
	}
	cfg.ServiceName = strings.TrimSpace(cfg.ServiceName)
	cfg.NodeID = strings.TrimSpace(cfg.NodeID)
	if cfg.NodeID == "" {
		cfg.NodeID = cfg.ServiceName
	}
	if cfg.Bootstrap.Password == "" && cfg.BootstrapPassword != "" {
		cfg.Bootstrap.Password = cfg.BootstrapPassword
	}
	if cfg.Bootstrap.Username == "" {
		cfg.Bootstrap.Username = "admin"
	}
	if cfg.Bootstrap.Name == "" {
		cfg.Bootstrap.Name = cfg.Bootstrap.Username
	}
	if cfg.Bootstrap.Team == "" {
		cfg.Bootstrap.Team = "平台运维"
	}
	if cfg.Jobs.LeaseTTL <= 0 {
		cfg.Jobs.LeaseTTL = 2 * time.Minute
	}
	if cfg.Jobs.MaxAttempts <= 0 {
		cfg.Jobs.MaxAttempts = 3
	}
	if cfg.Jobs.ArtifactDir == "" {
		cfg.Jobs.ArtifactDir = "storage/exports"
	}
	cfg.UserService.Addr = strings.TrimSpace(cfg.UserService.Addr)
	if cfg.UserService.RequestTimeout <= 0 {
		cfg.UserService.RequestTimeout = 3 * time.Second
	}
	cfg.WalletService.Addr = strings.TrimSpace(cfg.WalletService.Addr)
	if cfg.WalletService.RequestTimeout <= 0 {
		cfg.WalletService.RequestTimeout = 3 * time.Second
	}
	cfg.RoomService.Addr = strings.TrimSpace(cfg.RoomService.Addr)
	if cfg.RoomService.Addr == "" {
		cfg.RoomService.Addr = "127.0.0.1:13001"
	}
	if cfg.RoomService.RequestTimeout <= 0 {
		cfg.RoomService.RequestTimeout = 3 * time.Second
	}
	cfg.ActivityService.Addr = strings.TrimSpace(cfg.ActivityService.Addr)
	if cfg.ActivityService.RequestTimeout <= 0 {
		cfg.ActivityService.RequestTimeout = 3 * time.Second
	}
	cfg.GameService.Addr = strings.TrimSpace(cfg.GameService.Addr)
	if cfg.GameService.Addr == "" {
		cfg.GameService.Addr = "127.0.0.1:13008"
	}
	if cfg.GameService.RequestTimeout <= 0 {
		cfg.GameService.RequestTimeout = 3 * time.Second
	}
	cfg.StatisticsService.BaseURL = strings.TrimRight(strings.TrimSpace(cfg.StatisticsService.BaseURL), "/")
	if cfg.StatisticsService.BaseURL == "" {
		cfg.StatisticsService.BaseURL = "http://127.0.0.1:13010"
	}
	if cfg.StatisticsService.RequestTimeout <= 0 {
		cfg.StatisticsService.RequestTimeout = 3 * time.Second
	}
	cfg.RobotProfileSource.MySQLDSN = strings.TrimSpace(cfg.RobotProfileSource.MySQLDSN)
	if cfg.RobotProfileSource.RequestTimeout <= 0 {
		cfg.RobotProfileSource.RequestTimeout = 5 * time.Second
	}
	cfg.TencentCOS.SecretID = strings.TrimSpace(cfg.TencentCOS.SecretID)
	cfg.TencentCOS.SecretKey = strings.TrimSpace(cfg.TencentCOS.SecretKey)
	cfg.TencentCOS.BucketName = strings.TrimSpace(cfg.TencentCOS.BucketName)
	cfg.TencentCOS.Region = strings.TrimSpace(cfg.TencentCOS.Region)
	cfg.TencentCOS.AccessURL = strings.TrimRight(strings.TrimSpace(cfg.TencentCOS.AccessURL), "/")
	cfg.TencentCOS.ObjectPrefix = strings.Trim(strings.TrimSpace(cfg.TencentCOS.ObjectPrefix), "/")
	if cfg.TencentCOS.ObjectPrefix == "" {
		cfg.TencentCOS.ObjectPrefix = "admin"
	}
}

func (cfg Config) Validate() error {
	if !validEnvironment(cfg.Environment) {
		return errors.New("environment must be one of local, dev, staging, prod")
	}
	if strings.TrimSpace(cfg.ServiceName) == "" {
		return errors.New("service_name is required")
	}
	if strings.TrimSpace(cfg.NodeID) == "" {
		return errors.New("node_id is required")
	}
	if strings.TrimSpace(cfg.HTTPAddr) == "" {
		return errors.New("http_addr is required")
	}
	if strings.TrimSpace(cfg.MySQLDSN) == "" {
		return errors.New("mysql_dsn is required")
	}
	if strings.TrimSpace(cfg.UserMySQLDSN) == "" {
		return errors.New("user_mysql_dsn is required")
	}
	if strings.TrimSpace(cfg.WalletMySQLDSN) == "" {
		return errors.New("wallet_mysql_dsn is required")
	}
	if cfg.Migrations.Enabled && strings.TrimSpace(cfg.Migrations.Dir) == "" {
		return errors.New("migrations.dir is required when migrations are enabled")
	}
	if isLockedEnvironment(cfg.Environment) && cfg.Migrations.AllowChecksumRepair {
		return errors.New("migrations.allow_checksum_repair must be false outside local/dev")
	}
	if strings.TrimSpace(cfg.JWTSecret) == "" {
		return errors.New("jwt_secret is required")
	}
	if cfg.AccessTokenTTL <= 0 {
		return errors.New("access_token_ttl must be greater than 0")
	}
	if cfg.RefreshTokenTTL <= 0 {
		return errors.New("refresh_token_ttl must be greater than 0")
	}
	switch strings.ToLower(strings.TrimSpace(cfg.RefreshCookieSameSite)) {
	case "lax", "strict", "none":
	default:
		return errors.New("refresh_cookie_same_site must be one of lax, strict, none")
	}
	if strings.EqualFold(strings.TrimSpace(cfg.RefreshCookieSameSite), "none") && !cfg.RefreshCookieSecure {
		return errors.New("refresh_cookie_secure must be true when refresh_cookie_same_site is none")
	}
	if isLockedEnvironment(cfg.Environment) {
		if cfg.JWTSecret == "dev-shared-secret" || len(cfg.JWTSecret) < 32 {
			return errors.New("jwt_secret must be a non-default value with at least 32 characters outside local/dev")
		}
		if cfg.MySQLAutoMigrate {
			return errors.New("mysql_auto_migrate must be false outside local/dev")
		}
		if cfg.Bootstrap.Enabled && cfg.Bootstrap.Password == "admin123" {
			return errors.New("bootstrap.password must not use the default value outside local/dev")
		}
	}
	if len(cfg.CORSAllowedOrigins) == 0 {
		return errors.New("cors_allowed_origins must not be empty")
	}
	if cfg.Bootstrap.Enabled && strings.TrimSpace(cfg.Bootstrap.Password) == "" {
		return errors.New("bootstrap.password is required when bootstrap is enabled")
	}
	if strings.TrimSpace(cfg.UserService.Addr) == "" {
		return errors.New("user_service.addr is required")
	}
	if cfg.UserService.RequestTimeout <= 0 {
		return errors.New("user_service.request_timeout must be greater than 0")
	}
	if strings.TrimSpace(cfg.WalletService.Addr) == "" {
		return errors.New("wallet_service.addr is required")
	}
	if cfg.WalletService.RequestTimeout <= 0 {
		return errors.New("wallet_service.request_timeout must be greater than 0")
	}
	if strings.TrimSpace(cfg.RoomService.Addr) == "" {
		return errors.New("room_service.addr is required")
	}
	if cfg.RoomService.RequestTimeout <= 0 {
		return errors.New("room_service.request_timeout must be greater than 0")
	}
	if strings.TrimSpace(cfg.ActivityService.Addr) == "" {
		return errors.New("activity_service.addr is required")
	}
	if cfg.ActivityService.RequestTimeout <= 0 {
		return errors.New("activity_service.request_timeout must be greater than 0")
	}
	if strings.TrimSpace(cfg.GameService.Addr) == "" {
		return errors.New("game_service.addr is required")
	}
	if cfg.GameService.RequestTimeout <= 0 {
		return errors.New("game_service.request_timeout must be greater than 0")
	}
	if strings.TrimSpace(cfg.StatisticsService.BaseURL) == "" {
		return errors.New("statistics_service.base_url is required")
	}
	if cfg.StatisticsService.RequestTimeout <= 0 {
		return errors.New("statistics_service.request_timeout must be greater than 0")
	}
	if isLockedEnvironment(cfg.Environment) && strings.TrimSpace(cfg.RobotProfileSource.MySQLDSN) == "" {
		return errors.New("robot_profile_source.mysql_dsn is required outside local/dev")
	}
	if cfg.RobotProfileSource.RequestTimeout <= 0 {
		return errors.New("robot_profile_source.request_timeout must be greater than 0")
	}
	if cfg.TencentCOS.Enabled {
		if cfg.TencentCOS.SecretID == "" || cfg.TencentCOS.SecretKey == "" || cfg.TencentCOS.BucketName == "" || cfg.TencentCOS.Region == "" || cfg.TencentCOS.AccessURL == "" {
			return errors.New("tencent-cos config is incomplete")
		}
		if !strings.HasPrefix(cfg.TencentCOS.AccessURL, "http://") && !strings.HasPrefix(cfg.TencentCOS.AccessURL, "https://") {
			return errors.New("tencent-cos.access-url must be an absolute url")
		}
	}
	if cfg.Redis.Enabled && strings.TrimSpace(cfg.Redis.Addr) == "" {
		return errors.New("redis.addr is required when redis is enabled")
	}
	if cfg.Redis.DB < 0 {
		return fmt.Errorf("redis.db must be greater than or equal to 0")
	}
	if cfg.Jobs.Enabled && cfg.Jobs.WorkerInterval <= 0 {
		return errors.New("jobs.worker_interval must be greater than 0")
	}
	if cfg.Jobs.Enabled && cfg.Jobs.LeaseTTL <= 0 {
		return errors.New("jobs.lease_ttl must be greater than 0")
	}
	if cfg.Jobs.Enabled && cfg.Jobs.MaxAttempts <= 0 {
		return errors.New("jobs.max_attempts must be greater than 0")
	}
	if cfg.Jobs.Enabled && strings.TrimSpace(cfg.Jobs.ArtifactDir) == "" {
		return errors.New("jobs.artifact_dir is required when jobs are enabled")
	}
	return nil
}

func validEnvironment(value string) bool {
	switch value {
	case "local", "dev", "staging", "prod":
		return true
	default:
		return false
	}
}

func isLockedEnvironment(value string) bool {
	return value == "staging" || value == "prod"
}
