package handler

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"strings"
	"testing"
)

// TestValidateCommonName_ValidInputs tests common names that should pass validation.
func TestValidateCommonName_ValidInputs(t *testing.T) {
	tests := []struct {
		name string
		cn   string
	}{
		{
			name: "simple hostname",
			cn:   "example.com",
		},
		{
			name: "wildcard domain",
			cn:   "*.example.com",
		},
		{
			name: "subdomain",
			cn:   "sub.deep.example.com",
		},
		{
			name: "IPv4 address",
			cn:   "192.168.1.1",
		},
		{
			name: "IPv6 address",
			cn:   "2001:db8::1",
		},
		{
			name: "email address (S/MIME)",
			cn:   "user@example.com",
		},
		{
			name: "hostname with hyphen",
			cn:   "my-host",
		},
		{
			name: "single character hostname",
			cn:   "a",
		},
		{
			name: "hostname with underscore",
			cn:   "my_host",
		},
		{
			name: "complex subdomain",
			cn:   "api.v1.internal.example.com",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateCommonName(tt.cn)
			if err != nil {
				t.Errorf("ValidateCommonName(%q) = %v, want nil", tt.cn, err)
			}
		})
	}
}

// TestValidateCommonName_InvalidInputs tests common names that should fail validation.
func TestValidateCommonName_InvalidInputs(t *testing.T) {
	tests := []struct {
		name    string
		cn      string
		wantErr bool
	}{
		{
			name:    "empty string",
			cn:      "",
			wantErr: true,
		},
		{
			name:    "whitespace only",
			cn:      "   ",
			wantErr: true,
		},
		{
			name:    "string exceeds 253 characters",
			cn:      strings.Repeat("a", 254),
			wantErr: true,
		},
		{
			name:    "path traversal attempt",
			cn:      "../etc/passwd",
			wantErr: true,
		},
		{
			name:    "label starts with hyphen",
			cn:      "-example.com",
			wantErr: true,
		},
		{
			name:    "label ends with hyphen",
			cn:      "example-.com",
			wantErr: true,
		},
		{
			name:    "empty label",
			cn:      "example..com",
			wantErr: true,
		},
		{
			name:    "invalid character space",
			cn:      "my host.com",
			wantErr: true,
		},
		{
			name:    "invalid character slash",
			cn:      "my/host.com",
			wantErr: true,
		},
		{
			name:    "malformed email",
			cn:      "notanemail@",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateCommonName(tt.cn)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateCommonName(%q) error = %v, wantErr %v", tt.cn, err, tt.wantErr)
			}
		})
	}
}

// TestValidateRequired_EmptyAndWhitespace tests required field validation.
func TestValidateRequired_EmptyAndWhitespace(t *testing.T) {
	tests := []struct {
		name    string
		field   string
		value   string
		wantErr bool
	}{
		{
			name:    "empty value",
			field:   "test_field",
			value:   "",
			wantErr: true,
		},
		{
			name:    "valid value",
			field:   "test_field",
			value:   "value",
			wantErr: false,
		},
		{
			name:    "whitespace only value",
			field:   "another_field",
			value:   "   ",
			wantErr: false, // Whitespace is considered a value (not empty string)
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateRequired(tt.field, tt.value)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateRequired(%q, %q) error = %v, wantErr %v", tt.field, tt.value, err, tt.wantErr)
			}
			if err != nil {
				ve, ok := err.(ValidationError)
				if !ok {
					t.Errorf("Expected ValidationError, got %T", err)
				}
				if ve.Field != tt.field {
					t.Errorf("Expected field %q, got %q", tt.field, ve.Field)
				}
			}
		})
	}
}

