package eks

import (
	"context"
	"fmt"
	"time"

	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
	"github.com/crossplane-contrib/provider-aws/apis/ec2/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/vpc/task"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

type SecondaryCidr struct {
	*task.BaseTask
}

func NewSecondaryCidr(ctx *task.Context) *SecondaryCidr {
	return &SecondaryCidr{
		BaseTask: &task.BaseTask{Context: ctx},
	}
}

func (s *SecondaryCidr) Name() string {
	return "SecondaryCidr"
}

func (s *SecondaryCidr) Create(ctx context.Context) deploy.Result {
	s.BeginAt = ptr.To(time.Now())

	if len(s.VPC.Spec.SecondaryCidrs) == 0 {
		return deploy.Done()
	}

	for _, cidr := range s.VPC.Spec.SecondaryCidrs {
		if cidr == "" {
			return deploy.Panic(fmt.Errorf("secondary CIDR block cannot be empty")).WithRequeuAfter(time.Second * 30)
		}
		if !utils.IsValidCIDR(cidr) {
			return deploy.Panic(fmt.Errorf("secondary CIDR block %s is not a valid CIDR block", cidr)).WithRequeuAfter(time.Second * 30)
		}
		name, err := s.GetSecondaryCidrName(cidr)
		if err != nil {
			return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
		}
		secondaryCidr := v1beta1.VPCCIDRBlock{
			TypeMeta: metav1.TypeMeta{
				APIVersion: "ec2.aws.crossplane.io/v1beta1",
				Kind:       "VPCCIDRBlock",
			},
			ObjectMeta: metav1.ObjectMeta{
				Name:   name,
				Labels: s.Namer.Labels(),
			},
			Spec: v1beta1.VPCCIDRBlockSpec{
				ForProvider: v1beta1.VPCCIDRBlockParameters{
					Region:    s.VPC.Spec.Region,
					CIDRBlock: ptr.To(cidr),
					VPCIDRef:  &xpv1.Reference{Name: s.GetVPCName()},
				},
				ResourceSpec: xpv1.ResourceSpec{
					ProviderConfigReference: &xpv1.Reference{Name: s.ProviderConfigName()},
				},
			},
		}

		if _, err := controllerutil.CreateOrUpdate(ctx, s.Local, &secondaryCidr, func() error {
			return nil
		}); err != nil {
			return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
		}
	}
	return deploy.Done()
}

func (s *SecondaryCidr) PostCreate(ctx context.Context) deploy.Result {
	for _, cidr := range s.VPC.Spec.SecondaryCidrs {
		name, err := s.GetSecondaryCidrName(cidr)
		if err != nil {
			return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
		}
		secondaryCidr := &v1beta1.VPCCIDRBlock{}
		if err := s.Local.Get(ctx, types.NamespacedName{Name: name}, secondaryCidr); err != nil {
			return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
		}

		if secondaryCidr.Status.AtProvider.CIDRBlockState.State != string(ec2types.VpcCidrBlockStateCodeAssociated) {
			return deploy.Panic(fmt.Errorf("secondary CIDR block %s is not associated", cidr)).WithRequeuAfter(time.Second * 30)
		}
	}
	return deploy.Done()
}

func (s *SecondaryCidr) Delete(ctx context.Context) deploy.Result {
	for _, cidr := range s.VPC.Spec.SecondaryCidrs {
		name, err := s.GetSecondaryCidrName(cidr)
		if err != nil {
			return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
		}
		secondaryCidr := &v1beta1.VPCCIDRBlock{
			ObjectMeta: metav1.ObjectMeta{
				Name: name,
			},
		}
		if err := k8s.DeleteObject(ctx, s.Local, secondaryCidr); err != nil {
			return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
		}
	}
	return deploy.Done()
}
