package aks

import (
	"context"
	"fmt"
	"time"

	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/applier"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/loadbalancer/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/k8s"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/util"
	azurenetworkv1beta1 "github.com/upbound/provider-azure/apis/network/v1beta1"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

type DNS struct {
	*task.BaseTask
}

func NewDNS(ctx *task.Context) deploy.Task {
	return &DNS{
		BaseTask: &task.BaseTask{Context: ctx},
	}
}

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

func (t *DNS) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	if len(t.LoadBalancer.Status.PrivateIPs) == 0 {
		return deploy.Panic(fmt.Errorf("no LB ip found"))
	}

	record := t.newDNSRecord(t.LoadBalancer.Status.PrivateIPs[0])
	if err := k8s.Apply(ctx, t.Local, record); err != nil {
		return deploy.Panic(err)
	}
	return deploy.Done()
}

func (t *DNS) PostCreate(ctx context.Context) deploy.Result {
	dnsName := t.GetDNSNamePrefix(t.LoadBalancer)
	t.LoadBalancer.Status.DNS = t.GetDNSName(t.LoadBalancer, t.Config.DNSProviderConfig.AzureDNSZone.Domain)
	obj := applier.NewClusterObject[*azurenetworkv1beta1.DNSARecord](dnsName)
	if err := t.Local.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil {
		return deploy.Error(err)
	}
	ready := controllers.IsResourceReady(obj.Status.ResourceStatus)
	if ready {
		t.LoadBalancer.Status.Phase = v1alpha1.LoadBalancerPhaseActive
		return deploy.Done()
	} else {
		return deploy.Error(fmt.Errorf("wait dns record ready")).
			WithRequeuAfter(time.Second * 30)
	}
}

func (t *DNS) Delete(ctx context.Context) deploy.Result {
	dnsName := t.GetDNSNamePrefix(t.LoadBalancer)
	pc := applier.NewClusterObject[*azurenetworkv1beta1.DNSARecord](dnsName)
	return deploy.PanicOrDone(k8s.DeleteObject(ctx, t.Local, pc))
}

func (t *DNS) newDNSRecord(address string) *azurenetworkv1beta1.DNSARecord {
	dnsName := t.GetDNSNamePrefix(t.LoadBalancer)
	// make it competible with the old version in lb management
	rrs := applier.NewClusterObject[*azurenetworkv1beta1.DNSARecord](dnsName)
	rrs.Annotations = map[string]string{}
	rrs.Labels = t.LoadBalancer.Spec.Tags
	rrs.Spec = azurenetworkv1beta1.DNSARecordSpec{
		ResourceSpec: xpv1.ResourceSpec{
			ProviderConfigReference: &xpv1.Reference{
				Name: getProviderConfigName(t.Config.DNSProviderConfig.AzureDNSZone.SubscriptionID),
			},
		},
		ForProvider: azurenetworkv1beta1.DNSARecordParameters{
			Records: []*string{
				ptr.To(address),
			},
			ZoneName:          ptr.To(t.Config.DNSProviderConfig.AzureDNSZone.Domain),
			ResourceGroupName: ptr.To(t.Config.DNSProviderConfig.AzureDNSZone.ResourceGroup),
			TTL:               ptr.To(float64(60)),
		},
	}
	return rrs
}

func getProviderConfigName(accountID string) string {
	return fmt.Sprintf("dns-%s", accountID)
}