// TestValidateStringLength_Boundary tests string length validation at boundaries.
func TestValidateStringLength_Boundary(t *testing.T) {
	tests := []struct {
		name    string
		field   string
		value   string
		maxLen  int
		wantErr bool
	}{
		{
			name:    "at max length",
			field:   "test",
			value:   "0123456789",
			maxLen:  10,
			wantErr: false,
		},
		{
			name:    "under max length",
			field:   "test",
			value:   "012345678",
			maxLen:  10,
			wantErr: false,
		},
		{
			name:    "exceeds max length",
			field:   "test",
			value:   "01234567890",
			maxLen:  10,
			wantErr: true,
		},
		{
			name:    "empty string",
			field:   "test",
			value:   "",
			maxLen:  10,
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateStringLength(tt.field, tt.value, tt.maxLen)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateStringLength(%q, %q, %d) error = %v, wantErr %v",
					tt.field, tt.value, tt.maxLen, err, tt.wantErr)
			}
			if err != nil {
				ve, ok := err.(ValidationError)
				if !ok {
					t.Errorf("Expected ValidationError, got %T", err)
				}
				if ve.Field != tt.field {
					t.Errorf("Expected field %q, got %q", tt.field, ve.Field)
				}
			}
		})
	}
}

// TestValidateCSRPEM_Valid tests validation of a real CSR PEM.
func TestValidateCSRPEM_Valid(t *testing.T) {
	// Generate a real CSR using crypto/x509
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatalf("Failed to generate private key: %v", err)
	}

	csrTemplate := &x509.CertificateRequest{
		Subject: pkixName("example.com"),
	}

	csrDER, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, privateKey)
	if err != nil {
		t.Fatalf("Failed to create CSR: %v", err)
	}

	csrPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: csrDER,
	})

	err = ValidateCSRPEM(string(csrPEM))
	if err != nil {
		t.Errorf("ValidateCSRPEM() on valid CSR returned error: %v", err)
	}
}

// TestValidateCSRPEM_InvalidInputs tests CSR validation with invalid inputs.
func TestValidateCSRPEM_InvalidInputs(t *testing.T) {
	tests := []struct {
		name    string
		csrPEM  string
		wantErr bool
	}{
		{
			name:    "empty string",
			csrPEM:  "",
			wantErr: true,
		},
		{
			name:    "not PEM format",
			csrPEM:  "not-a-pem-block",
			wantErr: true,
		},
		{
			name:    "garbage data",
			csrPEM:  "asdfjkl;asdfjkl;",
			wantErr: true,
		},
		{
			name:    "certificate PEM (not CSR)",
			csrPEM:  "-----BEGIN CERTIFICATE-----\nMIIC",
			wantErr: true,
		},
		{
			name:    "PEM with wrong type",
			csrPEM:  "-----BEGIN PRIVATE KEY-----\ndata",
			wantErr: true,
		},
		{
			name:    "whitespace only",
			csrPEM:  "   \n   ",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateCSRPEM(tt.csrPEM)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateCSRPEM(%q) error = %v, wantErr %v", tt.csrPEM, err, tt.wantErr)
			}
			if err != nil {
				ve, ok := err.(ValidationError)
				if !ok {
					t.Errorf("Expected ValidationError, got %T", err)
				}
				if ve.Field != "csr_pem" {
					t.Errorf("Expected field 'csr_pem', got %q", ve.Field)
				}
			}
		})
	}
}

// TestValidatePolicyType_ValidTypes tests valid policy types.
func TestValidatePolicyType_ValidTypes(t *testing.T) {
	validTypes := []struct {
		name string
		ptype interface{}
	}{
		{
			name:  "AllowedIssuers",
			ptype: "AllowedIssuers",
		},
		{
			name:  "AllowedDomains",
			ptype: "AllowedDomains",
		},
		{
			name:  "RequiredMetadata",
			ptype: "RequiredMetadata",
		},
		{
			name:  "AllowedEnvironments",
			ptype: "AllowedEnvironments",
		},
		{
			name:  "RenewalLeadTime",
			ptype: "RenewalLeadTime",
		},
	}

	for _, tt := range validTypes {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidatePolicyType(tt.ptype)
			if err != nil {
				t.Errorf("ValidatePolicyType(%v) = %v, want nil", tt.ptype, err)
			}
		})
	}
}

