package cli

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/json"
	"encoding/pem"
	"math/big"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func TestClient_ListCertificates(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" || r.URL.Path != "/api/v1/certificates" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"data": []map[string]interface{}{
				{
					"id":          "mc-1",
					"common_name": "example.com",
					"status":      "Active",
					"expires_at":  "2025-12-31T00:00:00Z",
					"issuer_id":   "iss-local",
				},
			},
			"total": 1,
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.ListCertificates([]string{})
	if err != nil {
		t.Fatalf("ListCertificates failed: %v", err)
	}
}

func TestClient_GetCertificate(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" || r.URL.Path != "/api/v1/certificates/mc-1" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"id":          "mc-1",
			"common_name": "example.com",
			"status":      "Active",
			"expires_at":  "2025-12-31T00:00:00Z",
			"issuer_id":   "iss-local",
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "json")
	err := client.GetCertificate("mc-1")
	if err != nil {
		t.Fatalf("GetCertificate failed: %v", err)
	}
}

func TestClient_RenewCertificate(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" || r.URL.Path != "/api/v1/certificates/mc-1/renew" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"job_id": "job-123",
			"status": "Pending",
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.RenewCertificate("mc-1")
	if err != nil {
		t.Fatalf("RenewCertificate failed: %v", err)
	}
}

func TestClient_RevokeCertificate(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" || r.URL.Path != "/api/v1/certificates/mc-1/revoke" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"status": "revoked",
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.RevokeCertificate("mc-1", "cessationOfOperation")
	if err != nil {
		t.Fatalf("RevokeCertificate failed: %v", err)
	}
}

func TestClient_ListAgents(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" || r.URL.Path != "/api/v1/agents" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"data": []map[string]interface{}{
				{
					"id":           "ag-1",
					"hostname":     "agent1.example.com",
					"status":       "Online",
					"os":           "linux",
					"architecture": "amd64",
					"ip_address":   "192.168.1.1",
				},
			},
			"total": 1,
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.ListAgents([]string{})
	if err != nil {
		t.Fatalf("ListAgents failed: %v", err)
	}
}

func TestClient_GetAgent(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" || r.URL.Path != "/api/v1/agents/ag-1" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"id":           "ag-1",
			"hostname":     "agent1.example.com",
			"status":       "Online",
			"os":           "linux",
			"architecture": "amd64",
			"ip_address":   "192.168.1.1",
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "json")
	err := client.GetAgent("ag-1")
	if err != nil {
		t.Fatalf("GetAgent failed: %v", err)
	}
}

func TestClient_ListJobs(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" || r.URL.Path != "/api/v1/jobs" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"data": []map[string]interface{}{
				{
					"id":             "job-1",
					"type":           "Renewal",
					"certificate_id": "mc-1",
					"status":         "Completed",
					"attempts":       1,
					"max_attempts":   3,
				},
			},
			"total": 1,
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.ListJobs([]string{})
	if err != nil {
		t.Fatalf("ListJobs failed: %v", err)
	}
}

func TestClient_GetJob(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" || r.URL.Path != "/api/v1/jobs/job-1" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"id":             "job-1",
			"type":           "Renewal",
			"certificate_id": "mc-1",
			"status":         "Completed",
			"attempts":       1,
			"max_attempts":   3,
		})
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "json")
	err := client.GetJob("job-1")
	if err != nil {
		t.Fatalf("GetJob failed: %v", err)
	}
}

func TestClient_CancelJob(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" || r.URL.Path != "/api/v1/jobs/job-1/cancel" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.WriteHeader(http.StatusNoContent)
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.CancelJob("job-1")
	if err != nil {
		t.Fatalf("CancelJob failed: %v", err)
	}
}

func TestClient_GetStatus(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")

		if r.URL.Path == "/api/v1/health" {
			json.NewEncoder(w).Encode(map[string]interface{}{
				"status":    "healthy",
				"timestamp": time.Now().Format(time.RFC3339),
			})
		} else if r.URL.Path == "/api/v1/stats/summary" {
			json.NewEncoder(w).Encode(map[string]interface{}{
				"data": map[string]interface{}{
					"total_certificates": 10,
					"total_agents":       5,
				},
			})
		}
	}))
	defer server.Close()

	client := NewClient(server.URL, "", "table")
	err := client.GetStatus()
	if err != nil {
		t.Fatalf("GetStatus failed: %v", err)
	}
}

func TestParsePEMCertificates(t *testing.T) {
	// Generate a self-signed test certificate
	cert := generateTestCert()

	// Encode it to PEM
	pemBlock := &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert.Raw,
	}
	pemData := pem.EncodeToMemory(pemBlock)

	// Parse it back
	certs, err := parsePEMCertificates(pemData)
	if err != nil {
		t.Fatalf("parsePEMCertificates failed: %v", err)
	}

	if len(certs) != 1 {
		t.Fatalf("expected 1 certificate, got %d", len(certs))
	}

	if certs[0].Subject.CommonName != "test.example.com" {
		t.Fatalf("expected CommonName 'test.example.com', got %s", certs[0].Subject.CommonName)
	}
}

func TestParsePEMCertificates_Multiple(t *testing.T) {
	// Generate two test certificates
	cert1 := generateTestCert()
	cert2 := generateTestCert()

	// Encode both to PEM
	block1 := &pem.Block{Type: "CERTIFICATE", Bytes: cert1.Raw}
	block2 := &pem.Block{Type: "CERTIFICATE", Bytes: cert2.Raw}

	pemData := append(pem.EncodeToMemory(block1), pem.EncodeToMemory(block2)...)

	// Parse them back
	certs, err := parsePEMCertificates(pemData)
	if err != nil {
		t.Fatalf("parsePEMCertificates failed: %v", err)
	}

	if len(certs) != 2 {
		t.Fatalf("expected 2 certificates, got %d", len(certs))
	}
}

func TestParsePEMCertificates_NoCertificates(t *testing.T) {
	pemData := []byte("no certificates here")

	_, err := parsePEMCertificates(pemData)
	if err == nil {
		t.Fatal("expected error for empty PEM data")
	}
}

func TestClient_AuthHeader(t *testing.T) {
	var authHeader string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		authHeader = r.Header.Get("Authorization")
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{"data": []interface{}{}})
	}))
	defer server.Close()

	client := NewClient(server.URL, "testkey123", "json")
	client.do("GET", "/api/v1/certificates", nil, nil)

	if authHeader != "Bearer testkey123" {
		t.Fatalf("expected 'Bearer testkey123', got '%s'", authHeader)
	}
}

// Helper function to generate a test certificate
func generateTestCert() *x509.Certificate {
	now := time.Now()
	template := &x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: "test.example.com",
		},
		NotBefore:             now,
		NotAfter:              now.Add(365 * 24 * time.Hour),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		DNSNames:              []string{"test.example.com", "*.test.example.com"},
	}

	privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
	certBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey)
	cert, _ := x509.ParseCertificate(certBytes)

	return cert
}
