package config

import (
	"os"
	"strconv"

	log "github.com/sirupsen/logrus"

	"github.com/tidbcloud/metering/pkg/common/constants"
	"gopkg.in/yaml.v3"
)

const defaultPremiumReservedMetaS3Path = "premium-reserved-meta"

type ServiceConfig struct {
	Port        int    `json:"port" yaml:"port"`
	MetricsPort int    `json:"metricsPort" yaml:"metricsPort"`
	Vendor      string `json:"vendor" yaml:"vendor"`
	Region      string `json:"region" yaml:"region"`
	Env         string `json:"env" yaml:"env"`
	LogLevel    string `json:"logLevel" yaml:"logLevel"`
	Bucket      string `json:"bucket" yaml:"bucket"`
	EnablePprof bool   `json:"enablePprof" yaml:"enablePprof"`

	// TrafficMeteringConfig is the configuration for traffic metering
	TrafficMeteringConfig  *TrafficMeteringConfig  `json:"trafficMetering,omitempty" yaml:"trafficMetering,omitempty"`
	InstanceMeteringConfig *InstanceMeteringConfig `json:"instanceMetering,omitempty" yaml:"instanceMetering,omitempty"`
	NextGenMeteringConfig  *NextGenMeteringConfig  `json:"nextGenMetering,omitempty" yaml:"nextGenMetering,omitempty"`
	Secret                 *Secret                 `json:"secret,omitempty" yaml:"secret,omitempty"`
}

type InstanceMeteringConfig struct {
	StartTime                     int64                        `json:"startTime" yaml:"startTime"`
	CloudProviders                []string                     `json:"cloudProviders" yaml:"cloudProviders"`
	MgmtGrpcTarget                string                       `json:"mgmtGrpcTarget" yaml:"mgmtGrpcTarget"`
	BillingReportSwitchTime       int64                        `json:"billingReportSwitchTime" yaml:"billingReportSwitchTime"`
	TenantBlockListFullSwitchTime []TenantFilter               `json:"tenantBlockListFullSwitchTime" yaml:"tenantBlockListFullSwitchTime"`
	TenantWithSwitchTimes         []TenantFilter               `json:"tenantWithSwitchTimes" yaml:"tenantWithSwitchTimes"`
	FeatureGateFilter             map[string]FeatureGateFilter `json:"featureGateFilter" yaml:"featureGateFilter"`
	CleanCacheDelaySecond         int64                        `json:"cleanCacheDelaySecond" yaml:"cleanCacheDelaySecond"`
}

func (i *InstanceMeteringConfig) GetFeatureGateFilter(name string) FeatureGateFilter {
	if i.FeatureGateFilter == nil {
		return FeatureGateFilter{}
	}
	return i.FeatureGateFilter[name]
}

type Secret struct {
	ApiGatewayKey string `json:"apiGatewayKey" yaml:"apiGatewayKey"`
}

type TrafficMeteringConfig struct {
	Provider                TrafficMeteringProvider `json:"provider" yaml:"provider"`
	RerunDays               int                     `json:"rerunDays" yaml:"rerunDays"`
	MeterInterval           int                     `json:"meterInterval" yaml:"meterInterval"`
	CachePath               string                  `json:"cachePath" yaml:"cachePath"`
	BillingReportSwitchTime int64                   `json:"billingReportSwitchTime" yaml:"billingReportSwitchTime"`
	TenantWithSwitchTimes   []TenantFilter          `json:"tenantWithSwitchTimes" yaml:"tenantWithSwitchTimes"`
}

type TrafficMeteringProvider struct {
	Azure *TrafficMeteringProviderAzureConfig `json:"azure,omitempty" yaml:"azure,omitempty"`
	AWS   *TrafficMeteringProviderAWSConfig   `json:"aws,omitempty" yaml:"aws,omitempty"`
	GCP   *TrafficMeteringProviderGCPConfig   `json:"gcp,omitempty" yaml:"gcp,omitempty"`
}

type TrafficMeteringProviderAzureConfig struct {
	BillingSchemaVersion string                     `json:"billingSchemaVersion" yaml:"billingSchemaVersion"`
	BillingProvider      BillingProviderAzureConfig `json:"billingProvider" yaml:"billingProvider"`
	StoreProvider        StoreProviderAzureConfig   `json:"storeProvider" yaml:"storeProvider"`
}

type BillingProviderAzureConfig struct {
	StorageAccountName string   `json:"storageAccountName" yaml:"storageAccountName"`
	ContainerName      string   `json:"containerName" yaml:"containerName"`
	Perfix             []string `json:"perfix" yaml:"perfix"`
}

type StoreProviderAzureConfig struct {
	StorageAccountName string `json:"storageAccountName" yaml:"storageAccountName"`
	ContainerName      string `json:"containerName" yaml:"containerName"`
	Perfix             string `json:"perfix" yaml:"perfix"`
}

type TrafficMeteringProviderAWSConfig struct {
	BillingProvider []BillingProviderAWSConfig `json:"billingProvider" yaml:"billingProvider"`
	StoreProvider   StoreProviderAWSConfig     `json:"storeProvider" yaml:"storeProvider"`
}