// TestValidatePolicyType_InvalidType tests invalid policy types.
func TestValidatePolicyType_InvalidType(t *testing.T) {
	tests := []struct {
		name    string
		ptype   interface{}
		wantErr bool
	}{
		{
			name:    "nonexistent type",
			ptype:   "NonexistentType",
			wantErr: true,
		},
		{
			name:    "empty string",
			ptype:   "",
			wantErr: true,
		},
		{
			name:    "lowercase type",
			ptype:   "allowedissuers",
			wantErr: true,
		},
		{
			name:    "integer type",
			ptype:   123,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidatePolicyType(tt.ptype)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidatePolicyType(%v) error = %v, wantErr %v", tt.ptype, err, tt.wantErr)
			}
			if err != nil {
				ve, ok := err.(ValidationError)
				if !ok {
					t.Errorf("Expected ValidationError, got %T", err)
				}
				if ve.Field != "type" {
					t.Errorf("Expected field 'type', got %q", ve.Field)
				}
			}
		})
	}
}

// TestValidatePolicySeverity_ValidSeverities tests valid severity levels.
func TestValidatePolicySeverity_ValidSeverities(t *testing.T) {
	validSeverities := []struct {
		name string
		sev  interface{}
	}{
		{
			name: "Warning",
			sev:  "Warning",
		},
		{
			name: "Error",
			sev:  "Error",
		},
		{
			name: "Critical",
			sev:  "Critical",
		},
	}

	for _, tt := range validSeverities {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidatePolicySeverity(tt.sev)
			if err != nil {
				t.Errorf("ValidatePolicySeverity(%v) = %v, want nil", tt.sev, err)
			}
		})
	}
}

// TestValidatePolicySeverity_InvalidSeverity tests invalid severity levels.
func TestValidatePolicySeverity_InvalidSeverity(t *testing.T) {
	tests := []struct {
		name    string
		sev     interface{}
		wantErr bool
	}{
		{
			name:    "lowercase warning",
			sev:     "warning",
			wantErr: true,
		},
		{
			name:    "nonexistent severity",
			sev:     "Severe",
			wantErr: true,
		},
		{
			name:    "empty string",
			sev:     "",
			wantErr: true,
		},
		{
			name:    "integer",
			sev:     1,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidatePolicySeverity(tt.sev)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidatePolicySeverity(%v) error = %v, wantErr %v", tt.sev, err, tt.wantErr)
			}
			if err != nil {
				ve, ok := err.(ValidationError)
				if !ok {
					t.Errorf("Expected ValidationError, got %T", err)
				}
				if ve.Field != "severity" {
					t.Errorf("Expected field 'severity', got %q", ve.Field)
				}
			}
		})
	}
}

// TestValidationError_ErrorMessage tests ValidationError.Error() method.
func TestValidationError_ErrorMessage(t *testing.T) {
	tests := []struct {
		name    string
		err     ValidationError
		wantMsg string
	}{
		{
			name: "simple message",
			err: ValidationError{
				Field:   "common_name",
				Message: "common_name is required",
			},
			wantMsg: "common_name is required",
		},
		{
			name: "detailed message",
			err: ValidationError{
				Field:   "csr_pem",
				Message: "csr_pem must be a valid PEM-encoded certificate request",
			},
			wantMsg: "csr_pem must be a valid PEM-encoded certificate request",
		},
		{
			name: "error with field info",
			err: ValidationError{
				Field:   "test_field",
				Message: "test_field must be 10 characters or fewer",
			},
			wantMsg: "test_field must be 10 characters or fewer",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			errMsg := tt.err.Error()
			if errMsg != tt.wantMsg {
				t.Errorf("ValidationError.Error() = %q, want %q", errMsg, tt.wantMsg)
			}
		})
	}
}

// TestValidationError_IsError tests that ValidationError satisfies error interface.
func TestValidationError_IsError(t *testing.T) {
	ve := ValidationError{
		Field:   "test",
		Message: "test error",
	}

	// Assign to interface variable to verify it satisfies error
	var err error = ve
	_ = err

	msg := ve.Error()
	if msg != "test error" {
		t.Errorf("Expected error message 'test error', got %q", msg)
	}
}

// pkixName is a helper function to create PKIX name (used in CSR generation).
func pkixName(cn string) pkix.Name {
	return pkix.Name{
		CommonName: cn,
	}
}
