package secret

import (
	"context"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
)

// generateTestCert creates a self-signed certificate for testing
func generateTestCert(t *testing.T, notBefore, notAfter time.Time) (certPEM, keyPEM []byte) {
	t.Helper()

	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatalf("failed to generate key: %v", err)
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: "test-cert",
		},
		NotBefore:             notBefore,
		NotAfter:              notAfter,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
	}

	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		t.Fatalf("failed to create certificate: %v", err)
	}

	certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})

	keyDER, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		t.Fatalf("failed to marshal key: %v", err)
	}
	keyPEM = pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})

	return certPEM, keyPEM
}

// generateTestCA creates a CA certificate for testing
func generateTestCA(t *testing.T) []byte {
	t.Helper()

	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatalf("failed to generate CA key: %v", err)
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: "test-ca",
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().Add(24 * time.Hour),
		IsCA:                  true,
		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
		BasicConstraintsValid: true,
	}

	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		t.Fatalf("failed to create CA certificate: %v", err)
	}

	return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
}

func TestManager_GetTLSConfig(t *testing.T) {
	certPEM, keyPEM := generateTestCert(t, time.Now(), time.Now().Add(24*time.Hour))
	caPEM := generateTestCA(t)

	tests := []struct {
		name       string
		secretName string
		namespace  string
		secretData map[string][]byte
		wantErr    bool
		validate   func(*testing.T, *tls.Config)
	}{
		{
			name:       "load TLS config with tls.crt and tls.key",
			secretName: "test-secret",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.crt": certPEM,
				"tls.key": keyPEM,
			},
			wantErr: false,
			validate: func(t *testing.T, cfg *tls.Config) {
				if len(cfg.Certificates) == 0 {
					t.Error("expected at least one certificate")
				}
				if cfg.MinVersion != tls.VersionTLS12 {
					t.Errorf("expected MinVersion TLS12, got %d", cfg.MinVersion)
				}
			},
		},
		{
			name:       "load TLS config with cert.pem and key.pem",
			secretName: "pem-secret",
			namespace:  "default",
			secretData: map[string][]byte{
				"cert.pem": certPEM,
				"key.pem":  keyPEM,
			},
			wantErr: false,
			validate: func(t *testing.T, cfg *tls.Config) {
				if len(cfg.Certificates) == 0 {
					t.Error("expected at least one certificate")
				}
			},
		},
		{
			name:       "load TLS config with CA",
			secretName: "ca-secret",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.crt": certPEM,
				"tls.key": keyPEM,
				"ca.crt":  caPEM,
			},
			wantErr: false,
			validate: func(t *testing.T, cfg *tls.Config) {
				if cfg.RootCAs == nil {
					t.Error("expected RootCAs to be set")
				}
			},
		},
		{
			name:       "missing certificate",
			secretName: "missing-cert",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.key": keyPEM,
			},
			wantErr: true,
		},
		{
			name:       "missing key",
			secretName: "missing-key",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.crt": certPEM,
			},
			wantErr: true,
		},
		{
			name:       "secret not found",
			secretName: "nonexistent",
			namespace:  "default",
			secretData: nil,
			wantErr:    true,
		},
		{
			name:       "invalid certificate",
			secretName: "invalid-cert",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.crt": []byte("invalid cert data"),
				"tls.key": keyPEM,
			},
			wantErr: true,
		},
		{
			name:       "invalid key",
			secretName: "invalid-key",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.crt": certPEM,
				"tls.key": []byte("invalid key data"),
			},
			wantErr: true,
		},
		{
			name:       "invalid CA",
			secretName: "invalid-ca",
			namespace:  "default",
			secretData: map[string][]byte{
				"tls.crt": certPEM,
				"tls.key": keyPEM,
				"ca.crt":  []byte("invalid ca data"),
			},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			client := fake.NewClientset()

			// Create secret if data provided
			if tt.secretData != nil {
				secret := &corev1.Secret{
					ObjectMeta: metav1.ObjectMeta{
						Name:      tt.secretName,
						Namespace: tt.namespace,
					},
					Data: tt.secretData,
				}
				_, err := client.CoreV1().Secrets(tt.namespace).Create(context.Background(), secret, metav1.CreateOptions{})
				if err != nil {
					t.Fatalf("failed to create secret: %v", err)
				}
			}

			manager := NewProviderWithClient(client)
			cfg, err := manager.GetTLSConfig(context.Background(), tt.secretName, tt.namespace)

			if tt.wantErr {
				if err == nil {
					t.Error("expected error, got nil")
				}
				return
			}
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if cfg == nil {
				t.Fatal("expected non-nil TLS config")
			}
			if tt.validate != nil {
				tt.validate(t, cfg)
			}
		})
	}
}
