//go:build test

package validators

import (
	"testing"
)

func TestValidateEmail_Valid(t *testing.T) {
	valid := []string{
		"user@example.com",
		"user.name+tag@example.co.uk",
		"a@b.cd",
		"test@sub.example.org",
		"123@example.com",
	}
	for _, e := range valid {
		if err := ValidateEmail(e); err != nil {
			t.Errorf("expected valid email %q, got error: %v", e, err)
		}
	}
}

func TestValidateEmail_Invalid(t *testing.T) {
	invalid := []string{
		"",
		"not-an-email",
		"@example.com",
		"user@",
		"user@.com",
		"user@example",
		"a@b.c", // TLD too short
	}
	for _, e := range invalid {
		if err := ValidateEmail(e); err == nil {
			t.Errorf("expected invalid email %q to return error", e)
		}
	}
}

func TestValidateEmail_TooLong(t *testing.T) {
	long := string(make([]byte, 255))
	if err := ValidateEmail(long); err == nil {
		t.Error("expected error for email exceeding 254 chars")
	}
}

func TestValidateEmail_WhitespaceTrimmed(t *testing.T) {
	// ValidateEmail trims whitespace, so leading/trailing spaces are acceptable.
	if err := ValidateEmail("  user@example.com  "); err != nil {
		t.Errorf("expected valid after trimming whitespace, got: %v", err)
	}
}

func TestNormalizeGiftCardCode_StripsNonAlphanumeric(t *testing.T) {
	result := NormalizeGiftCardCode("abc-123_xyz!@#")
	if result != "abc123xyz" {
		t.Errorf("expected abc123xyz, got %s", result)
	}
}

func TestNormalizeGiftCardCode_PreservesCase(t *testing.T) {
	result := NormalizeGiftCardCode("aBcDeF123456")
	if result != "aBcDeF123456" {
		t.Errorf("expected aBcDeF123456, got %s", result)
	}
}

func TestNormalizeGiftCardCode_Empty(t *testing.T) {
	result := NormalizeGiftCardCode("")
	if result != "" {
		t.Errorf("expected empty string, got %s", result)
	}
}

func TestNormalizeGiftCardCode_AlreadyClean(t *testing.T) {
	result := NormalizeGiftCardCode("ABCDEF123456")
	if result != "ABCDEF123456" {
		t.Errorf("expected ABCDEF123456, got %s", result)
	}
}

func TestValidateEmail_RejectsSQLInjection(t *testing.T) {
	payloads := []string{
		"' OR '1'='1",
		"admin'--",
		"'; DROP TABLE users;--",
		`" OR 1=1 --`,
		"' OR '1'='1' --",
		"1' OR '1'='1",
		"' UNION SELECT * FROM users --",
		"admin'/*",
	}
	for _, p := range payloads {
		if err := ValidateEmail(p); err == nil {
			t.Errorf("expected SQLi payload %q to be rejected", p)
		}
	}
}

func TestValidateEmail_RejectsXSS(t *testing.T) {
	payloads := []string{
		"<script>alert(1)</script>",
		"<img src=x onerror=alert(1)>",
		"\"><script>alert(1)</script>",
		"javascript:alert(1)",
	}
	for _, p := range payloads {
		if err := ValidateEmail(p); err == nil {
			t.Errorf("expected XSS payload %q to be rejected", p)
		}
	}
}

func TestValidateEmail_RejectsCommandInjection(t *testing.T) {
	payloads := []string{
		"; rm -rf /",
		"| cat /etc/passwd",
		"`id`",
		"$(cat /etc/passwd)",
	}
	for _, p := range payloads {
		if err := ValidateEmail(p); err == nil {
			t.Errorf("expected command injection payload %q to be rejected", p)
		}
	}
}

func TestValidateEmail_RejectsControlChars(t *testing.T) {
	payloads := []string{
		"user@example.com\nX-Injected: header",
		"user@example.com\r\nX-Injected: header",
		"user\x00@example.com",
		"user@ex\tample.com",
	}
	for _, p := range payloads {
		if err := ValidateEmail(p); err == nil {
			t.Errorf("expected control char payload %q to be rejected", p)
		}
	}
}

