package traefik_test

import (
	"context"
	"encoding/json"
	"log/slog"
	"os"
	"path/filepath"
	"testing"

	"github.com/shankar0123/certctl/internal/connector/target"
	"github.com/shankar0123/certctl/internal/connector/target/traefik"
)

func TestTraefikConnector_ValidateConfig_Success(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	tmpDir := t.TempDir()
	cfg := traefik.Config{
		CertDir:  tmpDir,
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	err := connector.ValidateConfig(ctx, rawConfig)
	if err != nil {
		t.Fatalf("ValidateConfig failed: %v", err)
	}
}

func TestTraefikConnector_ValidateConfig_InvalidJSON(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	connector := traefik.New(&traefik.Config{}, logger)
	err := connector.ValidateConfig(ctx, json.RawMessage(`{invalid}`))
	if err == nil {
		t.Fatal("expected error for invalid JSON")
	}
}

func TestTraefikConnector_ValidateConfig_MissingCertDir(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	cfg := traefik.Config{
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	err := connector.ValidateConfig(ctx, rawConfig)
	if err == nil {
		t.Fatal("expected error for missing cert_dir")
	}
}

func TestTraefikConnector_ValidateConfig_DirectoryNotExists(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	cfg := traefik.Config{
		CertDir:  "/nonexistent/directory",
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	err := connector.ValidateConfig(ctx, rawConfig)
	if err == nil {
		t.Fatal("expected error for non-existent directory")
	}
}

func TestTraefikConnector_DeployCertificate_Success(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	tmpDir := t.TempDir()
	cfg := traefik.Config{
		CertDir:  tmpDir,
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	_ = connector.ValidateConfig(ctx, rawConfig)

	request := target.DeploymentRequest{
		CertPEM:  "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
		KeyPEM:   "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----",
		ChainPEM: "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
	}

	result, err := connector.DeployCertificate(ctx, request)
	if err != nil {
		t.Fatalf("DeployCertificate failed: %v", err)
	}

	if !result.Success {
		t.Fatalf("deployment should succeed, got: %s", result.Message)
	}

	// Verify certificate file was created
	certPath := filepath.Join(tmpDir, "cert.pem")
	if _, err := os.Stat(certPath); os.IsNotExist(err) {
		t.Fatalf("certificate file was not created: %s", certPath)
	}

	// Verify key file was created with correct permissions
	keyPath := filepath.Join(tmpDir, "key.pem")
	if _, err := os.Stat(keyPath); os.IsNotExist(err) {
		t.Fatalf("key file was not created: %s", keyPath)
	}

	// Check key file permissions (should be 0600)
	keyInfo, _ := os.Stat(keyPath)
	perms := keyInfo.Mode().Perm()
	if perms != 0600 {
		t.Fatalf("key file permissions are %o, expected 0600", perms)
	}
}

func TestTraefikConnector_DeployCertificate_WriteError(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	// Use a non-existent directory to trigger write error
	cfg := traefik.Config{
		CertDir:  "/root/certctl/certs",
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)

	request := target.DeploymentRequest{
		CertPEM:  "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
		KeyPEM:   "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----",
		ChainPEM: "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
	}

	result, err := connector.DeployCertificate(ctx, request)
	if err == nil {
		t.Fatal("expected error for write failure")
	}

	if result.Success {
		t.Fatal("deployment should fail")
	}
}

func TestTraefikConnector_ValidateDeployment_Success(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	tmpDir := t.TempDir()
	cfg := traefik.Config{
		CertDir:  tmpDir,
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	_ = connector.ValidateConfig(ctx, rawConfig)

	// First deploy a certificate
	deployRequest := target.DeploymentRequest{
		CertPEM:  "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
		KeyPEM:   "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----",
		ChainPEM: "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
	}
	connector.DeployCertificate(ctx, deployRequest)

	// Now validate
	validateRequest := target.ValidationRequest{
		CertificateID: "mc-test",
		Serial:        "123456",
	}

	result, err := connector.ValidateDeployment(ctx, validateRequest)
	if err != nil {
		t.Fatalf("ValidateDeployment failed: %v", err)
	}

	if !result.Valid {
		t.Fatalf("validation should succeed, got: %s", result.Message)
	}

	if result.Serial != "123456" {
		t.Fatalf("serial mismatch: expected 123456, got %s", result.Serial)
	}
}

func TestTraefikConnector_ValidateDeployment_CertFileNotFound(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	tmpDir := t.TempDir()
	cfg := traefik.Config{
		CertDir:  tmpDir,
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	_ = connector.ValidateConfig(ctx, rawConfig)

	// Don't deploy anything, just validate
	validateRequest := target.ValidationRequest{
		CertificateID: "mc-test",
		Serial:        "123456",
	}

	result, err := connector.ValidateDeployment(ctx, validateRequest)
	if err == nil {
		t.Fatal("expected error for missing certificate file")
	}

	if result.Valid {
		t.Fatal("validation should fail")
	}
}

func TestTraefikConnector_DeployCertificate_WithoutChain(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	tmpDir := t.TempDir()
	cfg := traefik.Config{
		CertDir:  tmpDir,
		CertFile: "cert.pem",
		KeyFile:  "key.pem",
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	_ = connector.ValidateConfig(ctx, rawConfig)

	// Deploy without chain
	request := target.DeploymentRequest{
		CertPEM: "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
		KeyPEM:  "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----",
	}

	result, err := connector.DeployCertificate(ctx, request)
	if err != nil {
		t.Fatalf("DeployCertificate failed: %v", err)
	}

	if !result.Success {
		t.Fatalf("deployment should succeed, got: %s", result.Message)
	}

	// Verify certificate file exists
	certPath := filepath.Join(tmpDir, "cert.pem")
	data, err := os.ReadFile(certPath)
	if err != nil {
		t.Fatalf("failed to read cert file: %v", err)
	}

	if string(data) != "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----\n" {
		t.Fatalf("certificate content mismatch")
	}
}

func TestTraefikConnector_DefaultFilenames(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
	ctx := context.Background()

	tmpDir := t.TempDir()
	cfg := traefik.Config{
		CertDir: tmpDir,
		// Don't specify CertFile and KeyFile, use defaults
	}

	connector := traefik.New(&cfg, logger)
	rawConfig, _ := json.Marshal(cfg)
	err := connector.ValidateConfig(ctx, rawConfig)
	if err != nil {
		t.Fatalf("ValidateConfig failed: %v", err)
	}
}
