package validate

import (
	"regexp"
	"testing"
)

func TestValidationError_Error(t *testing.T) {
	err := ValidationError{Field: "name", Message: "is required"}
	expected := "name: is required"
	if err.Error() != expected {
		t.Errorf("expected %q, got %q", expected, err.Error())
	}
}

func TestValidationErrors_Error(t *testing.T) {
	tests := []struct {
		name     string
		errors   ValidationErrors
		expected string
	}{
		{
			name:     "no errors",
			errors:   ValidationErrors{},
			expected: "",
		},
		{
			name: "single error",
			errors: ValidationErrors{
				{Field: "name", Message: "is required"},
			},
			expected: "name: is required",
		},
		{
			name: "multiple errors",
			errors: ValidationErrors{
				{Field: "name", Message: "is required"},
				{Field: "email", Message: "is invalid"},
			},
			expected: "validation failed: name: is required; email: is invalid",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.errors.Error(); got != tt.expected {
				t.Errorf("expected %q, got %q", tt.expected, got)
			}
		})
	}
}

func TestValidationErrors_HasErrors(t *testing.T) {
	if (ValidationErrors{}).HasErrors() {
		t.Error("expected empty ValidationErrors to have no errors")
	}

	errs := ValidationErrors{{Field: "test", Message: "error"}}
	if !errs.HasErrors() {
		t.Error("expected non-empty ValidationErrors to have errors")
	}
}

func TestValidationErrors_Fields(t *testing.T) {
	errs := ValidationErrors{
		{Field: "name", Message: "is required"},
		{Field: "name", Message: "is too short"}, // Second error for same field
		{Field: "email", Message: "is invalid"},
	}

	fields := errs.Fields()

	if len(fields) != 2 {
		t.Errorf("expected 2 fields, got %d", len(fields))
	}
	if fields["name"] != "is required" {
		t.Errorf("expected name error to be 'is required', got %q", fields["name"])
	}
	if fields["email"] != "is invalid" {
		t.Errorf("expected email error to be 'is invalid', got %q", fields["email"])
	}
}

func TestValidator_Required(t *testing.T) {
	tests := []struct {
		name    string
		value   string
		wantErr bool
	}{
		{"valid", "hello", false},
		{"empty", "", true},
		{"whitespace only", "   ", true},
		{"with whitespace", "  hello  ", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			v := New()
			v.Required(tt.value, "field")
			if (v.Error() != nil) != tt.wantErr {
				t.Errorf("Required(%q) error = %v, wantErr %v", tt.value, v.Error(), tt.wantErr)
			}
		})
	}
}

func TestValidator_RequiredSlice(t *testing.T) {
	tests := []struct {
		name    string
		value   []string
		wantErr bool
	}{
		{"valid", []string{"a", "b"}, false},
		{"single item", []string{"a"}, false},
		{"empty", []string{}, true},
		{"nil", nil, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			v := New()
			v.RequiredSlice(tt.value, "field")
			if (v.Error() != nil) != tt.wantErr {
				t.Errorf("RequiredSlice(%v) error = %v, wantErr %v", tt.value, v.Error(), tt.wantErr)
			}
		})
	}
}

func TestValidator_StringLength(t *testing.T) {
	tests := []struct {
		name    string
		value   string
		min     int
		max     int
		wantErr bool
		errMsg  string
	}{
		{"valid", "hello", 1, 10, false, ""},
		{"exact min", "ab", 2, 10, false, ""},
		{"exact max", "hello", 1, 5, false, ""},
		{"too short", "a", 2, 10, true, "must be at least 2 characters"},
		{"too long", "hello world", 1, 5, true, "must be at most 5 characters"},
		{"no min check", "", 0, 10, false, ""},
		{"no max check", "very long string", 1, 0, false, ""},
		{"empty with min", "", 1, 10, true, "must be at least 1 characters"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			v := New()
			v.StringLength(tt.value, "field", tt.min, tt.max)
			err := v.Error()
			if (err != nil) != tt.wantErr {
				t.Errorf("StringLength(%q, %d, %d) error = %v, wantErr %v", tt.value, tt.min, tt.max, err, tt.wantErr)
			}
			if tt.wantErr && tt.errMsg != "" {
				verr := v.errors[0]
				if verr.Message != tt.errMsg {
					t.Errorf("expected message %q, got %q", tt.errMsg, verr.Message)
				}
			}
		})
	}
}

func TestValidator_Pattern(t *testing.T) {
	alphaOnly := regexp.MustCompile(`^[a-zA-Z]+$`)

	tests := []struct {
		name    string
		value   string
		wantErr bool
	}{
		{"valid", "hello", false},
		{"empty (skipped)", "", false},
		{"invalid", "hello123", true},
		{"with spaces", "hello world", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			v := New()
			v.Pattern(tt.value, "field", alphaOnly, "must contain only letters")
			if (v.Error() != nil) != tt.wantErr {
				t.Errorf("Pattern(%q) error = %v, wantErr %v", tt.value, v.Error(), tt.wantErr)
			}
		})
	}
}

