package internal

import (
	"os"

	"github.com/juju/errors"
	log "github.com/sirupsen/logrus"

	cloud_provider "github.com/tidbcloud/auto-deploy/internal/cloud-provider"
	"github.com/tidbcloud/auto-deploy/internal/constants"
	"github.com/tidbcloud/auto-deploy/internal/meta"
	"github.com/tidbcloud/auto-deploy/internal/metrics"
	oidc_provider "github.com/tidbcloud/auto-deploy/internal/oidc-provider"
	"github.com/tidbcloud/auto-deploy/internal/types"
	"github.com/tidbcloud/auto-deploy/internal/utils"
)

func DeployO11yAwsRegion(args DeployAwsRegionArgs) error {
	metrics.AddCommonLabel(metrics.LABEL_CLOUD_PROVIDER, "aws")
	metrics.AddCommonLabel(metrics.LABEL_CLOUD_REGION, args.Region)
	metrics.AddCommonLabel(metrics.LABEL_REGION_NAME, args.RegionName)
	metrics.AddCommonLabel(metrics.LABEL_ENVIRONMENT, args.InstanceName)
	metrics.AddCommonLabel(metrics.LABEL_DEPLOY_TYPE, "o11y-region")
	logger := log.WithFields(log.Fields{
		"instance": args.InstanceName,
		"region":   args.Region,
		"name":     args.RegionName,
		"meta-uri": args.MetaURI,
	})

	logger.WithFields(log.Fields{
		"role-arn": args.RoleArn,
		"job-path": args.JobPath,
	}).Info("Deploy aws region")

	oidcProvider, err := oidc_provider.GetProvider(args.OidcProviderType)
	if err != nil {
		return err
	}
	metrics.AddCommonLabel(metrics.LABEL_DEPLOY_ID, oidcProvider.GetRunID())

	account, err := types.FromAwsArn(args.RoleArn)
	if err != nil {
		return err
	}
	cloudProvider, err := cloud_provider.NewAWSCloudProvider(oidcProvider, *account, args.Region)
	if err != nil {
		return err
	}
	logger.WithFields(log.Fields{
		"account":       account.AwsRole.AccountId,
		"oidc-provider": args.OidcProviderType,
	}).Info("Auth to cloud provider")

	homeDir, err := os.UserHomeDir()
	if err != nil {
		return errors.Annotatef(err, "Failed to get home dir")
	}

	tidbClusterAccounts := make([]types.Account, len(args.TidbClusterAccounts))
	if len(args.TidbClusterAccounts) == 0 {
		log.Info("Use control plane account as data plane account")
		tidbClusterAccounts = []types.Account{*account}
	} else {
		for i, role := range args.TidbClusterAccounts {
			acc, err := types.FromAwsArn(role)
			if err != nil {
				return err
			}
			tidbClusterAccounts[i] = *acc
			log.WithField("role-arn", acc.AwsRole.AccountId).Info("Add dataplane account")
		}
	}
	cloudProvider.WriteToLocal(homeDir, tidbClusterAccounts)
	logger.WithField("home-dir", homeDir).Info("Write credential files to host")

	oldRegion, err := meta.LoadRegionControl(args.MetaURI, args.InstanceName, args.RegionName)
	if err == nil {
		logger.Warn("Region has already deployed, try to re-deploy")
	} else if !errors.Is(err, errors.NotFound) {
		return err
	}

	if args.ImageRegistry == "" {
		args.ImageRegistry = account.AwsRole.AccountId + ".dkr.ecr.us-west-2.amazonaws.com"
	}

	region := &types.Region{
		InstanceName:        args.InstanceName,
		Name:                args.RegionName,
		Region:              args.Region,
		CloudProviderType:   cloudProvider.GetCloudProviderType(),
		OidcProvider:        oidcProvider,
		CloudProvider:       cloudProvider,
		Account:             *account,
		JobPath:             args.JobPath,
		OutputBucket:        getOutputBucket(args),
		JobOutputs:          make(map[string]types.JobOutput),
		TidbClusterAccounts: tidbClusterAccounts,
		ImageRegistry:       args.ImageRegistry,
		JobOptions:          make(map[string]any),
		LatestRunId:         oidcProvider.GetRunID(),
	}

	k8s, err := utils.RenderK8sTemplate(args.K8s, region)
	if err != nil {
		return errors.Annotatef(err, "Failed to parse k8s: %s", args.K8s)
	}
	region.K8s = *k8s
	region.K8s.Region = region
	region.K8s.Name = getO11yK8sName(region)
	region.K8s.CredentialSecretName = constants.AWS_SECRET_NAME
	region.K8s.CredentialSecret = cloudProvider.GetAuthConfigYaml(constants.AWS_SECRET_NAME, tidbClusterAccounts)

	if args.JobOptions != "" {
		jobOptions, err := utils.RenderJobOptionsTemplate(args.JobOptions, region)
		if err != nil {
			return errors.Annotatef(err, "Failed to render job additional options: %s", args.JobOptions)
		}
		region.JobOptions = jobOptions
	}

	if err = createK8s(region); err != nil {
		if !errors.Is(err, errors.AlreadyExists) || oldRegion == nil {
			return err
		}
		// update oidc issuer and public ip
		oldRegion.K8s.PublicIps = region.K8s.PublicIps
		oldRegion.K8s.OidcIssuer = region.K8s.OidcIssuer
		oldRegion.K8s.EndPoint = region.K8s.EndPoint

		// set unsaved values
		oldRegion.K8s.CredentialSecretName = region.K8s.CredentialSecretName
		oldRegion.K8s.CredentialSecret = region.K8s.CredentialSecret
		oldRegion.K8s.Region = region

		region.K8s = oldRegion.K8s
		metrics.AddCommonLabel(metrics.LABEL_DEPOLY_METHOD, "re-deploy")
	} else {
		metrics.AddCommonLabel(metrics.LABEL_DEPOLY_METHOD, "new-deploy")
	}

	err = deployRegion(region, args.ForceOverwriteResources)
	err2 := meta.SaveRegionControl(args.MetaURI, *region)
	if err2 != nil {
		logger.Error("Failed to save region meta")
		region.Print()
	} else {
		logger.Info("Saved region meta")
	}

	return err
}

func getO11yK8sName(region *types.Region) string {
	return constants.REGION_O11Y_K8S_PREFIX + "-" + region.InstanceName + "-" + constants.REGION_O11Y_REGION_PREFIX + "-" + region.Name
}
