package config

import (
	"bytes"
	"fmt"
	"os"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/dto"

	"github.com/spf13/viper"
)

var AppConfig Config
var OrgsConfig dto.Orgs

// LoadConfig loads configuration from file
func LoadConfig() error {
	// clinic data service prod
	viper.SetConfigName("config")
	// ec2 env
	//viper.SetConfigName(getConfigName())

	viper.SetConfigType("yaml")
	viper.AddConfigPath("./configs") // path to look for the config file in

	if err := viper.ReadInConfig(); err != nil {
		return err
	}
	if err := readConfigFileWithEnvExpansion(viper.GetViper(), viper.ConfigFileUsed()); err != nil {
		return err
	}

	viper.AutomaticEnv() // 👈 启用环境变量
	viper.SetEnvPrefix("CONFIG")
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	if err := viper.Unmarshal(&AppConfig); err != nil {
		return err
	}

	// Load organization configuration
	if err := LoadOrgsConfig(); err != nil {
		return fmt.Errorf("failed to load orgs config: %w", err)
	}

	return nil
}

// LoadOrgsConfig loads organization configuration from cluster.yaml
func LoadOrgsConfig() error {
	v := viper.New()
	v.SetConfigName("cluster")
	v.SetConfigType("yaml")
	v.AddConfigPath("./configs")

	if err := v.ReadInConfig(); err != nil {
		return fmt.Errorf("failed to read cluster config: %w", err)
	}

	v.AutomaticEnv() // 👈 启用环境变量
	v.SetEnvPrefix("CLUSTER")
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	if err := v.Unmarshal(&OrgsConfig); err != nil {
		return fmt.Errorf("failed to unmarshal cluster config: %w", err)
	}

	return nil
}

// LoadConfigFromFile loads configuration from a specific file path
func LoadConfigFromFile(configPath string) (*Config, error) {
	v := viper.New()
	v.SetConfigFile(configPath)
	v.SetConfigType("yaml")
	if err := readConfigFileWithEnvExpansion(v, configPath); err != nil {
		return nil, err
	}

	var config Config
	if err := v.Unmarshal(&config); err != nil {
		return nil, err
	}

	return &config, nil
}

func readConfigFileWithEnvExpansion(v *viper.Viper, configPath string) error {
	data, err := os.ReadFile(configPath)
	if err != nil {
		return err
	}
	return v.ReadConfig(bytes.NewBufferString(os.ExpandEnv(string(data))))
}

// ProcessSQLTemplate processes SQL template with configuration values
func (c *Config) ProcessSQLTemplate(sqlTemplate string) (string, error) {
	tmpl, err := template.New("sql").Parse(sqlTemplate)
	if err != nil {
		return "", err
	}

	var buf bytes.Buffer
	err = tmpl.Execute(&buf, c)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}

// ProcessSQLTemplateGlobal processes SQL template with global AppConfig
func ProcessSQLTemplateGlobal(sqlTemplate string) (string, error) {
	return AppConfig.ProcessSQLTemplate(sqlTemplate)
}

// ProcessSQLTemplateWithPattern processes SQL template with a specific pattern name
func ProcessSQLTemplateWithPattern(sqlTemplate string, patternName string) (string, error) {
	tmpl, err := template.New("sql").Parse(sqlTemplate)
	if err != nil {
		return "", err
	}

	data := struct {
		*Config
		PatternName string
	}{
		Config:      &AppConfig,
		PatternName: patternName,
	}

	var buf bytes.Buffer
	err = tmpl.Execute(&buf, data)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}

// GetOrgByID Get organization configuration by organization ID
func GetOrgByID(orgID string) *dto.Org {
	for _, org := range OrgsConfig.Orgs {
		if org.OrgID == orgID {
			return &org
		}
	}
	return nil
}

