package regionaction

import (
	"context"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/google/uuid"
	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/infra/clients/fadcli"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
)

var (
	ActionDeployNewBYOCRegion opent.ActionMetadata = opent.ActionMetadata{
		Name: "DeployNewBYOCRegion",
		Type: opent.ActionTypeAuto,
	}
)

type deployNewBYOCRegion struct {
	actions.BaseAction
	comps *actions.Components
}

func NewDeployNewBYOCRegion(comps *actions.Components) actions.AutoAction {
	return &deployNewBYOCRegion{comps: comps}
}

func (a *deployNewBYOCRegion) Metadata() opent.ActionMetadata {
	return ActionDeployNewBYOCRegion
}

func (a *deployNewBYOCRegion) PreCheck(ctx context.Context, instance *opent.Instance) error {
	return nil
}

func (a *deployNewBYOCRegion) Start(ctx context.Context, instance *opent.Instance) (*actions.ActionResult, error) {
	req, err := a.getParam(instance)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	err = a.comps.FadCli.DeployNewBYOCRegion(req)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	return actions.SucceededActionResult(nil), nil // return succeeded directly
}

func (a *deployNewBYOCRegion) Result(ctx context.Context, instance *opent.Instance) (*actions.ActionResult, error) {
	return actions.SucceededActionResult(nil), nil
}

func (a *deployNewBYOCRegion) getParam(instance *opent.Instance) (*fadcli.DeployBYOCRegionReq, error) {
	paramMap := make(map[opent.ParamName]string)
	paramList := []opent.ParamName{
		opent.ParamTiDBCloudInstance,
		opent.ParamProvider,
		opent.ParamRegionName,
		opent.ParamLogicalRegionName,
		opent.ParamO11YRegionName,
		opent.ParamMAccount,
		opent.ParamDAccount,
		opent.ParamO11YAccount,
		opent.ParamTenantID,
		opent.ParamTidbHostedZoneName,
		opent.ParamO11yHostedZoneName,
		opent.ParamRdsName,
		opent.ParamCreateRds,
		opent.ParamAvailabilityZones,
		opent.ParamECR,
		opent.ParamPCAArn,
	}
	for _, param := range paramList {
		paramMap[param], _ = instance.GetParam(param)
		// dont need to check missing param here, because it will be checked in action validation
	}

	traceID := uuid.New().String()
	if instance.ApprovalID != nil {
		traceID = fmt.Sprintf("%x", md5.Sum([]byte(strconv.Itoa(int(*instance.ApprovalID)))))
	}

	advanceOptions := map[string]string{
		"pca_arn":          paramMap[opent.ParamPCAArn],
		"tidb_hosted_zone": paramMap[opent.ParamTidbHostedZoneName],
		"o11y_hosted_zone": paramMap[opent.ParamO11yHostedZoneName],
		"tenant_id":        paramMap[opent.ParamTenantID],
		"ecr":              paramMap[opent.ParamECR],
	}
	bytes, _ := json.Marshal(advanceOptions)

	return &fadcli.DeployBYOCRegionReq{
		TiDBCloudInstance: paramMap[opent.ParamTiDBCloudInstance],
		Provider:          paramMap[opent.ParamProvider],
		RegionName:        paramMap[opent.ParamRegionName],
		LogicalRegionName: paramMap[opent.ParamLogicalRegionName],
		O11YRegionName:    paramMap[opent.ParamO11YRegionName],
		Trigger:           instance.Applicant,
		MAccount:          paramMap[opent.ParamMAccount],
		DAccounts:         paramMap[opent.ParamDAccount],
		O11YAccount:       paramMap[opent.ParamO11YAccount],
		TraceID:           traceID,
		RdsName:           paramMap[opent.ParamRdsName],
		CreateRds:         paramMap[opent.ParamCreateRds],
		AvailabilityZones: paramMap[opent.ParamAvailabilityZones],
		AdvanceOptions:    string(bytes),
	}, nil
}
