// Package config defines cron-service runtime configuration.
package config

import (
	"strings"

	"hyapp/pkg/configx"
	"hyapp/pkg/logx"
)

// Config describes cron-service startup and schedule settings.
type Config struct {
	// ServiceName is written into logs and health metadata.
	ServiceName string `yaml:"service_name"`
	// NodeID identifies this scheduler instance in run history and task leases.
	NodeID string `yaml:"node_id"`
	// Environment is the runtime environment tag used by structured logs.
	Environment string `yaml:"environment"`
	// GRPCAddr exposes gRPC health and future internal management APIs.
	GRPCAddr string `yaml:"grpc_addr"`
	// HealthHTTPAddr exposes HTTP health for CLB and deploy scripts only.
	HealthHTTPAddr string `yaml:"health_http_addr"`
	// MySQLDSN stores cron run history and schedule-level leases only.
	MySQLDSN string `yaml:"mysql_dsn"`
	// UserServiceAddr is the owner endpoint for user/auth/host batch tasks.
	UserServiceAddr string `yaml:"user_service_addr"`
	// ActivityServiceAddr is the owner endpoint for activity/message batch tasks.
	ActivityServiceAddr string `yaml:"activity_service_addr"`
	// GameServiceAddr is the owner endpoint for game/order relay batch tasks.
	GameServiceAddr string `yaml:"game_service_addr"`
	// WalletServiceAddr is the owner endpoint for wallet/VIP/settlement tasks.
	WalletServiceAddr string `yaml:"wallet_service_addr"`
	// Tasks contains per-task schedule knobs. Disabled tasks are documented but not started.
	Tasks map[string]TaskConfig `yaml:"tasks"`
	// Log controls stdout structured logging.
	Log logx.Config `yaml:"log"`
}

// TaskConfig keeps scheduler knobs as strings so YAML stays easy to read.
type TaskConfig struct {
	// Enabled controls whether cron-service starts a loop for this task.
	Enabled bool `yaml:"enabled"`
	// Interval is the delay between successful schedule attempts.
	Interval string `yaml:"interval"`
	// Timeout bounds the owner service RPC call for one batch.
	Timeout string `yaml:"timeout"`
	// LockTTL is the owner service task claim lock budget.
	LockTTL string `yaml:"lock_ttl"`
	// BatchSize is passed to owner service batch RPCs.
	BatchSize int `yaml:"batch_size"`
	// PendingPublishMaxAge is used by mic_open_session_compensation for pending_publish sessions.
	PendingPublishMaxAge string `yaml:"pending_publish_max_age"`
	// PublishingSessionMaxAge is used by mic_open_session_compensation for publishing sessions.
	PublishingSessionMaxAge string `yaml:"publishing_session_max_age"`
	// OpenSessionMaxAge is used by room_open_session_compensation for open in-room sessions.
	OpenSessionMaxAge string `yaml:"open_session_max_age"`
}

// Default returns local development defaults that follow the 13xxx port rule.
func Default() Config {
	return Config{
		ServiceName:         "cron-service",
		NodeID:              "cron-local",
		Environment:         "local",
		GRPCAddr:            ":13007",
		HealthHTTPAddr:      ":13107",
		MySQLDSN:            "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_cron?parseTime=true&charset=utf8mb4&loc=UTC",
		UserServiceAddr:     "127.0.0.1:13005",
		ActivityServiceAddr: "127.0.0.1:13006",
		GameServiceAddr:     "127.0.0.1:13008",
		WalletServiceAddr:   "127.0.0.1:13004",
		Tasks:               defaultTasks(),
		Log: logx.Config{
			Level:           "info",
			Format:          "json",
			MaxPayloadBytes: 2048,
		},
	}
}

// Load reads a YAML file into Config and normalizes empty fields.
func Load(path string) (Config, error) {
	cfg := Default()
	if path == "" {
		return cfg, nil
	}
	if err := configx.LoadYAML(path, &cfg); err != nil {
		return Config{}, err
	}
	normalize(&cfg)
	return cfg, nil
}

func normalize(cfg *Config) {
	cfg.ServiceName = strings.TrimSpace(cfg.ServiceName)
	if cfg.ServiceName == "" {
		cfg.ServiceName = "cron-service"
	}
	cfg.NodeID = strings.TrimSpace(cfg.NodeID)
	if cfg.NodeID == "" {
		cfg.NodeID = cfg.ServiceName
	}
	cfg.Environment = strings.ToLower(strings.TrimSpace(cfg.Environment))
	if cfg.Environment == "" {
		cfg.Environment = "local"
	}
	cfg.GRPCAddr = strings.TrimSpace(cfg.GRPCAddr)
	if cfg.GRPCAddr == "" {
		cfg.GRPCAddr = ":13007"
	}
	cfg.HealthHTTPAddr = strings.TrimSpace(cfg.HealthHTTPAddr)
	if cfg.HealthHTTPAddr == "" {
		cfg.HealthHTTPAddr = ":13107"
	}
	cfg.GameServiceAddr = strings.TrimSpace(cfg.GameServiceAddr)
	if cfg.GameServiceAddr == "" {
		cfg.GameServiceAddr = "127.0.0.1:13008"
	}
	cfg.Tasks = normalizeTasks(cfg.Tasks)
}

