package cert

import (
	"fmt"

	v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// DMComponentCertSecretName returns the TiDB Operator v2 TLS secret name for a DM component.
// Pattern: <groupName>-<component>-cluster-secret
func DMComponentCertSecretName(groupName, component string) string {
	return fmt.Sprintf("%s-%s-cluster-secret", groupName, component)
}

// DMComponentCertV2 builds a Certificate CR for a DM master or worker group in operator v2.
func DMComponentCertV2(groupName, component, namespace string) *v1.Certificate {
	secretName := DMComponentCertSecretName(groupName, component)
	return &v1.Certificate{
		ObjectMeta: metav1.ObjectMeta{
			Name:      secretName,
			Namespace: namespace,
		},
		Spec: v1.CertificateSpec{
			SecretName: secretName,
			Duration: &metav1.Duration{
				Duration: selfSignedComponentCACertDuration,
			},
			RenewBefore: &metav1.Duration{
				Duration: renewBefore,
			},
			CommonName: utils.FnvHash(namespace),
			Subject: &v1.X509Subject{
				Organizations: []string{certOrganization},
			},
			Usages: []v1.KeyUsage{
				v1.UsageServerAuth,
				v1.UsageClientAuth,
			},
			DNSNames: dmComponentDNSNames(groupName, component, namespace),
			IPAddresses: []string{
				"127.0.0.1",
				"::1",
			},
			PrivateKey: &v1.CertificatePrivateKey{
				RotationPolicy: v1.RotationPolicyAlways,
			},
			IssuerRef: cmmeta.ObjectReference{
				Name:  tidbComponentsSelfSignedIssuerName,
				Kind:  tidbComponentsSelfSignedIssuerKind,
				Group: selfSignedIssuerGroup,
			},
		},
	}
}

// DMMasterCertV2 builds the DM master cluster certificate for operator v2.
func DMMasterCertV2(dmClusterID, namespace string) (*v1.Certificate, error) {
	groupName, err := entity.DMGroupNameV2(dmClusterID)
	if err != nil {
		return nil, err
	}
	return DMComponentCertV2(groupName, entity.DMMasterComponent, namespace), nil
}

// DMWorkerCertV2 builds the DM worker cluster certificate for operator v2.
func DMWorkerCertV2(dmClusterID, namespace string) (*v1.Certificate, error) {
	groupName, err := entity.DMGroupNameV2(dmClusterID)
	if err != nil {
		return nil, err
	}
	return DMComponentCertV2(groupName, entity.DMWorkerComponent, namespace), nil
}

// DMClusterCertsV2 returns the DM master and worker cluster certificates for operator v2.
func DMClusterCertsV2(dmClusterID, namespace string) ([]*v1.Certificate, error) {
	masterCert, err := DMMasterCertV2(dmClusterID, namespace)
	if err != nil {
		return nil, err
	}
	workerCert, err := DMWorkerCertV2(dmClusterID, namespace)
	if err != nil {
		return nil, err
	}
	return []*v1.Certificate{masterCert, workerCert}, nil
}

func dmComponentDNSNames(groupName, component, namespace string) []string {
	peer := fmt.Sprintf("%s-%s", groupName, component)
	return []string{
		"*.db-cluster",
		fmt.Sprintf("*.db-cluster.%s", namespace),
		fmt.Sprintf("*.db-cluster.%s.svc", namespace),
		fmt.Sprintf("*.db-cluster.%s.svc.cluster.local", namespace),
		groupName,
		fmt.Sprintf("%s.%s", groupName, namespace),
		fmt.Sprintf("%s.%s.svc", groupName, namespace),
		fmt.Sprintf("%s.%s.svc.cluster.local", groupName, namespace),
		peer,
		fmt.Sprintf("%s.%s", peer, namespace),
		fmt.Sprintf("%s.%s.svc", peer, namespace),
		fmt.Sprintf("%s.%s.svc.cluster.local", peer, namespace),
		fmt.Sprintf("%s-peer", peer),
		fmt.Sprintf("%s-peer.%s", peer, namespace),
		fmt.Sprintf("%s-peer.%s.svc", peer, namespace),
		fmt.Sprintf("%s-peer.%s.svc.cluster.local", peer, namespace),
		fmt.Sprintf("*.%s-peer", peer),
		fmt.Sprintf("*.%s-peer.%s", peer, namespace),
		fmt.Sprintf("*.%s-peer.%s.svc", peer, namespace),
		fmt.Sprintf("*.%s-peer.%s.svc.cluster.local", peer, namespace),
	}
}
