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"
)

type DeployAliCloudRegionArgs struct {
	OidcProviderType        types.OidcProviderType
	InstanceName            string
	JobPath                 string
	RegionName              string
	RoleArn                 string
	Region                  string
	K8s                     string
	OutputBucket            string
	TerraformStateBucket    string
	TidbClusterAccounts     []string
	ImageRegistry           string
	JobOptions              string
	GlobalOptions           string
	ForceOverwriteResources bool
	MetaURI                 string
	DeployType              string
	ServicePlan             string
}

func DeployAliCloudRegion(args DeployAliCloudRegionArgs) error {
	metrics.AddCommonLabel(metrics.LABEL_CLOUD_PROVIDER, string(types.AliCloud))
	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, args.DeployType)
	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 alicloud region")

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

	account, err := types.FromAliCloudArn(args.RoleArn)
	if err != nil {
		return err
	}
	cloudProvider, err := cloud_provider.NewAliCloudProvider(oidcProvider, *account, args.Region)
	if err != nil {
		return err
	}
	logger.WithFields(log.Fields{
		"account":       account.AliCloudRole.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.FromAliCloudArn(role)
			if err != nil {
				return err
			}
			tidbClusterAccounts[i] = *acc
			log.WithField("role-arn", acc.AliCloudRole).Info("Add dataplane account")
		}
	}
	cloudProvider.WriteToLocal(homeDir, tidbClusterAccounts)
	logger.WithField("home-dir", homeDir).Info("Write credential files to host")

	oldRegion, err := meta.LoadAliCloudRegionControl(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.OutputBucket,
		TerraformStateBucket: args.TerraformStateBucket,
		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 = getAliCloudK8sName(region)
	region.K8s.CredentialSecretName = constants.ALICLOUD_SECRET_NAME
	region.K8s.CredentialSecret = cloudProvider.GetAuthConfigYaml(constants.ALICLOUD_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) {
			return err
		}

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

	// if err == nil {
	// 	authKey := os.Getenv(constants.GLOBAL_AUTH_KEY_ENV)
	// 	client := global_client.NewGlobalClient(globalOptions.Env, authKey, global_client.DEFAULT_TIMEOUT, log.IsLevelEnabled(log.DebugLevel))
	// 	serverAddress := ""
	// 	sharedBucketUrl := ""
	// 	switch args.DeployType {
	// 	case global_client.DEPLOY_TYPE_NEXTGEN:
	// 		dnsName, _ := region.GetJobOption("dataplaneManager.dnsName")
	// 		serverAddress = "https://" + dnsName.(string)
	// 		err = client.NewRegionalConfigCenter(region.GlobalOptions.LogicalRegionName, serverAddress, args.ServicePlan == "BYOC")
	// 		if err != nil {
	// 			logger.WithError(err).Error("Failed to register regional config center")
	// 		}
	// 	default:
	// 		return errors.Errorf("Unknown deploy type: %s", args.DeployType)
	// 	}
	//  _, err = client.CreateOrUpdateRegion(args.DeployType, region.Region, region.GlobalOptions.LogicalRegionName, serverAddress, args.ServicePlan, sharedBucketUrl, auditLogSetting, globalOptions.SortPriority, globalOptions.OpenedTenantIds, &globalOptions.Public, region.CloudProviderType)

	// }
	return err
}

func getAliCloudK8sName(region *types.Region) string {
	return region.InstanceName + "-" + region.Name + "-" + constants.ALICLOUD_REGION_K8S_SUFFIX
}
