package awsquota

import (
	"context"
	"strconv"
	"time"

	"cloud.google.com/go/bigquery"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/servicequotas"
	"github.com/aws/aws-sdk-go-v2/service/support"
	"github.com/go-logr/logr"

	cloudaws "github.com/tidbcloud/infra-provider/pkg/cloud/aws"
)

type Quota struct {
	ResourceID string
	Region     string
	Service    string
	Resource   string
	Limit      float64
	Usage      float64
}

type QuotaClient interface {
	QueryQuotas(context.Context) ([]Quota, error)
}

var _ QuotaClient = &quotaClient{}

type quotaClient struct {
	logger logr.Logger

	account     string
	support     *support.Client
	svcQuota    *servicequotas.Client
	s3Client    *s3.Client
	bigQueryCli *bigquery.Client
}

func newQuotaClient(logger logr.Logger, clientSet cloudaws.Client, assumedRole *cloudaws.AssumedRole, account, gcpProjectId string) QuotaClient {
	cli, err := bigquery.NewClient(context.Background(), gcpProjectId)
	if err != nil {
		panic(err)
	}
	return &quotaClient{
		account:     account,
		logger:      logger,
		support:     clientSet.Support(assumedRole, cloudaws.WithRegion("us-east-1")),
		s3Client:    clientSet.S3(assumedRole, cloudaws.WithRegion("us-east-1")),
		svcQuota:    clientSet.ServiceQuotas(assumedRole),
		bigQueryCli: cli,
	}
}

func (c *quotaClient) QueryQuotas(ctx context.Context) ([]Quota, error) {
	startAt := time.Now()
	var quotas []Quota
	quotas = append(quotas, c.queryQuotasFromTrustedAdvisor(ctx)...)
	quotas = append(quotas, c.queryExtraQuotas(ctx)...)
	c.logger.Info("got all quotas for an account", "cost", time.Since(startAt))
	return quotas, nil
}

func (c *quotaClient) queryQuotasFromTrustedAdvisor(ctx context.Context) []Quota {
	now := time.Now()
	var quotas []Quota
	for _, checkID := range checkIDs {
		c.logger.Info("refreshing a check", "checkID", checkID)

		// Refresh the check to get the latest results for the aws account.
		// Ref: https://docs.aws.amazon.com/awssupport/latest/user/get-started-with-aws-trusted-advisor.html#refresh-check-results
		if _, err := c.support.RefreshTrustedAdvisorCheck(ctx, &support.RefreshTrustedAdvisorCheckInput{
			CheckId: aws.String(checkID),
		}); err != nil {
			c.logger.Error(err, "failed to refresh a check", "checkID", checkID)
			continue
		}

		output, err := c.support.DescribeTrustedAdvisorCheckResult(ctx, &support.DescribeTrustedAdvisorCheckResultInput{
			CheckId: aws.String(checkID),
		})
		if err != nil {
			c.logger.Error(err, "failed to describe a check result", "checkID", checkID)
			continue
		}
		for _, resource := range output.Result.FlaggedResources {
			limit, _ := strconv.ParseFloat(aws.ToString(resource.Metadata[3]), 64)
			usage, _ := strconv.ParseFloat(aws.ToString(resource.Metadata[4]), 64)
			quota := Quota{
				ResourceID: aws.ToString(resource.ResourceId),
				Region:     aws.ToString(resource.Metadata[0]),
				Service:    aws.ToString(resource.Metadata[1]),
				Resource:   aws.ToString(resource.Metadata[2]),
				Limit:      limit,
				Usage:      usage,
			}
			quotas = append(quotas, quota)
		}
	}
	c.logger.Info("queried quotas from Trusted Advisor", "cost", time.Since(now))
	return quotas
}

func (c *quotaClient) queryExtraQuotas(ctx context.Context) []Quota {
	now := time.Now()
	var quotas []Quota
	quotas = append(quotas, c.queryEBSQuotas(ctx)...)
	quotas = append(quotas, c.queryS3Quotas(ctx)...)
	c.logger.Info("queried ebs and s3 quotas", "cost", time.Since(now))
	return quotas
}
