package action

import (
	"context"
	"errors"

	"github.com/tidbcloud/fastflow/pkg/entity/run"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/protobuf/encoding/protojson"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver"
	clusterrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cluster"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/region"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/gkeheterogeneous"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

const (
	EnableGKEPolicyControlActionName                            = "enable_gke_policy_control"
	PollUntilGKEPolicyControlEnabledActionName                  = "poll_until_gke_policy_control_enabled"
	ApplyGKEHeterogeneousDeployResourcesActionName              = "apply_gke_heterogeneous_deploy_resources"
	PollUntilGKEHeterogeneousDeployReadyForGatekeeperActionName = "poll_until_gke_heterogeneous_deploy_ready_for_gatekeeper"
	CreateGKEHeterogeneousGatekeeperAssignActionName            = "create_gke_heterogeneous_gatekeeper_assign"
	DeleteGKEHeterogeneousGatekeeperAssignActionName            = "delete_gke_heterogeneous_gatekeeper_assign"
	DeleteGKEHeterogeneousDeployActionName                      = "delete_gke_heterogeneous_deploy"
)

type gkeHeterogeneousDeployActionParam struct {
	RequestBody string `json:"request_body"`
}

type ApplyGKEHeterogeneousDeployResourcesAction struct {
	clusterRepo clusterrepo.Repo
	regionRepo  region.Repo
	regionalCli regionserver.Client
}

func NewApplyGKEHeterogeneousDeployResourcesAction(
	clusterRepo clusterrepo.Repo,
	regionRepo region.Repo,
	regionalCli regionserver.Client,
) *ApplyGKEHeterogeneousDeployResourcesAction {
	return &ApplyGKEHeterogeneousDeployResourcesAction{
		clusterRepo: clusterRepo,
		regionRepo:  regionRepo,
		regionalCli: regionalCli,
	}
}

func (a *ApplyGKEHeterogeneousDeployResourcesAction) Name() string {
	return ApplyGKEHeterogeneousDeployResourcesActionName
}

func (a *ApplyGKEHeterogeneousDeployResourcesAction) ParameterNew() any {
	return &gkeHeterogeneousDeployActionParam{}
}

func (a *ApplyGKEHeterogeneousDeployResourcesAction) Run(ctx run.ExecuteContext, params any) error {
	cctx, span := workflow.GenerateContext(ctx)
	defer span.End()

	tilog.Info(cctx, "start to apply gke heterogeneous deploy resources")
	defer func() {
		tilog.Info(cctx, "end to apply gke heterogeneous deploy resources")
	}()

	req, err := parseApplyGKEHeterogeneousDeployRequest(cctx, params)
	if err != nil {
		tilog.Error(cctx, "parse apply gke heterogeneous deploy request failed", zap.Error(err))
		return err
	}

	skip, err := skipIfClusterDeleted(cctx, a.clusterRepo, req.GetClusterId())
	if err != nil {
		tilog.Error(cctx, "check cluster deleted failed", zap.Uint64("cluster_id", req.GetClusterId()), zap.Error(err))
		return err
	}
	if skip {
		return nil
	}

	if berr := gkeheterogeneous.ApplyWithNodePoolTarget(
		cctx,
		a.clusterRepo,
		a.regionRepo,
		a.regionalCli,
		req,
	); berr != nil {
		tilog.Error(cctx, "apply gke heterogeneous deploy resources failed", zap.Uint64("cluster_id", req.GetClusterId()), zap.Error(berr))
		return berr
	}
	return nil
}

func parseApplyGKEHeterogeneousDeployRequest(_ context.Context, params any) (*globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest, error) {
	actionParam, ok := params.(*gkeHeterogeneousDeployActionParam)
	if !ok {
		return nil, errors.New("expected gke heterogeneous deploy action param type")
	}
	req := &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{}
	if err := (protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal([]byte(actionParam.RequestBody), req)); err != nil {
		return nil, err
	}
	return req, nil
}

func parseDeleteGKEHeterogeneousDeployRequest(_ context.Context, params any) (*globaldedicatedv1.DeleteGKEHeterogeneousDeployRequest, error) {
	actionParam, ok := params.(*gkeHeterogeneousDeployActionParam)
	if !ok {
		return nil, errors.New("expected gke heterogeneous deploy action param type")
	}
	req := &globaldedicatedv1.DeleteGKEHeterogeneousDeployRequest{}
	if err := (protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal([]byte(actionParam.RequestBody), req)); err != nil {
		return nil, err
	}
	return req, nil
}
