package validation

import (
	"strings"
	"testing"
)

func TestValidateRequired(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		value     string
		wantError bool
	}{
		{"Valid non-empty string", "test", false},
		{"Empty string", "", true},
		{"Whitespace only", "   ", true},
		{"Tab only", "\t", true},
		{"Newline only", "\n", true},
		{"Valid with spaces", "hello world", false},
	}

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

func TestValidateLength(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		value     string
		min       int
		max       int
		wantError bool
	}{
		{"Valid length", "hello", 3, 10, false},
		{"Too short", "ab", 3, 10, true},
		{"Too long", "hello world this is too long", 3, 10, true},
		{"Exact minimum", "abc", 3, 10, false},
		{"Exact maximum", "0123456789", 3, 10, false},
		{"No maximum (0)", "very long string here", 3, 0, false},
		{"Whitespace counted correctly", "  test  ", 4, 10, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := v.ValidateLength(tt.value, "test_field", tt.min, tt.max)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateLength() error = %v, wantError %v", err, tt.wantError)
			}
		})
	}
}

func TestValidateEmail(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		email     string
		wantError bool
	}{
		// Valid emails
		{"Valid email", "user@example.com", false},
		{"Valid email with plus", "user+tag@example.com", false},
		{"Valid email with dot", "first.last@example.com", false},
		{"Valid email with hyphen", "user-name@example-domain.com", false},
		{"Valid email with numbers", "user123@example456.com", false},
		{"Valid email with subdomain", "user@sub.example.com", false},

		// Invalid emails
		{"Empty email", "", true},
		{"Whitespace only", "   ", true},
		{"Missing @", "userexample.com", true},
		{"Missing domain", "user@", true},
		{"Missing local part", "@example.com", true},
		{"No TLD", "user@localhost", true},
		{"Consecutive dots in local", "user..name@example.com", true},
		{"Leading dot in local", ".user@example.com", true},
		{"Trailing dot in local", "user.@example.com", true},
		{"Double @", "user@@example.com", true},
		{"Spaces in email", "user name@example.com", true},
		{"Invalid characters", "user<>@example.com", true},
		{"Too long", strings.Repeat("a", 320) + "@example.com", true},
	}

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

func TestValidateURL(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		url       string
		wantError bool
	}{
		// Valid URLs
		{"Valid HTTP URL", "http://example.com", false},
		{"Valid HTTPS URL", "https://example.com", false},
		{"Valid URL with path", "https://example.com/path/to/resource", false},
		{"Valid URL with query", "https://example.com?param=value", false},
		{"Valid URL with port", "https://example.com:8080", false},
		{"Valid URL with subdomain", "https://sub.example.com", false},

		// Invalid URLs
		{"Empty URL", "", true},
		{"Whitespace only", "   ", true},
		{"Missing scheme", "example.com", true},
		{"Invalid scheme", "ftp://example.com", true},
		{"Missing host", "https://", true},
		{"Invalid characters", "https://exam ple.com", true},
		{"Too long", "https://" + strings.Repeat("a", 2048) + ".com", true},
	}

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

func TestValidateHTTPSURL(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		url       string
		wantError bool
	}{
		{"Valid HTTPS URL", "https://example.com", false},
		{"HTTP URL (should fail)", "http://example.com", true},
		{"FTP URL (should fail)", "ftp://example.com", true},
		{"Invalid URL", "not-a-url", true},
	}

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

func TestValidateDomain(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		domain    string
		wantError bool
	}{
		// Valid domains
		{"Valid domain", "example.com", false},
		{"Valid subdomain", "sub.example.com", false},
		{"Valid deep subdomain", "deep.sub.example.com", false},
		{"Valid with hyphen", "my-site.example.com", false},
		{"Valid with numbers", "site123.example456.com", false},

		// Invalid domains
		{"Empty domain", "", true},
		{"Whitespace only", "   ", true},
		{"Too short", "a.b", true},
		{"Too long", strings.Repeat("a", 254) + ".com", true},
		{"Label too long", strings.Repeat("a", 64) + ".example.com", true},
		{"No TLD", "localhost", true},
		{"Leading hyphen", "-example.com", true},
		{"Trailing hyphen", "example-.com", true},
		{"Double dot", "example..com", true},
		{"Leading dot", ".example.com", true},
		{"Trailing dot", "example.com.", true},
		{"Underscore", "my_site.example.com", true},
		{"Spaces", "my site.example.com", true},
	}

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

func TestValidateSlug(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		slug      string
		wantError bool
	}{
		// Valid slugs
		{"Valid slug", "my-company", false},
		{"Valid slug with numbers", "company123", false},
		{"Valid slug all lowercase", "testcompany", false},
		{"Valid slug with multiple hyphens", "my-test-company", false},

		// Invalid slugs
		{"Empty slug", "", true},
		{"Whitespace only", "   ", true},
		{"Too short", "ab", true},
		{"Too long", strings.Repeat("a", 64), true},
		{"Uppercase letters", "MyCompany", true},
		{"Leading hyphen", "-company", true},
		{"Trailing hyphen", "company-", true},
		{"Double hyphen", "my--company", true},
		{"Underscore", "my_company", true},
		{"Spaces", "my company", true},
		{"Special characters", "my@company", true},

		// Reserved slugs
		{"Reserved: api", "api", true},
		{"Reserved: admin", "admin", true},
		{"Reserved: www", "www", true},
		{"Reserved: login", "login", true},
		{"Reserved: register", "register", true},
	}

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

