package internal

import (
	"os"

	"github.com/juju/errors"
	log "github.com/sirupsen/logrus"
	"gopkg.in/yaml.v3"

	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 DeployAzureO11yRegion(args DeployAzureRegionArgs) error {
	metrics.AddCommonLabel(metrics.LABEL_CLOUD_PROVIDER, "azure")
	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, "dedicated-region")
	logger := log.WithFields(log.Fields{
		"instance": args.InstanceName,
		"region":   args.Region,
		"name":     args.RegionName,
		"meta-uri": args.MetaURI,
	})

	logger.WithFields(log.Fields{
		"tenant-id":       args.TenantId,
		"subscription-id": args.SubscriptionId,
		"client-id":       args.ClientId,
		"job-path":        args.JobPath,
	}).Info("Deploy Azure region")

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

	account, err := types.FromAzureClientId(args.TenantId, args.SubscriptionId, args.ClientId)
	if err != nil {
		return err
	}
	cloudProvider, err := cloud_provider.NewAzureCloudProvider(oidcProvider, *account, args.Region)
	if err != nil {
		return err
	}
	logger.WithFields(log.Fields{
		"account":       account.AzureClientId,
		"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))
	cloudProvider.WriteToLocal(homeDir, tidbClusterAccounts)
	logger.WithField("home-dir", homeDir).Info("Write credential files to host")

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

	var globalOptions types.GlobalOptions
	if args.GlobalOptions != "" {
		err = yaml.Unmarshal([]byte(args.GlobalOptions), &globalOptions)
		if err != nil {
			return errors.Annotatef(err, "Failed to parse global options")
		}
	}

	if globalOptions.Env == "" {
		globalOptions.Env = args.InstanceName
	}
	if globalOptions.LogicalRegionName == "" {
		globalOptions.LogicalRegionName = args.RegionName
	}

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

	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 = getAzureO11yK8sName(region)
	region.K8s.CredentialSecretName = constants.AZURE_SECRET_NAME
	region.K8s.CredentialSecret = cloudProvider.GetAuthConfigYaml(constants.AZURE_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
		}
		if oldRegion.GlobalOptions.LogicalRegionName != region.GlobalOptions.LogicalRegionName {
			return errors.Errorf("Can't set different logical region name")
		}

		// 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.SaveAzureRegionControl(args.MetaURI, *region)
	if err2 != nil {
		logger.Error("Failed to save region meta")
		region.Print()
	} else {
		logger.Info("Saved region meta")
	}
	return err
}

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