package action

import (
	"context"
	"testing"
	"time"

	v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
	"github.com/stretchr/testify/assert"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/config"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/cert"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
)

func TestComponentCerts(t *testing.T) {
	sharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id: "sp-cert-test",
			ComponentGroups: []*apiv1.Component{
				{
					Id:            "tikv-1",
					ComponentType: apiv1.ComponentType_TIKV,
				},
				{
					Id:            "pd-1",
					ComponentType: apiv1.ComponentType_PD,
				},
				{
					Id:            "tidb-1",
					ComponentType: apiv1.ComponentType_TIDB,
				},
			},
		},
	}

	tests := []struct {
		name            string
		args            actions.ActionArgs
		expectedNames   []string
		unexpectedNames []string
	}{
		{
			name: "all components",
			args: actions.ActionArgs{},
			expectedNames: []string{
				"db-tikv-1-tikv-cluster-secret",
				"db-pd-1-pd-cluster-secret",
				"db-cluster-client-secret",
				"db-tidb-server-secret",
				"db-tidb-cluster-secret",
				"db-tiproxy-tidb-secret",
			},
			unexpectedNames: []string{
				"db-tidb-1-tidb-cluster-secret",
				"cluster-agent-server-secret",
			},
		},
		{
			name: "target single component id",
			args: actions.ActionArgs{
				workflow.ComponentIdsKey: actions.ActionArgString("pd-1"),
			},
			expectedNames: []string{
				"db-pd-1-pd-cluster-secret",
				"db-cluster-client-secret",
				"db-tidb-server-secret",
				"db-tidb-cluster-secret",
				"db-tiproxy-tidb-secret",
			},
			unexpectedNames: []string{
				"db-tikv-1-tikv-cluster-secret",
				"cluster-agent-server-secret",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			certs := ComponentCerts(context.Background(), sharedPool, tt.args)
			nameSet := make(map[string]struct{}, len(certs))
			for _, c := range certs {
				nameSet[c.GetName()] = struct{}{}
			}

			for _, expected := range tt.expectedNames {
				_, ok := nameSet[expected]
				assert.True(t, ok)
			}
			for _, unexpected := range tt.unexpectedNames {
				_, ok := nameSet[unexpected]
				assert.False(t, ok)
			}
		})
	}
}

func TestComponentCertsAppliesDurationConfig(t *testing.T) {
	sharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id: "sp-cert-duration-test",
			ComponentGroups: []*apiv1.Component{
				{
					Id:            "pd-1",
					ComponentType: apiv1.ComponentType_PD,
				},
			},
		},
	}

	certs := ComponentCerts(context.Background(), sharedPool, actions.ActionArgs{}, cert.WithDurationConfig(cert.DurationConfig{
		TiDBServerDuration: 144 * time.Hour,
		RenewBefore:        24 * time.Hour,
	}))

	var tidbServerCert *v1.Certificate
	for _, c := range certs {
		if c.GetName() == cert.ServerCertSecret("tidb", "") {
			tidbServerCert = c
			break
		}
	}

	if assert.NotNil(t, tidbServerCert) {
		assert.Equal(t, 144*time.Hour, tidbServerCert.Spec.Duration.Duration)
		assert.Equal(t, 24*time.Hour, tidbServerCert.Spec.RenewBefore.Duration)
	}
}

func TestCertOptionsFromBizConfig(t *testing.T) {
	opts := CertOptionsFromBizConfig(&config.BizConfig{
		CertificateDuration: config.CertificateDurationConfig{
			TiDBServer:    144 * time.Hour,
			TiProxyServer: 120 * time.Hour,
			RenewBefore:   24 * time.Hour,
		},
	})

	tiproxyServerCert := cert.TiProxyServerCerts("tiproxy-1", "ns", "identity", "example.com", true, opts...)
	assert.Equal(t, 120*time.Hour, tiproxyServerCert.Spec.Duration.Duration)
	assert.Equal(t, 24*time.Hour, tiproxyServerCert.Spec.RenewBefore.Duration)
}
