package cluster

import (
	"testing"

	cmv1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	. "github.com/onsi/gomega"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

func TestCreateTiDBServerCert(t *testing.T) {
	cases := map[string]struct {
		namespace string
		address   string
		expect    *cmv1.Certificate
	}{
		"AWSServerCert": {
			namespace: "test",
			address:   "test.aws.pingcap.com",
			expect: &cmv1.Certificate{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "sharedtier-tidb-server-secret",
					Namespace: "test",
				},
				Spec: cmv1.CertificateSpec{
					SecretName: "sharedtier-tidb-server-secret",
					DNSNames: []string{
						"sharedtier-tidb.test",
						"sharedtier-tidb.test.svc",
						"test.aws.pingcap.com",
					},
					Duration: &metav1.Duration{
						Duration: DefaultCertificateDuration,
					},
					RenewBefore: &metav1.Duration{
						Duration: DefaultRenewBefore,
					},
					Subject: &cmv1.X509Subject{
						Organizations: []string{DefaultOrganization},
					},
					IssuerRef: cmmeta.ObjectReference{
						Name:  AwsPcaIssuer,
						Kind:  AwsPcaIssuerKind,
						Group: CertManagerGroup,
					},
				},
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			created := &cmv1.Certificate{}
			r := newFakeReconcileContext(&cloudv1.Cluster{ObjectMeta: metav1.ObjectMeta{
				Name: name, Namespace: tc.namespace,
			}}, nil)
			err := r.createTiDBServerCert(tc.namespace, "sharedtier", tc.address)
			g.Expect(err).To(Succeed())
			err = r.Client.Get(r.ctx, client.ObjectKey{Namespace: tc.expect.Namespace, Name: tc.expect.Name}, created)
			g.Expect(err).To(Succeed())
			g.Expect(created.Spec).To(Equal(tc.expect.Spec))
		})
	}
}

