package cluster

import (
	"context"
	"strconv"

	regionaldedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	pkgclusterproxy "github.com/tidbcloud/pkgv2/clusterproxy"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/clusterproxy"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func (s *serviceImpl) ChangeRootPasswd(ctx context.Context, clusterId uint64, passwd string) bizerr.BizError {
	err := s.transaction.Transaction(ctx, func(ctx context.Context) error {
		cluster, err := s.clusterRepo.Get(ctx, clusterId, false)
		if err != nil {
			return err
		}

		if cluster.State != globaldedicatedv1.Cluster_AVAILABLE &&
			cluster.State != globaldedicatedv1.Cluster_SCALING &&
			cluster.State != globaldedicatedv1.Cluster_MODIFYING {
			return bizerr.New(
				bizerr.ErrCodeParams,
				"root password can only be changed when the cluster is available or modifying",
			)
		}

		if err := validateClusterPassword(passwd); err != nil {
			return err
		}

		region, err := s.regionRepo.SelectRegionById(ctx, cluster.RegionId, cluster.OrganizationId)
		if err != nil {
			return err
		}
		if region.State != globalcommonv1.Region_ACTIVE {
			return bizerr.New(bizerr.ErrCodePermission, "region is not active")
		}

		getReq := &regionaldedicatedv1.GetClusterRequest{
			ClusterId:      strconv.FormatUint(cluster.ClusterId, 10),
			ShowStatusOnly: false,
		}

		regionCluster, err := s.regionServerCli.GetCluster(ctx, region, getReq)
		if err != nil {
			return err
		}
		cluster = s.mergeCluster(ctx, cluster, nil, regionCluster, nil, nil, nil, nil)

		cloudAdminPwd, err := s.encryptor.DecryptBase64(ctx, cluster.EncryptedCloudAdminPassword)
		if err != nil {
			return err
		}

		// Set cloud admin password via cluster proxy.
		proxy, err := s.clusterProxyProvider.GetClusterProxy(
			ctx,
			cluster.ClusterId,
			cluster.ProxyBaseUrl,
			cluster.Version,
			&pkgclusterproxy.ClusterOperator{
				Username:   clusterproxy.CloudAdminUser,
				AuthMethod: pkgclusterproxy.AuthMethodPassword,
				Credential: cloudAdminPwd,
			},
		)
		if err != nil {
			return err
		}
		if err := proxy.ChangePassword(ctx, clusterproxy.RootUser, passwd); err != nil {
			return err
		}

		// Root password only available for us during the creating stage.
		cluster.RootPassword = ""
		cluster.HasRootPassword = true

		if err := s.clusterRepo.Save(ctx, cluster); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return bizerr.WrapError(err, "change root password")
	}

	return nil
}

func validateClusterPassword(password string) bizerr.BizError {
	passwordLen := len(password)
	if passwordLen < minClusterPasswordLength || passwordLen > maxClusterPasswordLength {
		return bizerr.New(bizerr.ErrCodeParams, "Password must be 8-64 characters")
	}
	return nil
}