func TestValidator_Custom(t *testing.T) {
	tests := []struct {
		name    string
		valid   bool
		wantErr bool
	}{
		{"valid", true, false},
		{"invalid", false, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			v := New()
			v.Custom(tt.valid, "field", "custom error")
			if (v.Error() != nil) != tt.wantErr {
				t.Errorf("Custom(%v) error = %v, wantErr %v", tt.valid, v.Error(), tt.wantErr)
			}
		})
	}
}

func TestValidator_AddError(t *testing.T) {
	v := New()
	v.AddError("field", "custom message")

	if !v.HasErrors() {
		t.Error("expected validator to have errors after AddError")
	}

	err := v.Error()
	if err == nil {
		t.Fatal("expected non-nil error")
	}

	verrs := err.(ValidationErrors)
	if len(verrs) != 1 || verrs[0].Field != "field" || verrs[0].Message != "custom message" {
		t.Errorf("unexpected error: %v", verrs)
	}
}

func TestValidator_Composable(t *testing.T) {
	// Test chaining multiple validations
	v := New()
	v.Required("test", "name").
		StringLength("test", "name", 1, 64).
		Pattern("test", "name", AlphanumericDashUnderscore, "must be alphanumeric")

	if v.HasErrors() {
		t.Errorf("expected no errors, got %v", v.Error())
	}

	// Test with failures
	v2 := New()
	v2.Required("", "name").
		Required("", "email").
		StringLength("verylongnamethatshouldexceedlimit", "description", 0, 10)

	if !v2.HasErrors() {
		t.Error("expected validation errors")
	}

	errs := v2.Errors()
	if len(errs) != 3 {
		t.Errorf("expected 3 errors, got %d", len(errs))
	}
}

func TestValidator_Errors(t *testing.T) {
	v := New()
	v.Required("", "field1")
	v.Required("", "field2")

	errs := v.Errors()
	if len(errs) != 2 {
		t.Errorf("expected 2 errors, got %d", len(errs))
	}
}

// --- Standalone function tests ---

func TestRequired(t *testing.T) {
	tests := []struct {
		name    string
		value   string
		wantErr bool
	}{
		{"valid", "hello", false},
		{"empty", "", true},
		{"whitespace", "   ", true},
	}

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

func TestRequiredSlice(t *testing.T) {
	if err := RequiredSlice([]string{"a"}, "field"); err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if err := RequiredSlice([]string{}, "field"); err == nil {
		t.Error("expected error for empty slice")
	}
	if err := RequiredSlice(nil, "field"); err == nil {
		t.Error("expected error for nil slice")
	}
}

func TestStringLength(t *testing.T) {
	if err := StringLength("hello", "field", 1, 10); err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if err := StringLength("a", "field", 2, 10); err == nil {
		t.Error("expected error for too short string")
	}
	if err := StringLength("hello world", "field", 1, 5); err == nil {
		t.Error("expected error for too long string")
	}
}

func TestPattern(t *testing.T) {
	alphaOnly := regexp.MustCompile(`^[a-zA-Z]+$`)

	if err := Pattern("hello", "field", alphaOnly, "must be alpha"); err != nil {
		t.Errorf("expected no error, got %v", err)
	}
	if err := Pattern("hello123", "field", alphaOnly, "must be alpha"); err == nil {
		t.Error("expected error for invalid pattern")
	}
	// Empty string should pass (use Required for that check)
	if err := Pattern("", "field", alphaOnly, "must be alpha"); err != nil {
		t.Errorf("expected no error for empty string, got %v", err)
	}
}

// --- Common patterns tests ---

func TestCommonPatterns(t *testing.T) {
	tests := []struct {
		name    string
		pattern *regexp.Regexp
		valid   []string
		invalid []string
	}{
		{
			name:    "AlphanumericDashUnderscore",
			pattern: AlphanumericDashUnderscore,
			valid:   []string{"hello", "hello-world", "hello_world", "HelloWorld123", "a", "A1"},
			invalid: []string{"hello world", "hello.world", "@test", ""},
		},
		{
			name:    "AlphanumericDashUnderscoreDot",
			pattern: AlphanumericDashUnderscoreDot,
			valid:   []string{"hello", "hello.world", "config.yaml", "test-file_v1.2"},
			invalid: []string{"hello world", "@test", "path/to/file"},
		},
		{
			name:    "Email",
			pattern: Email,
			valid:   []string{"test@example.com", "user.name@domain.org", "a@b.co"},
			invalid: []string{"@example.com", "test@", "test@.com", "test"},
		},
		{
			name:    "UUID",
			pattern: UUID,
			valid:   []string{"550e8400-e29b-41d4-a716-446655440000", "ABCD1234-EF56-7890-ABCD-EF1234567890"},
			invalid: []string{"550e8400", "not-a-uuid", "550e8400-e29b-41d4-a716-44665544000"},
		},
		{
			name:    "Slug",
			pattern: Slug,
			valid:   []string{"hello", "hello-world", "my-blog-post", "a1b2"},
			invalid: []string{"Hello", "hello_world", "hello--world", "-hello", "hello-"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			for _, v := range tt.valid {
				if !tt.pattern.MatchString(v) {
					t.Errorf("%s: expected %q to match", tt.name, v)
				}
			}
			for _, v := range tt.invalid {
				if tt.pattern.MatchString(v) {
					t.Errorf("%s: expected %q to not match", tt.name, v)
				}
			}
		})
	}
}

func TestName(t *testing.T) {
	tests := []struct {
		name    string
		value   string
		wantErr bool
	}{
		{"valid", "my-config", false},
		{"valid with underscore", "my_config", false},
		{"valid alphanumeric", "config123", false},
		{"empty", "", true},
		{"too long", "this-name-is-way-too-long-and-exceeds-the-sixty-four-character-limit-allowed", true},
		{"invalid chars", "my config", true},
		{"dots not allowed", "my.config", true},
	}

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

func TestIsValidationError(t *testing.T) {
	tests := []struct {
		name     string
		err      error
		expected bool
	}{
		{"nil", nil, false},
		{"ValidationError", ValidationError{Field: "f", Message: "m"}, true},
		{"ValidationErrors", ValidationErrors{{Field: "f", Message: "m"}}, true},
		{"other error", errOther, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := IsValidationError(tt.err); got != tt.expected {
				t.Errorf("IsValidationError(%v) = %v, want %v", tt.err, got, tt.expected)
			}
		})
	}
}

// Custom error type for testing
type otherError struct{}

func (e otherError) Error() string { return "other error" }

var errOther error = otherError{}

func TestAsValidationErrors(t *testing.T) {
	tests := []struct {
		name        string
		err         error
		expectNil   bool
		expectCount int
	}{
		{"nil", nil, true, 0},
		{"ValidationError", ValidationError{Field: "f", Message: "m"}, false, 1},
		{"ValidationErrors", ValidationErrors{{Field: "f1", Message: "m1"}, {Field: "f2", Message: "m2"}}, false, 2},
		{"other error", errOther, true, 0},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := AsValidationErrors(tt.err)
			if tt.expectNil {
				if got != nil {
					t.Errorf("expected nil, got %v", got)
				}
			} else {
				if got == nil {
					t.Error("expected non-nil ValidationErrors")
				} else if len(got) != tt.expectCount {
					t.Errorf("expected %d errors, got %d", tt.expectCount, len(got))
				}
			}
		})
	}
}

