package service

import (
	"context"
	"fmt"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/clusterproxy"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
)

const (
	certManagerAPIVersion = "cert-manager.io/v1"
	certManagerKind       = "Certificate"

	certDurationHours    = "8760h0m0s"
	certRenewBeforeHours = "360h0m0s"
)

// ensureNewArchTiCDCCerts makes sure the two TLS Certificate CRs that
// tidb-operator v1 expects are present before it tries to schedule the
// TiCDC pods. Without these certs, the pod stays in ContainerCreating
// with FailedMount: secret <cr-name>-{cluster-client,ticdc-cluster}-secret
// not found.
func (s *cdcService) ensureNewArchTiCDCCerts(
	ctx context.Context,
	proxy facade.ServerlessClusterProxyFacade,
	clusterName string,
) bizerr.BizError {
	cfg := config.Global().CDCConfig.NewArch
	if cfg.IssuerName == "" {
		return bizerr.New(bizerr.ErrCodeUnknown,
			"newarch ticdc cert-manager issuer is not configured")
	}
	if err := s.ensureCert(ctx, proxy, buildClusterClientCert(clusterName, &cfg)); err != nil {
		return err
	}
	return s.ensureCert(ctx, proxy, buildTiCDCServerCert(clusterName, &cfg))
}

func (s *cdcService) ensureCert(
	ctx context.Context,
	proxy facade.ServerlessClusterProxyFacade,
	cert map[string]any,
) bizerr.BizError {
	name := certMetaName(cert)
	namespace := certMetaNamespace(cert)
	_, err := proxy.CallK8sResource(ctx, &clusterproxy.K8sResourceRequest{
		OperateType: "Get",
		APIVersion:  certManagerAPIVersion,
		Kind:        certManagerKind,
		Namespace:   namespace,
		Name:        name,
	})
	if err == nil {
		return nil
	}
	_, err = proxy.CallK8sResource(ctx, &clusterproxy.K8sResourceRequest{
		OperateType: "Create",
		APIVersion:  certManagerAPIVersion,
		Kind:        certManagerKind,
		Namespace:   namespace,
		Object:      cert,
	})
	if err != nil {
		// Tolerate the TOCTOU race between Get and Create — another
		// reconcile (or another shard pod) may have just created it.
		if isAlreadyExistsErr(err) {
			return nil
		}
		return bizerr.WrapError(err, "create cert %s failed", name)
	}
	return nil
}

func certMetaName(cert map[string]any) string {
	meta, _ := cert["metadata"].(map[string]any)
	name, _ := meta["name"].(string)
	return name
}

func certMetaNamespace(cert map[string]any) string {
	meta, _ := cert["metadata"].(map[string]any)
	ns, _ := meta["namespace"].(string)
	return ns
}

func buildClusterClientCert(clusterName string, cfg *config.CDCNewArchConfig) map[string]any {
	secretName := fmt.Sprintf("%s-cluster-client-secret", clusterName)
	return map[string]any{
		"apiVersion": certManagerAPIVersion,
		"kind":       certManagerKind,
		"metadata": map[string]any{
			"name":      secretName,
			"namespace": cfg.Namespace,
			"labels": map[string]any{
				"app.kubernetes.io/managed-by": "serverless-service",
			},
		},
		"spec": map[string]any{
			"secretName":  secretName,
			"commonName":  clusterName,
			"duration":    certDurationHours,
			"renewBefore": certRenewBeforeHours,
			"subject": map[string]any{
				"organizations": []string{"PingCAP"},
			},
			"usages": []string{"client auth"},
			"issuerRef": map[string]any{
				"group": "cert-manager.io",
				"kind":  cfg.IssuerKind,
				"name":  cfg.IssuerName,
			},
		},
	}
}

func buildTiCDCServerCert(clusterName string, cfg *config.CDCNewArchConfig) map[string]any {
	secretName := fmt.Sprintf("%s-ticdc-cluster-secret", clusterName)
	svc := fmt.Sprintf("%s-ticdc", clusterName)
	peer := fmt.Sprintf("%s-ticdc-peer", clusterName)
	dnsNames := []string{
		svc,
		svc + "." + cfg.Namespace,
		svc + "." + cfg.Namespace + ".svc",
		svc + "." + cfg.Namespace + ".svc.cluster.local",
		peer,
		peer + "." + cfg.Namespace,
		peer + "." + cfg.Namespace + ".svc",
		peer + "." + cfg.Namespace + ".svc.cluster.local",
		"*." + peer,
		"*." + peer + "." + cfg.Namespace,
		"*." + peer + "." + cfg.Namespace + ".svc",
		"*." + peer + "." + cfg.Namespace + ".svc.cluster.local",
	}
	return map[string]any{
		"apiVersion": certManagerAPIVersion,
		"kind":       certManagerKind,
		"metadata": map[string]any{
			"name":      secretName,
			"namespace": cfg.Namespace,
			"labels": map[string]any{
				"app.kubernetes.io/managed-by": "serverless-service",
			},
		},
		"spec": map[string]any{
			"secretName":  secretName,
			"commonName":  clusterName,
			"duration":    certDurationHours,
			"renewBefore": certRenewBeforeHours,
			"dnsNames":    dnsNames,
			"ipAddresses": []string{"127.0.0.1"},
			"subject": map[string]any{
				"organizations": []string{"PingCAP"},
			},
			"usages": []string{"server auth", "client auth"},
			"issuerRef": map[string]any{
				"group": "cert-manager.io",
				"kind":  cfg.IssuerKind,
				"name":  cfg.IssuerName,
			},
		},
	}
}