func TestTiDBComponentCert(t *testing.T) {
	cases := map[string]struct {
		namespace string
		comp      cloudv1.ComponentType
		expect    *cmv1.Certificate
	}{
		"TiDBCert": {
			namespace: "test",
			comp:      cloudv1.ComponentTiDB,
			expect: &cmv1.Certificate{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "db-tidb-cluster-secret",
					Namespace: "test-user-ns",
				},
				Spec: cmv1.CertificateSpec{
					SecretName: "db-tidb-cluster-secret",
					DNSNames: []string{
						"db-tidb",
						"db-tidb.test-user-ns",
						"db-tidb.test-user-ns.svc",
						"db-tidb-peer",
						"db-tidb-peer.test-user-ns",
						"db-tidb-peer.test-user-ns.svc",
						"*.db-tidb-peer",
						"*.db-tidb-peer.test-user-ns",
						"*.db-tidb-peer.test-user-ns.svc",
					},
					IPAddresses: []string{
						"127.0.0.1",
						"::1",
					},
					Duration: &metav1.Duration{
						Duration: DefaultCertificateDuration,
					},
					RenewBefore: &metav1.Duration{
						Duration: DefaultRenewBefore,
					},
					CommonName: CommonName("db"),
					Subject: &cmv1.X509Subject{
						Organizations: []string{DefaultOrganization},
					},
					Usages: []cmv1.KeyUsage{
						cmv1.UsageServerAuth,
						cmv1.UsageClientAuth,
					},
					IssuerRef: cmmeta.ObjectReference{
						Name:  AwsPcaIssuer,
						Kind:  AwsPcaIssuerKind,
						Group: CertManagerGroup,
					},
				},
			},
		},
		"TiKVCert": {
			namespace: "test",
			comp:      cloudv1.ComponentTiKV,
			expect: &cmv1.Certificate{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "db-tikv-cluster-secret",
					Namespace: "test-user-ns",
				},
				Spec: cmv1.CertificateSpec{
					SecretName: "db-tikv-cluster-secret",
					DNSNames: []string{
						"db-tikv",
						"db-tikv.test-user-ns",
						"db-tikv.test-user-ns.svc",
						"db-tikv-peer",
						"db-tikv-peer.test-user-ns",
						"db-tikv-peer.test-user-ns.svc",
						"*.db-tikv-peer",
						"*.db-tikv-peer.test-user-ns",
						"*.db-tikv-peer.test-user-ns.svc",
					},
					IPAddresses: []string{
						"127.0.0.1",
						"::1",
					},
					Duration: &metav1.Duration{
						Duration: DefaultCertificateDuration,
					},
					RenewBefore: &metav1.Duration{
						Duration: DefaultRenewBefore,
					},
					CommonName: CommonName("db"),
					Subject: &cmv1.X509Subject{
						Organizations: []string{DefaultOrganization},
					},
					Usages: []cmv1.KeyUsage{
						cmv1.UsageServerAuth,
						cmv1.UsageClientAuth,
					},
					IssuerRef: cmmeta.ObjectReference{
						Name:  AwsPcaIssuer,
						Kind:  AwsPcaIssuerKind,
						Group: CertManagerGroup,
					},
				},
			},
		},
		"PDCert": {
			namespace: "test",
			comp:      cloudv1.ComponentPD,
			expect: &cmv1.Certificate{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "db-pd-cluster-secret",
					Namespace: "test-user-ns",
				},
				Spec: cmv1.CertificateSpec{
					SecretName: "db-pd-cluster-secret",
					DNSNames: []string{
						"db-pd",
						"db-pd.test-user-ns",
						"db-pd.test-user-ns.svc",
						"db-pd-peer",
						"db-pd-peer.test-user-ns",
						"db-pd-peer.test-user-ns.svc",
						"*.db-pd-peer",
						"*.db-pd-peer.test-user-ns",
						"*.db-pd-peer.test-user-ns.svc",
					},
					IPAddresses: []string{
						"127.0.0.1",
						"::1",
					},
					Duration: &metav1.Duration{
						Duration: DefaultCertificateDuration,
					},
					RenewBefore: &metav1.Duration{
						Duration: DefaultRenewBefore,
					},
					CommonName: CommonName("db"),
					Subject: &cmv1.X509Subject{
						Organizations: []string{DefaultOrganization},
					},
					Usages: []cmv1.KeyUsage{
						cmv1.UsageServerAuth,
						cmv1.UsageClientAuth,
					},
					IssuerRef: cmmeta.ObjectReference{
						Name:  AwsPcaIssuer,
						Kind:  AwsPcaIssuerKind,
						Group: CertManagerGroup,
					},
				},
			},
		},
		"TiFlashCert": {
			namespace: "test",
			comp:      cloudv1.ComponentTiFlash,
			expect: &cmv1.Certificate{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "db-tiflash-cluster-secret",
					Namespace: "test-user-ns",
				},
				Spec: cmv1.CertificateSpec{
					SecretName: "db-tiflash-cluster-secret",
					DNSNames: []string{
						"db-tiflash",
						"db-tiflash.test-user-ns",
						"db-tiflash.test-user-ns.svc",
						"db-tiflash-peer",
						"db-tiflash-peer.test-user-ns",
						"db-tiflash-peer.test-user-ns.svc",
						"*.db-tiflash-peer",
						"*.db-tiflash-peer.test-user-ns",
						"*.db-tiflash-peer.test-user-ns.svc",
					},
					IPAddresses: []string{
						"127.0.0.1",
						"::1",
					},
					Duration: &metav1.Duration{
						Duration: DefaultCertificateDuration,
					},
					RenewBefore: &metav1.Duration{
						Duration: DefaultRenewBefore,
					},
					CommonName: CommonName("db"),
					Subject: &cmv1.X509Subject{
						Organizations: []string{DefaultOrganization},
					},
					Usages: []cmv1.KeyUsage{
						cmv1.UsageServerAuth,
						cmv1.UsageClientAuth,
					},
					IssuerRef: cmmeta.ObjectReference{
						Name:  AwsPcaIssuer,
						Kind:  AwsPcaIssuerKind,
						Group: CertManagerGroup,
					},
				},
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			created := &cmv1.Certificate{}
			r := newFakeReconcileContext(&cloudv1.Cluster{ObjectMeta: metav1.ObjectMeta{
				Name: "db", Namespace: tc.namespace,
			}}, nil)
			err := r.createTiDBComponentCert(tc.comp)
			g.Expect(err).To(Succeed())
			err = r.Client.Get(r.ctx, client.ObjectKey{Namespace: tc.expect.Namespace, Name: tc.expect.Name}, created)
			g.Expect(err).To(Succeed())
			g.Expect(created.Spec).To(Equal(tc.expect.Spec))
		})
	}
}
