package config

import (
	"os"

	log "github.com/sirupsen/logrus"
	"github.com/tidbcloud/observability/o11ymetering/pkg/common/constants"
	"gopkg.in/yaml.v3"
	"gorm.io/gorm"
)

var meteringConfig *MeteringConfig

type MeteringConfig struct {
	Auth0                      Auth0Config               `yaml:"auth0" json:"auth0" form:"auth0"`
	O11yRegionalDataServiceURL string                    `yaml:"o11yRegionalDataServiceUrl" json:"o11yRegionalDataServiceUrl"`
	Databases                  []DBConfig                `yaml:"databases" json:"databases" form:"databases"`
	PeriodConfig               PeriodConfig              `yaml:"periodConfig" json:"periodConfig" form:"periodConfig"`
	Tasks                      []TaskConfig              `yaml:"tasks" json:"tasks" form:"tasks"`
	CategoryShareProportions   []CategoryShareProportion `yaml:"categoryShareProportions" json:"categoryShareProportions" form:"categoryShareProportions"`
	ScheduleConfig             ScheduleConfig            `yaml:"scheduleConfig" json:"scheduleConfig" form:"scheduleConfig"`
}

type Auth0Config struct {
	GrantType    string `yaml:"grantType" json:"grant_type" form:"grant_type"`
	ClientID     string `yaml:"clientID" json:"client_id" form:"client_id"`
	ClientSecret string `yaml:"clientSecret" json:"client_secret" form:"client_secret"`
	Audience     string `yaml:"audience" json:"audience" form:"audience"`
}

type DBConfig struct {
	Type       string      `json:"type" yaml:"type"`
	Dsn        string      `json:"dsn" yaml:"dsn"`
	Use        string      `json:"use" yaml:"use"`
	GormConfig gorm.Config `json:"gormConfig" yaml:"gormConfig"`
}

type PeriodConfig struct {
	FirstPeriod string `json:"firstPeriod" yaml:"firstPeriod"`
}

type CategoryShareProportion struct {
	CloudService constants.CloudService `json:"cloudService" yaml:"cloudService"`
	Category     constants.Category     `json:"category" yaml:"category"`
	Proportion   float64                `json:"proportion" yaml:"proportion"`
}

type TaskConfig struct {
	StatisticClass constants.StatisticClass `json:"statisticClass" yaml:"statisticClass"`
	MeteringGroup  constants.MeteringGroup  `json:"meteringGroup" yaml:"meteringGroup"`
	Env            constants.BizEnv         `json:"env" yaml:"env"`
}

type ScheduleConfig struct {
	GenerateStatisticTaskIntervalSeconds int `json:"generateStatisticTaskIntervalSeconds" yaml:"generateStatisticTaskIntervalSeconds"`
	ExecuteStatisticTaskIntervalSeconds  int `json:"executeStatisticTaskIntervalSeconds" yaml:"executeStatisticTaskIntervalSeconds"`

	MergeStatisticIntervalHours int  `json:"mergeStatisticIntervalHours" yaml:"mergeStatisticIntervalHours"`
	MergeStatisticImmediately   bool `json:"mergeStatisticImmediately" yaml:"mergeStatisticImmediately"`

	GenerateStatisticReportIntervalHours int  `json:"generateStatisticReportIntervalHours" yaml:"generateStatisticReportIntervalHours"`
	GenerateStatisticReportImmediately   bool `json:"generateStatisticReportImmediately" yaml:"generateStatisticReportImmediately"`

	ClearStatisticDataIntervalHours int `json:"clearStatisticDataIntervalHours" yaml:"clearStatisticDataIntervalHours"`

	MonthlyProportion        bool `json:"monthlyProportion" yaml:"monthlyProportion"`
	RenewMonthlyProportionTs int  `json:"renewMonthlyProportionTs" yaml:"renewMonthlyProportionTs"`
}

func GetDatabases() []DBConfig {
	if meteringConfig == nil {
		panic("metering config is empty")
	}
	return meteringConfig.Databases
}

func GetAuth0Config() Auth0Config {
	if meteringConfig == nil {
		panic("metering config is empty")
	}
	return meteringConfig.Auth0
}

func GetPeriodConfig() PeriodConfig {
	if meteringConfig == nil {
		panic("metering config is empty")
	}
	return meteringConfig.PeriodConfig
}

func GetTasks() []TaskConfig {
	if meteringConfig == nil {
		panic("metering config is empty")
	}
	return meteringConfig.Tasks
}

func GetCategoryShareProportion(category constants.Category, cloudService constants.CloudService) float64 {
	if meteringConfig == nil {
		panic("metering config is empty")
	}

	for _, p := range meteringConfig.CategoryShareProportions {
		if category == p.Category && cloudService == p.CloudService {
			return p.Proportion
		}
	}
	log.Warnf("undifined CategoryShareProportion, category = %s, cloud service = %s", category, cloudService)
	return 0
}

func GetScheduleConfig() ScheduleConfig {
	if meteringConfig == nil {
		panic("metering config is empty")
	}

	return meteringConfig.ScheduleConfig
}

func LoadMeteringConfig(path string) {
	meteringConfig = &MeteringConfig{}
	in, err := os.ReadFile(path)
	if err != nil {
		panic(err)
	}
	err = yaml.Unmarshal(in, meteringConfig)
	if err != nil {
		panic(err)
	}
}

func GetMeteringConfig() *MeteringConfig {
	if meteringConfig == nil {
		panic("metering config is empty")
	}
	return meteringConfig
}

func UpdateMeteringConfigSecret() {
	config := GetMeteringConfig()
	// Update the secret in the config
	if sec := os.Getenv("AUTH0_CLIENT_SECRET"); sec != "" {
		config.Auth0.ClientSecret = sec
	}
	if len(config.Databases) == 1 {
		if sec := os.Getenv("DATABASE_DSN"); sec != "" {
			config.Databases[0].Dsn = os.Getenv("DATABASE_DSN")
		}
	}
	log.Infof("update config success")
}
