package cluster

import (
	"fmt"

	"github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-api/pkg/consts"
	"github.com/tidbcloud/infra-api/pkg/k8s"
	"github.com/tidbcloud/infra-api/pkg/util"
)

func (r *reconcileContext) createTLSCertForMySQLClient(ns, clusterName, mysqlAddress string) error {
	return r.createTiDBServerCert(ns, clusterName, mysqlAddress)
}

func (r *reconcileContext) createTiDBServerCert(ns, clusterName, mysqlAddress string) error {
	privateAddress := getPrivateEndpointAddress(mysqlAddress)
	privatelinkAddress := getPrivateLinkEndpointAddress(mysqlAddress)
	componentDNSNames := r.componentSvcNamespaceNames(ns, clusterName, cloudv1.ComponentTiDB)
	serverCertificate := &v1alpha2.Certificate{
		ObjectMeta: metav1.ObjectMeta{
			Name:      tidbServerSecretName(clusterName),
			Namespace: ns,
		},
		Spec: v1alpha2.CertificateSpec{
			SecretName: tidbServerSecretName(clusterName),
			DNSNames:   append(componentDNSNames, mysqlAddress, privateAddress, privatelinkAddress),
			Duration: &metav1.Duration{
				Duration: consts.CertificateDuration,
			},
			RenewBefore: &metav1.Duration{
				Duration: consts.RenewBefore,
			},
			Organization: []string{consts.CertificateOrg},
			IssuerRef: cmmeta.ObjectReference{
				Name:  consts.AwsPcaIssuer,
				Kind:  consts.AwsPcaIssuerKind,
				Group: consts.CertManagerGroup,
			},
		},
	}

	_, err := k8s.CreateIgnoreExists(r.ctx, r.shootClient, serverCertificate)
	return err
}

func CommonName(ns string) string {
	return util.FnvHash(ns)
}

func (r *reconcileContext) createTLSCertBetweenComponents(ns, clusterName string) error {
	for _, comp := range r.Target.GetComponents() {
		err := r.createTiDBComponentCert(ns, clusterName, comp.GetComponentType())
		if err != nil {
			return err
		}
	}

	return r.createClusterClientCert(ns, clusterName)
}

func (r *reconcileContext) componentSvcNamespaceNames(ns, clusterName string, comp cloudv1.ComponentType) []string {
	return []string{
		fmt.Sprintf("%s-%s.%s", clusterName, comp, ns),
		fmt.Sprintf("%s-%s.%s.svc", clusterName, comp, ns),
	}
}

func (r *reconcileContext) createTiDBComponentCert(ns, clusterName string, comp cloudv1.ComponentType) error {
	componentDNSNames := append([]string{fmt.Sprintf("%s-%s", clusterName, comp)},
		r.componentSvcNamespaceNames(ns, clusterName, comp)...,
	)
	componentDNSNames = append(componentDNSNames,
		fmt.Sprintf("%s-%s-peer", clusterName, comp),
		fmt.Sprintf("%s-%s-peer.%s", clusterName, comp, ns),
		fmt.Sprintf("%s-%s-peer.%s.svc", clusterName, comp, ns),
		fmt.Sprintf("*.%s-%s-peer", clusterName, comp),
		fmt.Sprintf("*.%s-%s-peer.%s", clusterName, comp, ns),
		fmt.Sprintf("*.%s-%s-peer.%s.svc", clusterName, comp, ns),
	)
	if comp == cloudv1.ComponentTiDB {
		privilegedTiDBNames := []string{
			fmt.Sprintf("%s-tidb-extra-service.%s", clusterName, ns),
			fmt.Sprintf("%s-tidb-extra-service.%s.svc", clusterName, ns),
			fmt.Sprintf("*.%s-tidb-extra-service.%s", clusterName, ns),
			fmt.Sprintf("*.%s-tidb-extra-service.%s.svc", clusterName, ns),
		}
		componentDNSNames = append(componentDNSNames, privilegedTiDBNames...)
	}
	componentCertificate := &v1alpha2.Certificate{
		ObjectMeta: metav1.ObjectMeta{
			Name:      tidbClusterComponentSecretName(clusterName, comp),
			Namespace: ns,
		},
		Spec: v1alpha2.CertificateSpec{
			SecretName: tidbClusterComponentSecretName(clusterName, comp),
			Duration: &metav1.Duration{
				Duration: consts.CertificateDuration,
			},
			RenewBefore: &metav1.Duration{
				Duration: consts.RenewBefore,
			},
			CommonName:   CommonName(ns),
			Organization: []string{consts.CertificateOrg},
			Usages: []v1alpha2.KeyUsage{
				v1alpha2.UsageServerAuth,
				v1alpha2.UsageClientAuth,
			},
			DNSNames: componentDNSNames,
			IPAddresses: []string{
				"127.0.0.1",
				"::1",
			},
			IssuerRef: cmmeta.ObjectReference{
				Name:  consts.AwsPcaIssuer,
				Kind:  consts.AwsPcaIssuerKind,
				Group: consts.CertManagerGroup,
			},
		},
	}

	_, err := k8s.CreateIgnoreExists(r.ctx, r.shootClient, componentCertificate)
	return err
}

func (r *reconcileContext) createClusterClientCert(ns, clusterName string) error {
	clientCertificate := &v1alpha2.Certificate{
		ObjectMeta: metav1.ObjectMeta{
			Name:      clusterClientSecretName(clusterName),
			Namespace: ns,
		},
		Spec: v1alpha2.CertificateSpec{
			SecretName: clusterClientSecretName(clusterName),
			Duration: &metav1.Duration{
				Duration: consts.CertificateDuration,
			},
			RenewBefore: &metav1.Duration{
				Duration: consts.RenewBefore,
			},
			CommonName:   CommonName(ns),
			Organization: []string{consts.CertificateOrg},
			Usages: []v1alpha2.KeyUsage{
				v1alpha2.UsageClientAuth,
			},
			IssuerRef: cmmeta.ObjectReference{
				Name:  consts.AwsPcaIssuer,
				Kind:  consts.AwsPcaIssuerKind,
				Group: consts.CertManagerGroup,
			},
		},
	}

	_, err := k8s.CreateIgnoreExists(r.ctx, r.shootClient, clientCertificate)
	return err
}