type BillingProviderAWSConfig struct {
	Account     string `json:"account" yaml:"account"`
	RoleArn     string `json:"roleArn" yaml:"roleArn"`
	BucketName  string `json:"bucketName" yaml:"bucketName"`
	Region      string `json:"region" yaml:"region"`
	Perfix      string `json:"perfix" yaml:"perfix"`
	SourceType  string `json:"sourceType" yaml:"sourceType"`
	ServicePlan string `json:"servicePlan,omitempty" yaml:"servicePlan,omitempty"`
}

type StoreProviderAWSConfig struct {
	Account                   string `json:"account" yaml:"account"`
	RoleArn                   string `json:"roleArn" yaml:"roleArn"`
	BucketName                string `json:"bucketName" yaml:"bucketName"`
	Region                    string `json:"region" yaml:"region"`
	Perfix                    string `json:"perfix" yaml:"perfix"`
	PremiumReservedMetaS3Path string `json:"premiumReservedMetaS3Path,omitempty" yaml:"premiumReservedMetaS3Path,omitempty"`
}

type TrafficMeteringProviderGCPConfig struct {
	BillingProvider BillingProviderGCPConfig `json:"billingProvider" yaml:"billingProvider"`
	StoreProvider   StoreProviderGCPConfig   `json:"storeProvider" yaml:"storeProvider"`
}

// BillingProviderGCPConfig is the configuration for GCP Billing Provider, which get the billing data from bigquery
type BillingProviderGCPConfig struct {
	ProjectId        string   `json:"projectId" yaml:"projectId"`
	Location         string   `json:"location" yaml:"location"`
	MeteringProjects []string `json:"meteringProjects" yaml:"meteringProjects"`
}

type StoreProviderGCPConfig struct {
	ProjectId  string `json:"projectId" yaml:"projectId"`
	BucketName string `json:"bucketName" yaml:"bucketName"`
	Perfix     string `json:"perfix" yaml:"perfix"`
}

type FeatureGateFilter struct {
	FullSwitchTime int64           `json:"fullSwitchTime" yaml:"fullSwitchTime"`
	Tenants        []TenantFilter  `json:"tenants" yaml:"tenants"`
	Projects       []ProjectFilter `json:"projects" yaml:"projects"`
	Clusters       []ClusterFilter `json:"clusters" yaml:"clusters"`
}

// IsFullSwitch returns whether the feature gate is fully switched
// if fullSwitchTime is not set, it will return false
func (f FeatureGateFilter) IsFullSwitch(nowts int64) bool {
	return f.FullSwitchTime > 0 && nowts >= f.FullSwitchTime
}

// IsTenantSwitch returns whether the tenant is switched
func (f FeatureGateFilter) IsTenantSwitch(tenantId string, nowts int64) bool {
	if f.IsFullSwitch(nowts) {
		return true
	}
	for _, tenant := range f.Tenants {
		if tenant.TenantId == tenantId && nowts >= tenant.SwitchTime {
			return true
		}
	}
	return false
}

// IsClusterGreaterThanSwitchTime returns whether the tenant is greater than tenant id
func (f FeatureGateFilter) IsTenantGreaterThanSwitchTime(tenantId string, nowts int64) bool {
	if f.IsFullSwitch(nowts) {
		return true
	}
	for _, tenant := range f.Tenants {
		tenantIdint, err := strconv.ParseInt(tenantId, 10, 64)
		if err != nil {
			log.Panicf("parse tenantId %s to int64 failed, error is %v", tenantId, err)
		}
		DestTenantIdint, err := strconv.ParseInt(tenant.TenantId, 10, 64)
		if err != nil {
			log.Panicf("parse tenantId %s to int64 failed, error is %v", tenant.TenantId, err)
		}
		if tenantIdint >= DestTenantIdint && nowts >= tenant.SwitchTime {
			log.Debugf("tenantId %s >= %s, nowts %d >= %d, switch cached", tenantId, tenant.TenantId, nowts, tenant.SwitchTime)
			return true
		}
	}
	return false
}

// IsProjectSwitch returns whether the project is switched
func (f FeatureGateFilter) IsProjectSwitch(projectId string, nowts int64) bool {
	if f.IsFullSwitch(nowts) {
		return true
	}
	for _, project := range f.Projects {
		if project.ProjectId == projectId && nowts >= project.SwitchTime {
			return true
		}
	}
	return false
}

// IsClusterSwitch returns whether the cluster is switched
func (f FeatureGateFilter) IsClusterSwitch(clusterId string, nowts int64) bool {
	if f.IsFullSwitch(nowts) {
		return true
	}
	for _, cluster := range f.Clusters {
		if cluster.ClusterId == clusterId && nowts >= cluster.SwitchTime {
			return true
		}
	}
	return false
}

type ProjectFilter struct {
	ProjectId  string `json:"projectId" yaml:"projectId"`
	SwitchTime int64  `json:"switchTime" yaml:"switchTime"`
}

