package action

import (
	"fmt"

	dplanemgrv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
	"github.com/tidbcloud/cluster-service-ng/domain/keyspacerole"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

const DeleteKeyspaceRoleActionName = "delete-keyspace-role"

type DeleteKeyspaceRoleAction struct {
	sharedPoolRepo  repo.SharedPoolRepo
	tidbClusterRepo repo.TiDBClusterRepo
	client          dplanemgrv1.KeyspaceRoleServiceClient
}

func NewDeleteKeyspaceRoleAction(
	sharedPoolRepo repo.SharedPoolRepo,
	tidbClusterRepo repo.TiDBClusterRepo,
	client dplanemgrv1.KeyspaceRoleServiceClient,
) actions.Action {
	return &DeleteKeyspaceRoleAction{
		sharedPoolRepo:  sharedPoolRepo,
		tidbClusterRepo: tidbClusterRepo,
		client:          client,
	}
}

func (a *DeleteKeyspaceRoleAction) Name() string {
	return DeleteKeyspaceRoleActionName
}

func (a *DeleteKeyspaceRoleAction) Invoke(args actions.ActionArgs) error {
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "delete keyspace role action invoked")

	sharedPoolID, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		tilog.Error(ctx, "failed to get sharedPool id", zap.Error(err))
		return fmt.Errorf("failed to get sharedPool id: %w", err)
	}

	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolID, false)
	if err != nil {
		tilog.Error(ctx, "failed to load sharedPool", zap.String("sharedPoolId", sharedPoolID), zap.Error(err))
		return fmt.Errorf("failed to load sharedPool: %w", err)
	}

	if keyspacerole.ShouldSkip(sharedPool) {
		tilog.Info(ctx, "skip delete keyspace role for shared pool",
			zap.String("sharedPoolId", sharedPoolID),
			zap.Stringer("capacityMode", sharedPool.GetCapacityMode()),
			zap.Stringer("provider", sharedPool.GetProvider().GetType()),
		)
		return nil
	}

	tidbClusterID, err := args.GetString(workflow.TiDBClusterIdKey)
	if err != nil {
		tilog.Error(ctx, "failed to get tidb cluster id", zap.Error(err))
		return fmt.Errorf("failed to get tidb cluster id: %w", err)
	}

	tidbCluster, err := a.tidbClusterRepo.Load(ctx, tidbClusterID, true)
	if err != nil {
		tilog.Error(ctx, "failed to load tidb cluster", zap.String("tidbClusterId", tidbClusterID), zap.Error(err))
		return fmt.Errorf("failed to load tidb cluster: %w", err)
	}

	if keyspacerole.ShouldSkipForCluster(sharedPool, tidbCluster) {
		tilog.Info(ctx, "skip delete keyspace role for shared pool",
			zap.String("sharedPoolId", sharedPoolID),
			zap.Stringer("capacityMode", sharedPool.GetCapacityMode()),
			zap.Stringer("provider", sharedPool.GetProvider().GetType()),
		)
		return nil
	}

	dataplaneID, err := args.GetString(workflow.DataplaneIdKey)
	if err != nil {
		tilog.Warn(ctx, "failed to get dataplane id from action args, fallback to sharedPool", zap.Error(err))
	}

	req, err := keyspacerole.BuildDeleteRequest(sharedPool, tidbCluster, dataplaneID)
	if err != nil {
		return fmt.Errorf("failed to build delete keyspace role request: %w", err)
	}

	if _, err := a.client.DeleteKeyspaceRole(ctx, req); err != nil {
		if status.Code(err) == codes.NotFound {
			tilog.Info(ctx, "keyspace role not found",
				zap.String("dataplaneId", req.GetDataplaneId()),
				zap.String("keyspaceName", req.GetKeyspaceName()),
			)
			return nil
		}
		tilog.Error(ctx, "failed to delete keyspace role",
			zap.String("dataplaneId", req.GetDataplaneId()),
			zap.String("keyspaceName", req.GetKeyspaceName()),
			zap.Error(err),
		)
		return fmt.Errorf("failed to delete keyspace role: %w", err)
	}

	tilog.Info(ctx, "delete keyspace role successfully",
		zap.String("dataplaneId", req.GetDataplaneId()),
		zap.String("keyspaceName", req.GetKeyspaceName()),
	)
	return nil
}