func defaultTasks() map[string]TaskConfig {
	return map[string]TaskConfig{
		"login_ip_risk": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "3s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
		"user_region_rebuild": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "5s",
			LockTTL:   "30s",
			BatchSize: 500,
		},
		"message_fanout": {
			Enabled:   true,
			Interval:  "1s",
			Timeout:   "10s",
			LockTTL:   "30s",
			BatchSize: 500,
		},
		"vip_daily_coin_rebate": {
			// scheduler 启动即执行，因此不能用 24h 间隔假装 UTC 0 点；每分钟触发一次，
			// wallet owner 以 UTC task_day 的 completed run 快速 no-op，并在跨日后创建新批次。
			Enabled:   true,
			Interval:  "1m",
			Timeout:   "30s",
			LockTTL:   "1m",
			BatchSize: 500,
		},
		"growth_level_reward": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "10s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
		"temporary_growth_level": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "15s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
		"achievement_reward": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "10s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
		"weekly_star_settlement": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "20s",
			LockTTL:   "1m",
			BatchSize: 50,
		},
		"activity_template_settlement": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "30s",
			LockTTL:   "1m",
			BatchSize: 50,
		},
		"agency_opening_settlement": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "20s",
			LockTTL:   "1m",
			BatchSize: 50,
		},
		"cp_weekly_rank_settlement": {
			Enabled:   true,
			Interval:  "5m",
			Timeout:   "30s",
			LockTTL:   "5m",
			BatchSize: 50,
		},
		"room_turnover_reward_settlement": {
			Enabled:   true,
			Interval:  "5m",
			Timeout:   "30s",
			LockTTL:   "5m",
			BatchSize: 100,
		},
		"cp_intimacy_leaderboard": {
			Enabled:   true,
			Interval:  "5m",
			Timeout:   "20s",
			LockTTL:   "5m",
			BatchSize: 10000,
		},
		"private_message_history": {
			// 腾讯消息文件在小时结束后异步生成；每分钟最多认领一个小时并在 user-service
			// 内按 500 条写入，既能回补最近七天，又不会形成下载或数据库突发。
			Enabled:   true,
			Interval:  "1m",
			Timeout:   "2m",
			LockTTL:   "3m",
			BatchSize: 500,
		},
		"game_level_event_relay": {
			Enabled:   true,
			Interval:  "5s",
			Timeout:   "10s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
		"host_salary_daily_settlement": {
			Enabled:   true,
			Interval:  "24h",
			Timeout:   "30s",
			LockTTL:   "2m",
			BatchSize: 200,
		},
		"host_salary_half_month_settlement": {
			Enabled:   true,
			Interval:  "24h",
			Timeout:   "30s",
			LockTTL:   "2m",
			BatchSize: 200,
		},
		"host_salary_month_end": {
			Enabled:   true,
			Interval:  "24h",
			Timeout:   "60s",
			LockTTL:   "5m",
			BatchSize: 200,
		},
		"mic_open_session_compensation": {
			Enabled:                 true,
			Interval:                "60s",
			Timeout:                 "10s",
			LockTTL:                 "60s",
			BatchSize:               100,
			PendingPublishMaxAge:    "2m",
			PublishingSessionMaxAge: "12h",
		},
		"room_open_session_compensation": {
			Enabled:   true,
			Interval:  "60s",
			Timeout:   "10s",
			LockTTL:   "60s",
			BatchSize: 200,
			// stale worker 2 分钟兜底断线离房；24h 只兜 room-service 节点崩溃或 outbox 丢失的极端场景。
			OpenSessionMaxAge: "24h",
		},
		"manager_user_block_expiry": {
			Enabled:   true,
			Interval:  "30s",
			Timeout:   "10s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
		"admin_user_ban_expiry": {
			Enabled:   true,
			Interval:  "30s",
			Timeout:   "10s",
			LockTTL:   "30s",
			BatchSize: 100,
		},
	}
}

func normalizeTasks(tasks map[string]TaskConfig) map[string]TaskConfig {
	defaults := defaultTasks()
	if tasks == nil {
		tasks = map[string]TaskConfig{}
	}
	for name, def := range defaults {
		current, ok := tasks[name]
		if !ok {
			tasks[name] = def
			continue
		}
		tasks[name] = mergeTaskConfig(def, current)
	}
	for name, task := range tasks {
		tasks[name] = mergeTaskConfig(TaskConfig{
			Interval:  "5s",
			Timeout:   "5s",
			LockTTL:   "30s",
			BatchSize: 100,
		}, task)
	}
	return tasks
}

func mergeTaskConfig(def TaskConfig, current TaskConfig) TaskConfig {
	current.Interval = strings.TrimSpace(current.Interval)
	if current.Interval == "" {
		current.Interval = def.Interval
	}
	current.Timeout = strings.TrimSpace(current.Timeout)
	if current.Timeout == "" {
		current.Timeout = def.Timeout
	}
	current.LockTTL = strings.TrimSpace(current.LockTTL)
	if current.LockTTL == "" {
		current.LockTTL = def.LockTTL
	}
	current.PendingPublishMaxAge = strings.TrimSpace(current.PendingPublishMaxAge)
	if current.PendingPublishMaxAge == "" {
		current.PendingPublishMaxAge = def.PendingPublishMaxAge
	}
	current.PublishingSessionMaxAge = strings.TrimSpace(current.PublishingSessionMaxAge)
	if current.PublishingSessionMaxAge == "" {
		current.PublishingSessionMaxAge = def.PublishingSessionMaxAge
	}
	current.OpenSessionMaxAge = strings.TrimSpace(current.OpenSessionMaxAge)
	if current.OpenSessionMaxAge == "" {
		current.OpenSessionMaxAge = def.OpenSessionMaxAge
	}
	if current.BatchSize <= 0 {
		current.BatchSize = def.BatchSize
	}
	return current
}
