package objectstorage

import (
	"context"
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	awsconfig "github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/sts"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/metrics/objectstorage/enrichment"
	crossplaneenrichment "github.com/tidbcloud/infra-provider/regional-server/pkg/metrics/objectstorage/enrichment/crossplane"
	objectstorageexporter "github.com/tidbcloud/infra-provider/regional-server/pkg/metrics/objectstorage/exporter/objectstorage"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/metrics/objectstorage/provider"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/metrics/objectstorage/provider/aws/storagelensreport"
	"k8s.io/client-go/dynamic"
)

const defaultRefreshInterval = time.Hour

type Runner struct {
	refreshers []*provider.Refresher
	collector  prometheus.Collector
}

func NewRunner(ctx context.Context, cfg config.ObjectStorageMetricsConfig, dynamicClient dynamic.Interface) (*Runner, error) {
	if len(cfg.AWSStorageLensReports) == 0 {
		return nil, nil
	}
	refreshInterval := defaultRefreshInterval
	if cfg.RefreshInterval != "" {
		parsed, err := time.ParseDuration(cfg.RefreshInterval)
		if err != nil {
			return nil, fmt.Errorf("parse object storage metrics refresh interval %q: %w", cfg.RefreshInterval, err)
		}
		refreshInterval = parsed
	}
	if refreshInterval <= 0 {
		return nil, fmt.Errorf("object storage metrics refresh interval must be positive: %s", refreshInterval)
	}

	crossplaneSource := crossplaneenrichment.NewSource(dynamicClient)
	refreshers := make([]*provider.Refresher, 0, len(cfg.AWSStorageLensReports))
	for _, report := range cfg.AWSStorageLensReports {
		awsCfg, err := loadAWSConfig(ctx, report)
		if err != nil {
			return nil, err
		}
		reportPrefix := report.ReportPrefix
		if reportPrefix == "" {
			reportPrefix = "StorageLens/"
		}
		collector := enrichment.NewCollector(storagelensreport.NewCollector(storagelensreport.Config{
			Name:         report.Name,
			Region:       report.Region,
			AccountID:    report.AccountID,
			ConfigID:     report.ConfigID,
			ReportBucket: report.ReportBucket,
			ReportPrefix: reportPrefix,
		}, s3.NewFromConfig(awsCfg)), crossplaneSource)
		refreshers = append(refreshers, provider.NewRefresher(collector, refreshInterval))
	}

	readers := make([]objectstorageexporter.SnapshotReader, 0, len(refreshers))
	for _, refresher := range refreshers {
		readers = append(readers, refresher)
	}
	return &Runner{refreshers: refreshers, collector: objectstorageexporter.NewExporter(readers)}, nil
}

func (r *Runner) Collector() prometheus.Collector {
	return r.collector
}

func (r *Runner) Start(ctx context.Context) {
	for _, refresher := range r.refreshers {
		go refresher.Run(ctx)
	}
}

func loadAWSConfig(ctx context.Context, report config.AWSStorageLensReportConfig) (aws.Config, error) {
	awsCfg, err := awsconfig.LoadDefaultConfig(ctx, awsconfig.WithRegion(report.Region))
	if err != nil {
		return aws.Config{}, err
	}
	if report.RoleARN != "" {
		stsClient := sts.NewFromConfig(awsCfg)
		awsCfg.Credentials = aws.NewCredentialsCache(stscreds.NewAssumeRoleProvider(stsClient, report.RoleARN))
	}
	return awsCfg, nil
}