func TestValidateEmail_ValidMailsAreSafe(t *testing.T) {
	// These are perfectly valid emails that happen to contain
	// characters used in injection attacks — verify they pass.
	valid := []string{
		"safe.sql+select@example.com",
		"safe.xss+script@example.co.uk",
		"drop+table@example.org",
	}
	for _, e := range valid {
		if err := ValidateEmail(e); err != nil {
			t.Errorf("expected safe email %q to be valid, got: %v", e, err)
		}
	}
}

func TestParseCursor_Valid(t *testing.T) {
	tm, id, err := ParseCursor("2026-06-15T10:30:00Z|abc123def456")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if tm.Year() != 2026 || tm.Month() != 6 || tm.Day() != 15 {
		t.Errorf("unexpected time: %v", tm)
	}
	if id != "abc123def456" {
		t.Errorf("expected id 'abc123def456', got %q", id)
	}
}

func TestParseCursor_InvalidFormat(t *testing.T) {
	_, _, err := ParseCursor("not-a-valid-cursor")
	if err == nil {
		t.Fatal("expected error for invalid cursor format, got nil")
	}
}

func TestParseCursor_InvalidTimestamp(t *testing.T) {
	_, _, err := ParseCursor("not-a-time|abc123def456")
	if err == nil {
		t.Fatal("expected error for invalid timestamp, got nil")
	}
}

func TestParseCursor_EmptyCursor(t *testing.T) {
	_, _, err := ParseCursor("")
	if err == nil {
		t.Fatal("expected error for empty cursor, got nil")
	}
}

func TestParseCursor3_Valid(t *testing.T) {
	count, tm, id, err := ParseCursor3("5|2026-06-15T10:30:00Z|abc123def456")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if count != 5 {
		t.Errorf("expected count 5, got %d", count)
	}
	if tm.Year() != 2026 || tm.Month() != 6 || tm.Day() != 15 {
		t.Errorf("unexpected time: %v", tm)
	}
	if id != "abc123def456" {
		t.Errorf("expected id 'abc123def456', got %q", id)
	}
}

func TestParseCursor3_InvalidFormat(t *testing.T) {
	_, _, _, err := ParseCursor3("not-a-valid-cursor")
	if err == nil {
		t.Fatal("expected error for invalid cursor format, got nil")
	}
}

func TestParseCursor3_InvalidCount(t *testing.T) {
	_, _, _, err := ParseCursor3("not-a-number|2026-06-15T10:30:00Z|abc123def456")
	if err == nil {
		t.Fatal("expected error for invalid count, got nil")
	}
}

func TestParseCursor3_InvalidTimestamp(t *testing.T) {
	_, _, _, err := ParseCursor3("5|not-a-time|abc123def456")
	if err == nil {
		t.Fatal("expected error for invalid timestamp, got nil")
	}
}

func TestParseCursor3_EmptyCursor(t *testing.T) {
	_, _, _, err := ParseCursor3("")
	if err == nil {
		t.Fatal("expected error for empty cursor, got nil")
	}
}

func TestParseCursor3_ZeroCount(t *testing.T) {
	count, _, _, err := ParseCursor3("0|2026-06-15T10:30:00Z|abc123def456")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if count != 0 {
		t.Errorf("expected count 0, got %d", count)
	}
}

func TestParseCursor3_LargeCount(t *testing.T) {
	count, _, _, err := ParseCursor3("999|2026-06-15T10:30:00Z|abc123def456")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if count != 999 {
		t.Errorf("expected count 999, got %d", count)
	}
}

func TestParseCursor3_NegativeCount(t *testing.T) {
	_, _, _, err := ParseCursor3("-1|2026-06-15T10:30:00Z|abc123def456")
	if err != nil {
		t.Fatalf("unexpected error for negative count: %v", err)
	}
}
