package zonalshift

import (
	"context"
	"fmt"

	"github.com/mitchellh/mapstructure"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	"github.com/tidbcloud/eks-provider/controllers/workflow/action"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/dataplane"
)

var _ action.Action = &cancelZonalShift{}

type cancelZonalShift struct {
	cli            client.Client
	awsAPI         AWSAPI
	cachedProvider dataplane.EnvProviderManager
	roleCfg        roleConfig
}

type cancelZonalShiftInputs struct {
	Namespace   string   `mapstructure:"namespace" json:"namespace" validate:"required"`
	ClusterName string   `mapstructure:"clusterName" json:"clusterName" validate:"required"`
	GroupIDs    []string `mapstructure:"groupIds" json:"groupIds"`
}

func NewCancelZonalShift(cli client.Client, awsAPI AWSAPI, cachedProvider dataplane.EnvProviderManager, assumeRoleName, externalID string) action.Action {
	return &cancelZonalShift{
		cli:            cli,
		awsAPI:         awsAPI,
		cachedProvider: cachedProvider,
		roleCfg:        roleConfig{assumeRoleName: assumeRoleName, externalID: externalID},
	}
}

func (a *cancelZonalShift) Name() cloudv1.WorkflowActionName {
	return consts.WfActionCancelZonalShiftAction
}

func (a *cancelZonalShift) PreCheck(ctx context.Context, wf *cloudv1.Workflow, step cloudv1.WorkflowStep) error {
	inputs := &cancelZonalShiftInputs{}
	if err := mapstructure.Decode(step.Inputs.Object, inputs); err != nil {
		return fmt.Errorf("decode inputs: %w", err)
	}
	if err := validate.Struct(inputs); err != nil {
		return fmt.Errorf("validate inputs: %w", err)
	}
	return nil
}

func (a *cancelZonalShift) Handle(ctx context.Context, wf *cloudv1.Workflow, step cloudv1.WorkflowStep) error {
	logger := log.FromContext(ctx)
	inputs := &cancelZonalShiftInputs{}
	_ = mapstructure.Decode(step.Inputs.Object, inputs)

	cluster, err := getCluster(ctx, a.cli, inputs.Namespace, inputs.ClusterName)
	if err != nil {
		return err
	}
	if err := ensureAWSCluster(cluster); err != nil {
		return err
	}
	envProvider, err := a.cachedProvider.GetEnvProvider(ctx, cluster)
	if err != nil {
		return err
	}
	role, err := resolveAssumedRole(ctx, a.cli, cluster, envProvider, a.roleCfg)
	if err != nil {
		return cloudv1.NewWFNonRetriableError(err)
	}
	region := envProvider.GetRegion()

	nlbs, err := discoverTargetNLBs(ctx, a.awsAPI, role, region, cluster, inputs.GroupIDs)
	if err != nil {
		return err
	}

	for _, nlb := range nlbs {
		shifts, err := a.awsAPI.ListActiveZonalShifts(ctx, role, region, nlb.ARN)
		if err != nil {
			return err
		}
		if len(shifts) == 0 {
			logger.Info("no active zonal shifts found, skip cancel", "nlbARN", nlb.ARN)
			continue
		}
		for _, shift := range shifts {
			if err := a.awsAPI.CancelZonalShift(ctx, role, region, shift.ID); err != nil {
				return err
			}
			logger.Info("cancelled zonal shift", "zonalShiftId", shift.ID, "nlbARN", nlb.ARN)
		}
	}
	return nil
}
