package cluster

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/constant"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
)

func (svc *service) SetRootPassword(
	ctx context.Context,
	clusterId uint64,
	rootPassword string,
) (*entity.Cluster, *entity.ClusterCustomStatus, error) {
	spanName := "ClusterService.SetRootPassword"
	ctx, span := tracer.Start(ctx, spanName)
	defer span.End()

	var (
		cluster *entity.Cluster
		status  *entity.ClusterCustomStatus
	)

	if clusterId == 0 {
		return nil, nil, fmt.Errorf("cluster id is invalid")
	}

	if rootPassword == "" {
		return nil, nil, fmt.Errorf("root password is required")
	}

	if err := svc.transaction.Transaction(ctx, func(ctx context.Context) error {
		var (
			err     error
			isMutex bool
		)
		// First make sure cluster exists in database
		cluster, err = svc.clusterRepo.GetCluster(ctx, clusterId, false)
		if err != nil {
			return fmt.Errorf("failed to get cluster record: %w", err)
		}

		if cluster.DeployType != entity.DeployTypeNextGen {
			return fmt.Errorf("cluster deploy type is not nextgen, got: %s", cluster.DeployType)
		}

		// check ClusterAction_CLUSTER_ACTION_PAUSE_CLUSTER mutex
		isMutex, status, err = svc.customStatusRepo.IsMutexClusterAction(
			ctx,
			clusterId,
			tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_SET_ROOT_PASSWORD,
		)
		if err != nil {
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return fmt.Errorf("failed to check mutex cluster action: %w", err)
			}
		}

		if isMutex {
			tilog.Error(ctx, "mutex custom status is exist", zap.Uint64("cluster_id", clusterId), zap.Any("custom status", status))
			return constant.ErrClusterCustomStatusForbidden
		}

		// Update cluster record in schema.
		err = svc.clusterRepo.TouchCluster(ctx, clusterId)
		if err != nil {
			return fmt.Errorf("failed to update cluster record in database: %w", err)
		}

		_, err = svc.nextGenCli.ChangeClusterPasswd(ctx, &globalngv1.ChangeClusterPasswdRequest{
			ClusterId: clusterId,
			Passwd:    rootPassword,
		})
		if err != nil {
			return err
		}

		nextgenCluster, err := svc.nextGenCli.GetCluster(ctx, &globalngv1.GetClusterRequest{ClusterId: clusterId})
		if err != nil {
			return err
		}

		cluster = cvtNextGenCluster(cluster, nextgenCluster)

		return nil
	}); err != nil {
		return nil, status, fmt.Errorf("failed to set root password: %w", err)
	}

	return cluster, nil, nil
}