// --- Integration-style tests ---

func TestValidator_RealWorldUsage(t *testing.T) {
	// Simulating the CreateKeyRequest validation from keys.go
	type CreateKeyRequest struct {
		Name   string
		Scopes []string
	}

	validate := func(req CreateKeyRequest) error {
		v := New()
		v.Required(req.Name, "name")
		v.RequiredSlice(req.Scopes, "scopes")
		return v.Error()
	}

	// Valid request
	if err := validate(CreateKeyRequest{Name: "my-key", Scopes: []string{"read"}}); err != nil {
		t.Errorf("expected no error for valid request, got %v", err)
	}

	// Missing name
	if err := validate(CreateKeyRequest{Name: "", Scopes: []string{"read"}}); err == nil {
		t.Error("expected error for missing name")
	}

	// Missing scopes
	if err := validate(CreateKeyRequest{Name: "my-key", Scopes: []string{}}); err == nil {
		t.Error("expected error for missing scopes")
	}

	// Both missing
	err := validate(CreateKeyRequest{Name: "", Scopes: []string{}})
	if err == nil {
		t.Error("expected error for missing name and scopes")
	}
	verrs := AsValidationErrors(err)
	if len(verrs) != 2 {
		t.Errorf("expected 2 validation errors, got %d", len(verrs))
	}
}

func TestValidator_ConfigItemValidation(t *testing.T) {
	// Simulating the ConfigItemRequest validation from claude_config.go
	type ConfigItemRequest struct {
		Name    string
		Content string
	}

	validate := func(req ConfigItemRequest) error {
		v := New()
		v.Required(req.Name, "name")
		v.Required(req.Content, "content")
		v.StringLength(req.Name, "name", 1, 64)
		v.Pattern(req.Name, "name", AlphanumericDashUnderscore, "must be alphanumeric with dashes or underscores")
		return v.Error()
	}

	// Valid request
	if err := validate(ConfigItemRequest{Name: "my-skill", Content: "# Skill content"}); err != nil {
		t.Errorf("expected no error for valid request, got %v", err)
	}

	// Invalid name pattern
	err := validate(ConfigItemRequest{Name: "my skill", Content: "content"})
	if err == nil {
		t.Error("expected error for invalid name pattern")
	}

	// Name too long
	longName := "this-name-is-way-too-long-and-exceeds-the-sixty-four-character-limit"
	err = validate(ConfigItemRequest{Name: longName, Content: "content"})
	if err == nil {
		t.Error("expected error for name too long")
	}
}