func TestValidateUUID(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		uuid      string
		wantError bool
	}{
		{"Valid UUID v4", "550e8400-e29b-41d4-a716-446655440000", false},
		{"Valid UUID v4 lowercase", "123e4567-e89b-42d3-a456-426614174000", false},
		{"Empty UUID", "", true},
		{"Invalid format", "not-a-uuid", true},
		{"Invalid version", "550e8400-e29b-21d4-a716-446655440000", true},
		{"Missing hyphens", "550e8400e29b41d4a716446655440000", true},
		{"Too short", "550e8400-e29b-41d4-a716", true},
		{"With uppercase", "550E8400-E29B-41D4-A716-446655440000", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := v.ValidateUUID(tt.uuid, "id")
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateUUID() error = %v, wantError %v", err, tt.wantError)
			}
		})
	}
}

func TestValidateEnum(t *testing.T) {
	v := NewValidator()

	allowedValues := []string{"free", "basic", "pro", "enterprise"}

	tests := []struct {
		name      string
		value     string
		wantError bool
	}{
		{"Valid: free", "free", false},
		{"Valid: basic", "basic", false},
		{"Valid: pro", "pro", false},
		{"Valid: enterprise", "enterprise", false},
		{"Invalid: premium", "premium", true},
		{"Invalid: empty", "", true},
		{"Invalid: wrong case", "FREE", true},
		{"Invalid: typo", "basi", true},
	}

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

func TestValidateRange(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		value     int
		min       int
		max       int
		wantError bool
	}{
		{"Valid within range", 5, 1, 10, false},
		{"Valid at minimum", 1, 1, 10, false},
		{"Valid at maximum", 10, 1, 10, false},
		{"Below minimum", 0, 1, 10, true},
		{"Above maximum", 11, 1, 10, true},
		{"No maximum (0)", 1000, 1, 0, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := v.ValidateRange(tt.value, "count", tt.min, tt.max)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateRange() error = %v, wantError %v", err, tt.wantError)
			}
		})
	}
}

func TestValidateNoHTML(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		value     string
		wantError bool
	}{
		{"Plain text", "Hello world", false},
		{"Text with punctuation", "Hello, world!", false},
		{"HTML tag <script>", "<script>alert('xss')</script>", true},
		{"HTML tag <img>", "<img src='x'>", true},
		{"HTML tag <div>", "<div>content</div>", true},
		{"HTML tag <a>", "<a href='#'>link</a>", true},
		{"Less than symbol", "5 < 10", false},
		{"Greater than symbol", "10 > 5", false},
		{"Both symbols", "5 < x < 10", false},
	}

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

func TestSanitizeString(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{"Trim whitespace", "  hello  ", "hello"},
		{"Remove null bytes", "hello\x00world", "helloworld"},
		{"Already clean", "hello", "hello"},
		{"Empty string", "", ""},
		{"Only whitespace", "   ", ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := v.SanitizeString(tt.input)
			if result != tt.expected {
				t.Errorf("SanitizeString() = %q, want %q", result, tt.expected)
			}
		})
	}
}

func TestStripHTML(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{"Remove script tag", "<script>alert('xss')</script>", "alert('xss')"},
		{"Remove div tag", "<div>content</div>", "content"},
		{"Remove multiple tags", "<p>Hello <b>world</b></p>", "Hello world"},
		{"No tags", "plain text", "plain text"},
		{"Empty string", "", ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := v.StripHTML(tt.input)
			if result != tt.expected {
				t.Errorf("StripHTML() = %q, want %q", result, tt.expected)
			}
		})
	}
}

func TestValidateAndSanitizeString(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		input     string
		minLen    int
		maxLen    int
		wantValue string
		wantError bool
	}{
		{"Valid and clean", "hello", 3, 10, "hello", false},
		{"Trim and validate", "  hello  ", 3, 10, "hello", false},
		{"Too short after trim", "  a  ", 3, 10, "", true},
		{"Too long", "hello world this is too long", 3, 10, "", true},
		{"Empty after trim", "   ", 3, 10, "", true},
		{"Valid with null byte removed", "hel\x00lo", 3, 10, "hello", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := v.ValidateAndSanitizeString(tt.input, "test_field", tt.minLen, tt.maxLen)
			if (err != nil) != tt.wantError {
				t.Errorf("ValidateAndSanitizeString() error = %v, wantError %v", err, tt.wantError)
			}
			if !tt.wantError && result != tt.wantValue {
				t.Errorf("ValidateAndSanitizeString() = %q, want %q", result, tt.wantValue)
			}
		})
	}
}

func TestValidatePrintable(t *testing.T) {
	v := NewValidator()

	tests := []struct {
		name      string
		value     string
		wantError bool
	}{
		{"All printable", "Hello World 123!", false},
		{"With tabs and newlines", "Hello\tWorld\n", false},
		{"With control character", "Hello\x01World", true},
		{"With bell character", "Hello\x07", true},
		{"Empty string", "", false},
		{"Unicode printable", "Hello 世界", false},
	}

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