// GetClusterByID Get cluster configuration by cluster ID
func GetClusterByID(clusterID string) *dto.Cluster {
	for _, org := range OrgsConfig.Orgs {
		for _, cluster := range org.Clusters {
			if cluster.ClusterID == clusterID {
				return &cluster
			}
		}
	}
	return nil
}

// wlt @todo GetOrgByClusterID Get organization configuration by cluster ID
func GetOrgByClusterID(clusterID string) *dto.Org {
	for _, org := range OrgsConfig.Orgs {
		for _, cluster := range org.Clusters {
			if cluster.ClusterID == clusterID {
				return &org
			}
		}
	}
	return nil
}

// // GetAllClusters Get all cluster configurations
// func GetAllClusters() []dto.Cluster {
// 	var clusters []dto.Cluster
// 	for _, org := range OrgsConfig.Orgs {
// 		clusters = append(clusters, org.Clusters...)
// 	}
// 	return clusters
// }

// GetDynamicTimeFrom Calculate UTC 00:00:00 timestamp for the day based on tableName
func GetDynamicTimeFrom(dayStr string) string {
	if len(dayStr) != 8 {

		panic("dayStr format error:" + dayStr)
	}

	// Parse date format YYYYMMDD
	year, err1 := strconv.Atoi(dayStr[0:4])
	month, err2 := strconv.Atoi(dayStr[4:6])
	day, err3 := strconv.Atoi(dayStr[6:8])

	if err1 != nil || err2 != nil || err3 != nil {
		panic("dayStr format error:" + dayStr)
	}

	// Create UTC time 00:00:00
	startTime := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
	return fmt.Sprintf("%d", startTime.Unix())
}

// GetDynamicTimeTo2 Calculate UTC 23:59:59 timestamp for the day based on tableName
func GetDynamicTimeTo2(dayStr string) string {
	if len(dayStr) != 8 {
		panic("dayStr format error:" + dayStr)
	}

	// Parse date format YYYYMMDD
	year, err1 := strconv.Atoi(dayStr[0:4])
	month, err2 := strconv.Atoi(dayStr[4:6])
	day, err3 := strconv.Atoi(dayStr[6:8])

	if err1 != nil || err2 != nil || err3 != nil {
		panic("dayStr format error:" + dayStr)
	}

	// Create UTC time 23:59:59
	endTime := time.Date(year, time.Month(month), day, 23, 59, 59, 0, time.UTC)
	return fmt.Sprintf("%d", endTime.Unix())
}

func GetDbPath(orgId string) string {
	day := AppConfig.Day
	dbDir := fmt.Sprintf("%s/%s/%s", AppConfig.Database.DbPath, day, orgId)
	if err := os.MkdirAll(dbDir, 0755); err != nil {
		return ""
	}
	return fmt.Sprintf("%s/%s.db", dbDir, day)
}
func GetTikvDbPath(orgId string) string {
	day := AppConfig.Day
	dbDir := fmt.Sprintf("%s/%s/%s", AppConfig.Database.TikvDbPath, day, orgId)
	if err := os.MkdirAll(dbDir, 0755); err != nil {
		return ""
	}
	return fmt.Sprintf("%s/%s.db", dbDir, day)
}

func GetOrgDbDir(orgId string, day string) string {
	dbDir := fmt.Sprintf("%s/%s/%s", AppConfig.Database.DbPath, day, orgId)
	return dbDir
}

func GetTikvOrgDbDir(orgId string, day string) string {
	dbDir := fmt.Sprintf("%s/%s/%s", AppConfig.Database.TikvDbPath, day, orgId)
	return dbDir
}

func GetOrgS3FileDir(orgId string) string {
	day := AppConfig.Day
	localDir := fmt.Sprintf("%s/%s/%s", AppConfig.InputDir, day, orgId)
	return localDir
}
func GetTikvOrgS3FileDir(orgId string) string {
	day := AppConfig.Day
	// day := time.Now().Format("20060102")
	localDir := fmt.Sprintf("%s/%s/%s", AppConfig.TikvInputDir, day, orgId)
	return localDir
}
