package task

import (
	"context"
	"fmt"
	"time"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/ticdc-service/pkg/cloud"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

const (
	certDuration = 10 * 365 * 24 * time.Hour
	renewBefore  = 30 * 24 * time.Hour
)

type certificate struct {
	*baseTask
}

func NewCertificateTask(ctx *Context) deploy.Task {
	return &certificate{
		baseTask: &baseTask{Context: ctx},
	}
}

func (t *certificate) Name() string {
	return "Certificate"
}

func (t *certificate) Create(ctx context.Context) deploy.Result {
	clusterCertName := types.NamespacedName{
		Name:      t.getClusterCertName(),
		Namespace: t.Namespace,
	}
	clusterCertReq := cloud.CertificateRequest{
		Duration:    &metav1.Duration{Duration: certDuration},
		RenewBefore: &metav1.Duration{Duration: renewBefore},
		DNSNames: []string{
			// headless service
			fmt.Sprintf("%s-%s-peer", t.Cdc.Name, cloudv1.ComponentTiCDC),
			fmt.Sprintf("%s-%s-peer.%s", t.Cdc.Name, cloudv1.ComponentTiCDC, t.Namespace),
			fmt.Sprintf("%s-%s-peer.%s.svc", t.Cdc.Name, cloudv1.ComponentTiCDC, t.Namespace),
			// pod dns name via headless service
			fmt.Sprintf("*.%s-%s-peer", t.Cdc.Name, cloudv1.ComponentTiCDC),
			fmt.Sprintf("*.%s-%s-peer.%s", t.Cdc.Name, cloudv1.ComponentTiCDC, t.Namespace),
			fmt.Sprintf("*.%s-%s-peer.%s.svc", t.Cdc.Name, cloudv1.ComponentTiCDC, t.Namespace),
		},
		IPAddresses: []string{
			"127.0.0.1",
			"::1",
		},
	}
	if err := t.createCert(ctx, clusterCertName, &clusterCertReq); err != nil {
		return deploy.Panic(err)
	}
	clientCertName := types.NamespacedName{
		Name:      t.getClientCertName(),
		Namespace: t.Namespace,
	}
	clientCertReq := cloud.CertificateRequest{
		Duration:    &metav1.Duration{Duration: certDuration},
		RenewBefore: &metav1.Duration{Duration: renewBefore},
		DNSNames:    []string{},
		IPAddresses: []string{},
	}

	return deploy.PanicOrDone(t.createCert(ctx, clientCertName, &clientCertReq))
}

func (t *certificate) createCert(ctx context.Context, name types.NamespacedName, req *cloud.CertificateRequest) error {
	c, err := t.CertProvider.BuildCertificate(name, req)
	if err != nil {
		return err
	}
	return k8s.Apply(ctx, t.Remote, c)
}

func (t *certificate) Delete(ctx context.Context) deploy.Result {
	certNames := []types.NamespacedName{
		{
			Name:      t.getClusterCertName(),
			Namespace: t.Namespace,
		},
		{
			Name:      t.getClientCertName(),
			Namespace: t.Namespace,
		},
	}
	for _, name := range certNames {
		if err := t.CertProvider.DeleteCertificate(ctx, t.Remote, name); err != nil {
			return deploy.Error(err)
		}
	}
	return deploy.Done()
}
