package config

import (
	"fmt"
	"os"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/yaml"
)

const defaultGCPProjectForBigQuery = "dbaas-eng-bi"

type CacheConfig struct {
	// TTL is the time to live for cached metrics.
	TTL *metav1.Duration `json:"ttl"`
}

func (c *CacheConfig) Complete() error {
	if c.TTL == nil {
		c.TTL = &metav1.Duration{Duration: 1 * time.Hour}
	}
	return nil
}

type AWSQuotaExporterConfig struct {
	// MetricPath is the api path to expose metrics on.
	// Defaults to "/metrics/quota/aws".
	MetricPath string `json:"metricPath,omitempty"`
	// CheckQuotaInterval is the interval to check quotas by calling AWS APIs, should not be too frequent.
	// Defaults to "2h".
	CheckQuotaInterval *metav1.Duration `json:"checkQuotaInterval,omitempty"`
	// AssumedRoleName is the name of the role to assume to query quota.
	AssumedRoleName string `json:"assumedRoleName,omitempty"`
	// ExternalID is the external ID to use when assuming the role.
	// Defaults to nil.
	ExternalID *string `json:"externalID,omitempty"`
	// Cache means to enable metrics cache
	Cache *CacheConfig `json:"cache,omitempty"`
	// GCPProjectID is the gcp project where to store data with BigQuery.
	GCPProjectID string `json:"gcpProjectID,omitempty"`
	// AWSAccounts is the list of AWS accounts to query quota.
	AWSAccounts []AWSAccount `json:"awsAccounts,omitempty"`
}

type AWSAccount struct {
	// AccountID is the AWS account ID.
	AccountID string `json:"accountID,omitempty"`
}

// Complete fills in any fields not set that are required to have valid data.
func (c *AWSQuotaExporterConfig) Complete() error {
	if c.MetricPath == "" {
		c.MetricPath = "/metrics/quota/aws"
	}
	if c.CheckQuotaInterval == nil {
		c.CheckQuotaInterval = &metav1.Duration{Duration: 2 * time.Hour}
	}
	if c.AssumedRoleName == "" {
		return fmt.Errorf("assumedRoleName is required")
	}
	if c.GCPProjectID == "" {
		c.GCPProjectID = defaultGCPProjectForBigQuery
	}
	for i, acct := range c.AWSAccounts {
		if acct.AccountID == "" {
			return fmt.Errorf("awsAccounts[%d].accountID is required", i)
		}
	}

	return nil
}

type GCPQuotaExporterConfig struct {
	// MetricPath is the api path to expose metrics on.
	// Defaults to "/metrics/quota/gcp".
	MetricPath string `json:"metricPath,omitempty"`
	// Cache means to enable metrics cache
	Cache *CacheConfig `json:"cache,omitempty"`
	// GCPAccounts is the list of GCP projects to query quota.
	GCPAccounts []GCPAccount `json:"gcpAccounts,omitempty"`
}

type GCPAccount struct {
	// ProjectID is the GCP project ID.
	ProjectID string `json:"projectID,omitempty"`
}

// Complete fills in any fields not set that are required to have valid data.
func (c *GCPQuotaExporterConfig) Complete() error {
	if c.MetricPath == "" {
		c.MetricPath = "/metrics/quota/gcp"
	}
	for i, acct := range c.GCPAccounts {
		if acct.ProjectID == "" {
			return fmt.Errorf("gcpAccounts[%d].projectID is required", i)
		}
	}
	return nil
}

// ExporterConfig contains the configuration for each exporter.
// It is passed to a exporter factory to create a exporter.
type ExporterConfig struct {
	AWSQuotaExporterConfig *AWSQuotaExporterConfig `json:"awsQuota,omitempty"`
	GCPQuotaExporterConfig *GCPQuotaExporterConfig `json:"gcpQuota,omitempty"`
}

type RootConfig struct {
	ListenAddr string `json:"listenAddr,omitempty"`
	// EnabledForGCP is the flag to enable quota exporter for GCP.
	EnabledForGCP bool `json:"enabledForGCP,omitempty"`
	// EnabledForAWS is the flag to enable quota exporter for AWS.
	EnabledForAWS bool `json:"enabledForAWS,omitempty"`

	Exporters ExporterConfig `json:"exporters"`
}

func (c *RootConfig) Complete() error {
	if c.ListenAddr == "" {
		c.ListenAddr = ":8080"
	}
	return nil
}

func LoadRootConfig(path string) (*RootConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	config := &RootConfig{}
	err = yaml.Unmarshal(data, config)
	if err != nil {
		return nil, err
	}

	err = config.Complete()
	if err != nil {
		return nil, err
	}
	return config, nil
}