type TenantFilter struct {
	TenantId   string `json:"tenantId" yaml:"tenantId"`
	SwitchTime int64  `json:"switchTime" yaml:"switchTime"`
}

type ClusterFilter struct {
	ClusterId  string `json:"clusterId" yaml:"clusterId"`
	SwitchTime int64  `json:"switchTime" yaml:"switchTime"`
}

type NextGenMeteringConfig struct {
	StartTime        int64             `json:"startTime" yaml:"startTime"`                 // Configuration start timestamp
	TaskInterval     int               `json:"taskInterval" yaml:"taskInterval"`           // Task interval (minutes)
	DelayMinutes     int               `json:"delayMinutes" yaml:"delayMinutes"`           // Metering delay (minutes, default 5 minutes)
	EnableRecompute  bool              `json:"enableRecompute" yaml:"enableRecompute"`     // Whether to enable recomputation
	RecomputeDays    int               `json:"recomputeDays" yaml:"recomputeDays"`         // Recomputation days
	DeltaTablePath   string            `json:"deltaTablePath" yaml:"deltaTablePath"`       // Delta Lake table path
	CheckpointPrefix string            `json:"checkpointPrefix" yaml:"checkpointPrefix"`   // Checkpoint prefix in storage
	MeteringPrefix   string            `json:"meteringPrefix" yaml:"meteringPrefix"`       // Metering data prefix in storage
	CachePath        string            `json:"cachePath" yaml:"cachePath"`                 // Local cache path for temporary files
	BucketEndpoint   string            `json:"bucketEndpoint" yaml:"bucketEndpoint"`       // Custom bucket endpoint (for S3-compatible storage)
	StorageOptions   map[string]string `json:"storageOptions" yaml:"storageOptions"`       // Storage options (S3/Azure etc. configuration)
	RoleArn          string            `json:"roleArn,omitempty" yaml:"roleArn,omitempty"` // Optional AWS IAM role ARN for accessing metering resources

	// Reporter
	EnableReporter   bool `json:"enableReporter" yaml:"enableReporter"`     // Whether to enable reporting functionality
	ReporterInterval int  `json:"reporterInterval" yaml:"reporterInterval"` // Reporter task interval (minutes, default 60 minutes)

	// Maintenance
	MaintenanceConfig *NextGenMaintenanceConfig `json:"maintenance,omitempty" yaml:"maintenance,omitempty"`
	//Feature gates
	FeatureGateFilter map[string]FeatureGateFilter `json:"featureGateFilter" yaml:"featureGateFilter"`

	// Plan controls the billing plan. "premium" (default) for normal mode, "byoc" for BYOC mode.
	Plan string `json:"plan" yaml:"plan"`
}

func (n *NextGenMeteringConfig) GetFeatureGateFilter(name string) FeatureGateFilter {
	if n.FeatureGateFilter == nil {
		return FeatureGateFilter{}
	}
	return n.FeatureGateFilter[name]
}

// NextGenMaintenanceConfig defines maintenance-related settings for NextGen metering data
type NextGenMaintenanceConfig struct {
	Enable                bool  `json:"enable" yaml:"enable"`                               // Whether maintenance task is enabled
	IntervalMinutes       int   `json:"intervalMinutes" yaml:"intervalMinutes"`             // Maintenance interval in minutes (default 1440 for daily)
	EnableDelete          bool  `json:"enableDelete" yaml:"enableDelete"`                   // Whether to delete outdated records
	DeleteRetentionDays   int   `json:"deleteRetentionDays" yaml:"deleteRetentionDays"`     // Retention window in days for delete operation
	DeleteBeforeTimestamp int64 `json:"deleteBeforeTimestamp" yaml:"deleteBeforeTimestamp"` // Absolute cutoff timestamp for delete (if set, takes precedence)
	EnableVacuum          bool  `json:"enableVacuum" yaml:"enableVacuum"`                   // Whether to run vacuum after maintenance
	VacuumRetentionHours  int   `json:"vacuumRetentionHours" yaml:"vacuumRetentionHours"`   // Retention period for vacuum in hours
	VacuumDryRun          bool  `json:"vacuumDryRun" yaml:"vacuumDryRun"`                   // Whether vacuum should run in dry-run mode
}

func (p ServiceConfig) IsProd() bool {
	return (p.Env == constants.EnvProd)
}

func LoadServiceConfig(path string) (ServiceConfig, error) {
	serviceConfig := ServiceConfig{}

	in, err := os.ReadFile(path)
	if err != nil {
		return serviceConfig, err
	}
	err = yaml.Unmarshal(in, &serviceConfig)
	if err != nil {
		return serviceConfig, err
	}
	if serviceConfig.TrafficMeteringConfig != nil &&
		serviceConfig.TrafficMeteringConfig.Provider.AWS != nil &&
		serviceConfig.TrafficMeteringConfig.Provider.AWS.StoreProvider.PremiumReservedMetaS3Path == "" {
		serviceConfig.TrafficMeteringConfig.Provider.AWS.StoreProvider.PremiumReservedMetaS3Path = defaultPremiumReservedMetaS3Path
	}
	return serviceConfig, nil
}
