package common

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/samber/lo"
	clusterctlpb "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/constants"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/approute/task"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

type UpdateSecondarySubnets struct {
	*task.BaseTask
}

func NewUpdateSecondarySubnets(ctx *task.Context) *UpdateSecondarySubnets {
	return &UpdateSecondarySubnets{BaseTask: &task.BaseTask{Context: ctx}}
}

func (t *UpdateSecondarySubnets) Name() string {
	return "UpdateSecondarySubnets"
}

func (t *UpdateSecondarySubnets) Create(ctx context.Context) deploy.Result {
	// only support AWS for now
	if t.APPBox.Spec.Provider != string(constants.AWS) && t.APPBox.Spec.Provider != consts.ProviderTypeEKS {
		return deploy.Done()
	}
	if t.APPRoute.Annotations == nil {
		t.APPRoute.Annotations = make(map[string]string)
	}
	vpc := new(v1alpha1.VPC)
	{
		if err := t.VPCR.Get(ctx, client.ObjectKey{Namespace: t.APPBox.Spec.VPCRef.Namespace, Name: t.APPBox.Spec.VPCRef.Name}, vpc); err != nil {
			t.Logger.Error(err, "failed to get VPC", "key", client.ObjectKey{Namespace: t.APPBox.Spec.VPCRef.Namespace, Name: t.APPBox.Spec.VPCRef.Name})
			return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
		}
	}
	allSecondarySubnets := make([]*clusterctlpb.Kubernetes_AwsSubnet, 0)
	for key, value := range t.APPRoute.Annotations {
		if !strings.HasPrefix(key, v1alpha1.SecondaryCidrNamePrefix) {
			continue
		}
		if value == v1alpha1.SecondaryCidrStatusFailed {
			t.Logger.Error(fmt.Errorf("secondary cidr already failed: %s", key), "secondaryCidr", key)
			continue
		}
		secondaryCidr := utils.RecoverSanitizedCidr(strings.TrimPrefix(key, v1alpha1.SecondaryCidrNamePrefix))
		if vpc.Status.SecondaryNodeSubnets == nil {
			t.Logger.Error(fmt.Errorf("secondary node subnets not found"), "secondaryCidr", secondaryCidr)
			return deploy.Panic(fmt.Errorf("secondary node subnets not found")).WithRequeuAfter(30 * time.Second)
		}
		secondaryNodeSubnets := lo.Filter(vpc.Status.SecondaryNodeSubnets, func(subnet v1alpha1.Subnet, _ int) bool {
			contains, err := utils.CidrContains(secondaryCidr, subnet.CIDRBlock)
			if err != nil {
				t.Logger.Error(err, "failed to check if secondary cidr contains subnet cidr", "secondaryCidr", secondaryCidr, "subnetCidr", subnet.CIDRBlock)
				return false
			}
			return contains
		})
		if len(secondaryNodeSubnets) != 3 {
			t.Logger.Error(fmt.Errorf("expected 3 secondary node subnets, got %d", len(secondaryNodeSubnets)), "secondaryCidr", secondaryCidr)
			return deploy.Panic(fmt.Errorf("expected 3 secondary node subnets, got %d", len(secondaryNodeSubnets))).WithRequeuAfter(30 * time.Second)
		}
		// just update the subnets that are ready
		for _, subnet := range secondaryNodeSubnets {
			allSecondarySubnets = append(allSecondarySubnets, &clusterctlpb.Kubernetes_AwsSubnet{
				Id:     subnet.SubnetID,
				AzId:   subnet.AvailabilityZoneID,
				AzName: subnet.AvailabilityZone,
			})
		}
		t.APPRoute.Annotations[key] = v1alpha1.SecondaryCidrStatusReady
	}

	if len(allSecondarySubnets) == 0 {
		return deploy.Done()
	}
	if _, err := t.ClusterCli.UpdateClusterAWSCloud(ctx, &clusterctlpb.UpdateClusterAWSCloudRequest{
		ClusterId:            t.APPRoute.Name,
		SecondaryNodeSubnets: allSecondarySubnets,
	}); err != nil {
		t.Logger.Error(err, "failed to update cluster aws cloud", "clusterId", t.APPRoute.Name)
		return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
	}

	return deploy.Done()
}
