package common

import (
	"context"
	"fmt"
	"time"

	"github.com/samber/lo"
	"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/utils"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/vpc/task"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

type Trigger struct {
	*task.BaseTask
}

func NewTrigger(ctx *task.Context) *Trigger {
	return &Trigger{BaseTask: &task.BaseTask{Context: ctx}}
}

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

func (t *Trigger) Create(ctx context.Context) deploy.Result {
	internetList := &v1alpha1.InternetList{}
	if err := t.InternetRW.List(ctx, internetList, client.MatchingLabels{
		v1alpha1.VPCNameKey: t.VPC.Name,
	}); err != nil {
		return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
	}
	if t.VPC.Annotations == nil {
		t.VPC.Annotations = make(map[string]string)
	}
	for _, secondaryCidr := range t.VPC.Spec.SecondaryCidrs {
		secondaryCidrName := v1alpha1.SecondaryCidrNamePrefix + utils.SanitizeCidr(secondaryCidr)
		if _, ok := t.VPC.Annotations[secondaryCidrName]; !ok {
			t.Logger.Info("secondary cidr not found on vpc annotations, will retry later", "secondaryCidr", secondaryCidr)
			continue
		}
		if t.VPC.Annotations[secondaryCidrName] == v1alpha1.SecondaryCidrStatusFailed {
			t.Logger.Error(fmt.Errorf("secondary cidr already failed: %s", secondaryCidr), "secondaryCidr", secondaryCidr)
			return deploy.Panic(fmt.Errorf("secondary cidr already failed: %s", secondaryCidr)).WithRequeuAfter(30 * time.Second)
		}

		// make sure secondary node subnets are ready
		if t.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(t.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)
		}

		for _, internet := range internetList.Items {
			if err := t.InternetRW.Get(ctx, client.ObjectKey{
				Namespace: internet.Namespace,
				Name:      internet.Name,
			}, &internet); err != nil {
				return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
			}

			if internet.GetAnnotations() == nil {
				internet.Annotations = make(map[string]string)
			}
			if internet.Annotations[secondaryCidrName] == v1alpha1.SecondaryCidrStatusReady {
				t.Logger.Info("secondary cidr already ready on internet annotations, skip", "secondaryCidr", secondaryCidr)
				continue
			}
			internet.Annotations[secondaryCidrName] = v1alpha1.SecondaryCidrStatusPending
			if err := t.InternetRW.Update(ctx, &internet); err != nil {
				return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
			}
		}
		t.VPC.Annotations[secondaryCidrName] = v1alpha1.SecondaryCidrStatusReady
		if err := t.VPCRW.Update(ctx, t.VPC); err != nil {
			return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
		}
	}

	return deploy.Done()
}
