/**
 * Comprehensive unit tests for common validation utilities
 *
 * Test Coverage:
 * - All primitive validators (UUID, Email, URL, Timestamp)
 * - Schema composition (Pagination, DateRange)
 * - Factory functions (createEnumSchema, createStringSchema, createArraySchema)
 * - Edge cases and attack vectors
 * - Security validations (path traversal, SQL injection, XSS, DoS)
 */

import { describe, it, expect } from 'vitest'
import { z } from 'zod'
import {
  UuidSchema,
  NonEmptyStringSchema,
  EmailSchema,
  UrlSchema,
  PaginationSchema,
  TimestampSchema,
  DateRangeSchema,
  ResourceTypeSchema,
  OriginResourceTypeSchema,
  CredentialNameSchema,
  OrganizationIdSchema,
  OAuthProviderSchema,
  OAuthCodeSchema,
  OAuthStateParamSchema,
  SanitizedStringSchema,
  createEnumSchema,
  createStringSchema,
  createArraySchema,
  createPayloadSizeValidator,
  formatZodValidationError
} from '../validation'

describe('UuidSchema', () => {
  it('accepts valid UUID v4', () => {
    const validUuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'
    expect(UuidSchema.parse(validUuid)).toBe(validUuid)
  })

  it('accepts valid UUID v1', () => {
    const validUuid = '550e8400-e29b-11d4-a716-446655440000'
    expect(UuidSchema.parse(validUuid)).toBe(validUuid)
  })

  it('rejects invalid UUID format', () => {
    expect(() => UuidSchema.parse('not-a-uuid')).toThrow()
    expect(() => UuidSchema.parse('12345')).toThrow()
    expect(() => UuidSchema.parse('')).toThrow()
  })

  it('rejects UUID-like strings with wrong format', () => {
    expect(() => UuidSchema.parse('a0eebc99-9c0b-4ef8-bb6d')).toThrow()
    expect(() => UuidSchema.parse('a0eebc999c0b4ef8bb6d6bb9bd380a11')).toThrow()
  })
})

describe('CredentialNameSchema', () => {
  describe('valid credential names', () => {
    it('accepts lowercase with hyphens (service-env format)', () => {
      expect(CredentialNameSchema.parse('gmail-prod')).toBe('gmail-prod')
      expect(CredentialNameSchema.parse('notion-dev')).toBe('notion-dev')
      expect(CredentialNameSchema.parse('stripe-api-key')).toBe('stripe-api-key')
    })

    it('accepts multi-segment names', () => {
      expect(CredentialNameSchema.parse('notion-dev-2024')).toBe('notion-dev-2024')
      expect(CredentialNameSchema.parse('google-sheets-prod')).toBe('google-sheets-prod')
    })

    it('auto-lowercases input', () => {
      expect(CredentialNameSchema.parse('Gmail-Prod')).toBe('gmail-prod')
      expect(CredentialNameSchema.parse('NOTION-DEV')).toBe('notion-dev')
    })

    it('trims whitespace', () => {
      expect(CredentialNameSchema.parse('  gmail-prod  ')).toBe('gmail-prod')
    })

    it('accepts numbers in segments', () => {
      expect(CredentialNameSchema.parse('api-v2')).toBe('api-v2')
      expect(CredentialNameSchema.parse('s3-bucket-01')).toBe('s3-bucket-01')
    })
  })

  describe('format enforcement', () => {
    it('rejects names without hyphens (must have service-env format)', () => {
      expect(() => CredentialNameSchema.parse('gmailprod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('12345')).toThrow(/must be lowercase/)
    })

    it('rejects underscores', () => {
      expect(() => CredentialNameSchema.parse('gmail_prod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('notion_dev_2024')).toThrow(/must be lowercase/)
    })

    it('rejects sequential hyphens', () => {
      expect(() => CredentialNameSchema.parse('gmail--prod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('notion----dev')).toThrow(/must be lowercase/)
    })

    it('rejects leading or trailing hyphens', () => {
      expect(() => CredentialNameSchema.parse('-gmail-prod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('gmail-prod-')).toThrow(/must be lowercase/)
    })
  })

  describe('SECURITY: path traversal prevention', () => {
    it('rejects path traversal attempts', () => {
      expect(() => CredentialNameSchema.parse('../admin-cred')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('../../secrets')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('./../config')).toThrow(/must be lowercase/)
    })

    it('rejects relative path characters', () => {
      expect(() => CredentialNameSchema.parse('./local-cred')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('../parent')).toThrow(/must be lowercase/)
    })
  })

  describe('SECURITY: special character prevention', () => {
    it('rejects names with spaces', () => {
      expect(() => CredentialNameSchema.parse('gmail prod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('notion dev 2024')).toThrow(/must be lowercase/)
    })

    it('rejects names with special characters', () => {
      expect(() => CredentialNameSchema.parse('gmail@prod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('notion#dev')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('slack$prod')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('attio%dev')).toThrow(/must be lowercase/)
    })

    it('rejects SQL injection attempts', () => {
      expect(() => CredentialNameSchema.parse("' OR '1'='1")).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse("admin'; DROP TABLE credentials;--")).toThrow(/must be lowercase/)
    })

    it('rejects shell injection attempts', () => {
      expect(() => CredentialNameSchema.parse('cred; rm -rf /')).toThrow(/must be lowercase/)
      expect(() => CredentialNameSchema.parse('cred && malicious')).toThrow(/must be lowercase/)
    })
  })

  describe('SECURITY: DoS prevention', () => {
    it('rejects empty names', () => {
      expect(() => CredentialNameSchema.parse('')).toThrow(/required/)
      expect(() => CredentialNameSchema.parse('   ')).toThrow(/required/)
    })

    it('rejects names too long (over 100 chars)', () => {
      const longName = 'a-' + 'b'.repeat(99)
      expect(() => CredentialNameSchema.parse(longName)).toThrow(/too long/)
    })

    it('accepts names at max length (100 chars)', () => {
      // 100 chars: 49 'a' + '-' + 49 'b' + 'c' = a{49}-b{49}c
      const maxName = 'a'.repeat(49) + '-' + 'b'.repeat(49) + 'c'
      expect(CredentialNameSchema.parse(maxName)).toBe(maxName)
    })
  })
})

describe('OrganizationIdSchema', () => {
  it('is an alias for UuidSchema', () => {
    const validUuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'
    expect(OrganizationIdSchema.parse(validUuid)).toBe(validUuid)
  })

  it('rejects invalid UUIDs', () => {
    expect(() => OrganizationIdSchema.parse('not-a-uuid')).toThrow()
  })
})

describe('OAuthProviderSchema', () => {
  it('accepts valid OAuth providers', () => {
    expect(OAuthProviderSchema.parse('dropbox')).toBe('dropbox')
    expect(OAuthProviderSchema.parse('google-sheets')).toBe('google-sheets')
    expect(OAuthProviderSchema.parse('google-calendar')).toBe('google-calendar')
  })

  it('rejects unknown providers', () => {
    expect(() => OAuthProviderSchema.parse('slack')).toThrow()
    expect(() => OAuthProviderSchema.parse('attio')).toThrow()
    expect(() => OAuthProviderSchema.parse('github')).toThrow()
    expect(() => OAuthProviderSchema.parse('invalid')).toThrow()
  })

  it('rejects empty string', () => {
    expect(() => OAuthProviderSchema.parse('')).toThrow()
  })
})

describe('OAuthCodeSchema', () => {
  it('accepts valid OAuth authorization codes', () => {
    const validCode = 'a'.repeat(50)
    expect(OAuthCodeSchema.parse(validCode)).toBe(validCode)
  })

  it('accepts codes at minimum length (10 chars)', () => {
    const minCode = 'a'.repeat(10)
    expect(OAuthCodeSchema.parse(minCode)).toBe(minCode)
  })

  it('accepts codes at maximum length (1000 chars)', () => {
    const maxCode = 'a'.repeat(1000)
    expect(OAuthCodeSchema.parse(maxCode)).toBe(maxCode)
  })

  it('rejects codes too short (DoS prevention)', () => {
    const shortCode = 'a'.repeat(9)
    expect(() => OAuthCodeSchema.parse(shortCode)).toThrow(/too short/)
  })

  it('rejects codes too long (DoS prevention)', () => {
    const longCode = 'a'.repeat(1001)
    expect(() => OAuthCodeSchema.parse(longCode)).toThrow(/too long/)
  })
})

describe('OAuthStateParamSchema', () => {
  it('accepts valid state parameters', () => {
    const validState = 'eyJvcmdhbml6YXRpb25JZCI6InRlc3QifQ=='
    expect(OAuthStateParamSchema.parse(validState)).toBe(validState)
  })

  it('accepts state at minimum length (10 chars)', () => {
    const minState = 'a'.repeat(10)
    expect(OAuthStateParamSchema.parse(minState)).toBe(minState)
  })

  it('accepts state at maximum length (2048 chars)', () => {
    const maxState = 'a'.repeat(2048)
    expect(OAuthStateParamSchema.parse(maxState)).toBe(maxState)
  })

  it('rejects state too short', () => {
    const shortState = 'a'.repeat(9)
    expect(() => OAuthStateParamSchema.parse(shortState)).toThrow(/too short/)
  })

  it('rejects state too long (DoS prevention)', () => {
    const longState = 'a'.repeat(2049)
    expect(() => OAuthStateParamSchema.parse(longState)).toThrow(/too long/)
  })
})

describe('NonEmptyStringSchema', () => {
  it('accepts valid non-empty strings', () => {
    expect(NonEmptyStringSchema.parse('test')).toBe('test')
    expect(NonEmptyStringSchema.parse('a')).toBe('a')
  })

  it('trims whitespace', () => {
    expect(NonEmptyStringSchema.parse('  test  ')).toBe('test')
  })

  it('rejects empty strings', () => {
    expect(() => NonEmptyStringSchema.parse('')).toThrow()
  })

  it('rejects whitespace-only strings', () => {
    // .trim() runs BEFORE .min(1), so '   ' is trimmed to '' which fails min(1)
    const result = NonEmptyStringSchema.safeParse('   ')
    expect(result.success).toBe(false)
  })

  it('accepts strings up to max length (1000 chars)', () => {
    const maxString = 'a'.repeat(1000)
    expect(NonEmptyStringSchema.parse(maxString)).toBe(maxString)
  })

  it('rejects strings over max length (DoS prevention)', () => {
    const tooLong = 'a'.repeat(1001)
    expect(() => NonEmptyStringSchema.parse(tooLong)).toThrow()
  })
})

describe('SanitizedStringSchema', () => {
  it('removes dangerous characters', () => {
    expect(SanitizedStringSchema.parse('hello<script>world')).toBe('helloscriptworld')
    expect(SanitizedStringSchema.parse('test>value')).toBe('testvalue')
    expect(SanitizedStringSchema.parse("test'value")).toBe('testvalue')
    expect(SanitizedStringSchema.parse('test"value')).toBe('testvalue')
  })

  it('removes all dangerous characters in one string', () => {
    expect(SanitizedStringSchema.parse(`<>"'`)).toBe('')
  })

  it('trims whitespace', () => {
    expect(SanitizedStringSchema.parse('  test  ')).toBe('test')
  })

  it('preserves safe characters', () => {
    expect(SanitizedStringSchema.parse('hello-world_123')).toBe('hello-world_123')
    expect(SanitizedStringSchema.parse('test@example.com')).toBe('test@example.com')
  })
})

describe('EmailSchema', () => {
  it('accepts valid email addresses', () => {
    const validEmails = [
      'user@example.com',
      'test.user@example.co.uk',
      'user+tag@example.com',
      'user_name@example-domain.com'
    ]

    validEmails.forEach((email) => {
      expect(EmailSchema.parse(email)).toBe(email)
    })
  })

  it('rejects invalid email formats', () => {
    const invalidEmails = [
      'not-an-email',
      '@example.com',
      'user@',
      'user @example.com',
      'user@example',
      '',
      'user@@example.com'
    ]

    invalidEmails.forEach((email) => {
      expect(() => EmailSchema.parse(email)).toThrow()
    })
  })

  it('prevents email header injection', () => {
    const injectionAttempts = ['user@example.com\nBcc: attacker@evil.com', 'user@example.com\r\nCc: spam@spam.com']

    injectionAttempts.forEach((attempt) => {
      expect(() => EmailSchema.parse(attempt)).toThrow()
    })
  })
})

describe('UrlSchema', () => {
  it('accepts valid HTTP URLs', () => {
    expect(UrlSchema.parse('http://example.com')).toBe('http://example.com')
  })

  it('accepts valid HTTPS URLs', () => {
    const validUrls = [
      'https://example.com',
      'https://example.com/path',
      'https://example.com/path?query=value',
      'https://sub.example.com',
      'https://example.com:8080/path'
    ]

    validUrls.forEach((url) => {
      expect(UrlSchema.parse(url)).toBe(url)
    })
  })

  it('rejects invalid URL formats', () => {
    const invalidUrls = [
      'not-a-url',
      'example.com', // Missing protocol
      ''
    ]

    invalidUrls.forEach((url) => {
      expect(() => UrlSchema.parse(url)).toThrow()
    })
  })

  it('accepts all valid URL schemes (including javascript: and ftp:)', () => {
    // Note: Zod's .url() validator accepts ALL valid URL schemes
    // This includes potentially dangerous ones like javascript:
    // For HTTP/HTTPS only, use a refinement (see example below)
    expect(UrlSchema.parse('ftp://example.com')).toBe('ftp://example.com')
    expect(UrlSchema.parse('javascript:alert(1)')).toBe('javascript:alert(1)')
  })

  it('can be refined for HTTPS-only', () => {
    const SecureUrlSchema = UrlSchema.refine((url) => url.startsWith('https://'), { message: 'HTTPS required' })

    expect(SecureUrlSchema.parse('https://example.com')).toBe('https://example.com')
    expect(() => SecureUrlSchema.parse('http://example.com')).toThrow()
  })
})

describe('PaginationSchema', () => {
  it('accepts valid pagination parameters', () => {
    const result = PaginationSchema.parse({ limit: 20, offset: 0 })
    expect(result).toEqual({ limit: 20, offset: 0 })
  })

  it('coerces string to number', () => {
    const result = PaginationSchema.parse({ limit: '50', offset: '100' })
    expect(result).toEqual({ limit: 50, offset: 100 })
  })

  it('uses default values when not provided', () => {
    const result = PaginationSchema.parse({})
    expect(result).toEqual({ limit: 20, offset: 0 })
  })

  it('rejects limit over 100 (DoS protection)', () => {
    expect(() => PaginationSchema.parse({ limit: 101 })).toThrow()
  })

  it('accepts limit of exactly 100', () => {
    const result = PaginationSchema.parse({ limit: 100 })
    expect(result.limit).toBe(100)
  })

  it('rejects limit of 0', () => {
    expect(() => PaginationSchema.parse({ limit: 0 })).toThrow()
  })

  it('rejects negative offset', () => {
    expect(() => PaginationSchema.parse({ offset: -1 })).toThrow()
  })

  it('can be extended with additional filters', () => {
    const FilteredListSchema = PaginationSchema.extend({
      status: z.enum(['active', 'inactive']),
      search: z.string().optional()
    })

    const result = FilteredListSchema.parse({
      limit: 50,
      offset: 0,
      status: 'active',
      search: 'test'
    })

    expect(result.status).toBe('active')
  })
})

describe('TimestampSchema', () => {
  it('accepts valid ISO 8601 datetime', () => {
    const validTimestamps = ['2025-11-13T10:30:00Z', '2025-01-01T00:00:00.000Z', '2025-12-31T23:59:59.999Z']

    validTimestamps.forEach((timestamp) => {
      expect(TimestampSchema.parse(timestamp)).toBe(timestamp)
    })
  })

  it('rejects invalid datetime formats', () => {
    const invalidTimestamps = ['invalid-date', '2025-01-01 00:00:00', '2025-01-01', '', '01/01/2025']

    invalidTimestamps.forEach((timestamp) => {
      expect(() => TimestampSchema.parse(timestamp)).toThrow()
    })
  })
})

describe('DateRangeSchema', () => {
  it('accepts valid date range', () => {
    const result = DateRangeSchema.parse({
      startDate: '2025-01-01T00:00:00Z',
      endDate: '2025-12-31T23:59:59Z'
    })

    expect(result.startDate).toBe('2025-01-01T00:00:00Z')
    expect(result.endDate).toBe('2025-12-31T23:59:59Z')
  })

  it('can be refined for logical validation (end > start)', () => {
    const ValidatedDateRangeSchema = DateRangeSchema.refine(
      (data) => new Date(data.endDate) > new Date(data.startDate),
      { message: 'End date must be after start date' }
    )

    expect(
      ValidatedDateRangeSchema.parse({
        startDate: '2025-01-01T00:00:00Z',
        endDate: '2025-12-31T23:59:59Z'
      })
    ).toBeTruthy()

    expect(() =>
      ValidatedDateRangeSchema.parse({
        startDate: '2025-12-31T23:59:59Z',
        endDate: '2025-01-01T00:00:00Z'
      })
    ).toThrow()
  })

  it('rejects invalid date formats in range', () => {
    expect(() =>
      DateRangeSchema.parse({
        startDate: 'invalid',
        endDate: '2025-12-31T23:59:59Z'
      })
    ).toThrow()
  })
})

describe('ResourceTypeSchema', () => {
  it('accepts valid resource types', () => {
    expect(ResourceTypeSchema.parse('agent')).toBe('agent')
    expect(ResourceTypeSchema.parse('workflow')).toBe('workflow')
  })

  it('rejects invalid resource types', () => {
    const invalidTypes = ['invalid', 'scheduler', 'api', '']

    invalidTypes.forEach((type) => {
      expect(() => ResourceTypeSchema.parse(type)).toThrow()
    })
  })
})

describe('OriginResourceTypeSchema', () => {
  it('accepts all valid origin types', () => {
    const validOrigins = ['agent', 'workflow', 'scheduler', 'api']

    validOrigins.forEach((origin) => {
      expect(OriginResourceTypeSchema.parse(origin)).toBe(origin)
    })
  })

  it('rejects invalid origin types', () => {
    const invalidOrigins = ['invalid', 'user', 'system', '']

    invalidOrigins.forEach((origin) => {
      expect(() => OriginResourceTypeSchema.parse(origin)).toThrow()
    })
  })
})

describe('createEnumSchema', () => {
  it('creates enum validator', () => {
    const StatusSchema = createEnumSchema(['active', 'inactive', 'pending'])

    expect(StatusSchema.parse('active')).toBe('active')
    expect(StatusSchema.parse('inactive')).toBe('inactive')
    expect(StatusSchema.parse('pending')).toBe('pending')
    expect(() => StatusSchema.parse('invalid')).toThrow()
  })

  it('supports custom error message', () => {
    const StatusSchema = createEnumSchema(['active', 'inactive'], 'Status must be active or inactive')

    expect(StatusSchema.description).toBe('Status must be active or inactive')
  })

  it('works without error message', () => {
    const StatusSchema = createEnumSchema(['on', 'off'])

    expect(StatusSchema.parse('on')).toBe('on')
    expect(StatusSchema.parse('off')).toBe('off')
  })
})

describe('createStringSchema', () => {
  it('creates string with length constraints', () => {
    const UsernameSchema = createStringSchema(3, 20)

    expect(UsernameSchema.parse('abc')).toBe('abc')
    expect(UsernameSchema.parse('a'.repeat(20))).toBe('a'.repeat(20))
    expect(() => UsernameSchema.parse('ab')).toThrow()
    expect(() => UsernameSchema.parse('a'.repeat(21))).toThrow()
  })

  it('trims whitespace', () => {
    const schema = createStringSchema(3, 20)
    const result = schema.parse('  test  ')

    expect(result).toBe('test')
  })

  it('supports field name for description', () => {
    const UsernameSchema = createStringSchema(3, 20, 'Username')

    expect(UsernameSchema.description).toBe('Username (3-20 characters)')
  })

  it('works without field name', () => {
    const schema = createStringSchema(5, 50)

    expect(schema.parse('valid string')).toBe('valid string')
  })
})

describe('createArraySchema', () => {
  it('creates array with size constraints', () => {
    const TagsSchema = createArraySchema(z.string(), 1, 3)

    expect(TagsSchema.parse(['tag1'])).toEqual(['tag1'])
    expect(TagsSchema.parse(['tag1', 'tag2'])).toEqual(['tag1', 'tag2'])
    expect(TagsSchema.parse(['tag1', 'tag2', 'tag3'])).toEqual(['tag1', 'tag2', 'tag3'])
    expect(() => TagsSchema.parse([])).toThrow()
    expect(() => TagsSchema.parse(['t1', 't2', 't3', 't4'])).toThrow()
  })

  it('validates item schema', () => {
    const EmailListSchema = createArraySchema(EmailSchema, 1, 5)

    expect(EmailListSchema.parse(['user@example.com'])).toEqual(['user@example.com'])
    expect(() => EmailListSchema.parse(['invalid'])).toThrow()
  })

  it('supports field name for description', () => {
    const TagsSchema = createArraySchema(z.string(), 1, 10, 'Tags')

    expect(TagsSchema.description).toBe('Tags (1-10 items)')
  })

  it('works with complex item schemas', () => {
    const UserSchema = z.object({
      id: UuidSchema,
      email: EmailSchema
    })

    const UsersArraySchema = createArraySchema(UserSchema, 1, 10)

    const result = UsersArraySchema.parse([{ id: '123e4567-e89b-12d3-a456-426614174000', email: 'user@example.com' }])

    expect(result).toHaveLength(1)
  })
})

describe('createPayloadSizeValidator', () => {
  it('accepts payload under size limit', () => {
    const PayloadSchema = createPayloadSizeValidator(500_000)

    const smallPayload = { data: 'test' }
    expect(PayloadSchema.parse(smallPayload)).toEqual(smallPayload)
  })

  it('rejects payload over size limit', () => {
    const PayloadSchema = createPayloadSizeValidator(100)

    const largePayload = { data: 'x'.repeat(1000) }
    expect(() => PayloadSchema.parse(largePayload)).toThrow()
  })

  it('validates serialized JSON size', () => {
    const PayloadSchema = createPayloadSizeValidator(50)

    expect(PayloadSchema.parse({ a: 'test' })).toEqual({ a: 'test' })
    expect(() => PayloadSchema.parse({ a: 'x'.repeat(100) })).toThrow()
  })

  it('accepts null as valid empty payload', () => {
    const PayloadSchema = createPayloadSizeValidator(100)

    expect(PayloadSchema.parse(null)).toBe(null)
  })
})

describe('Security Integration Tests', () => {
  it('prevents mass assignment with strict mode', () => {
    const CreateUserSchema = z
      .object({
        name: NonEmptyStringSchema,
        email: EmailSchema
      })
      .strict()

    expect(
      CreateUserSchema.parse({
        name: 'John Doe',
        email: 'john@example.com'
      })
    ).toBeTruthy()

    expect(() =>
      CreateUserSchema.parse({
        name: 'John Doe',
        email: 'john@example.com',
        isAdmin: true
      })
    ).toThrow()
  })

  it('combines validators for complex validation', () => {
    const CreateWorkflowSchema = z
      .object({
        workflowId: UuidSchema,
        name: NonEmptyStringSchema.max(100),
        description: NonEmptyStringSchema.max(500).optional(),
        tags: createArraySchema(NonEmptyStringSchema.max(50), 0, 10).optional(),
        webhookUrl: UrlSchema.optional()
      })
      .strict()

    const validInput = {
      workflowId: '123e4567-e89b-12d3-a456-426614174000',
      name: 'My Workflow',
      description: 'Test workflow',
      tags: ['automation', 'test'],
      webhookUrl: 'https://example.com/webhook'
    }

    expect(CreateWorkflowSchema.parse(validInput)).toBeTruthy()
  })

  it('validates pagination with filters', () => {
    const ListWorkflowsSchema = z.object({
      query: PaginationSchema.extend({
        status: z.enum(['active', 'inactive']).optional(),
        createdAfter: TimestampSchema.optional()
      })
    })

    const validQuery = {
      query: {
        limit: '50',
        offset: '0',
        status: 'active',
        createdAfter: '2025-01-01T00:00:00Z'
      }
    }

    const result = ListWorkflowsSchema.parse(validQuery)
    expect(result.query.limit).toBe(50)
    expect(result.query.offset).toBe(0)
  })
})

describe('formatZodValidationError', () => {
  describe('single field errors', () => {
    it('formats single field validation error', () => {
      const schema = z.object({ email: EmailSchema })

      try {
        schema.parse({ email: 'invalid-email' })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.message).toBe('Validation failed on 1 field')
        expect(formatted.fields).toHaveProperty('email')
        expect(formatted.fields.email).toHaveLength(1)
        expect(formatted.fields.email[0]).toContain('Invalid email')
      }
    })
  })

  describe('multiple field errors', () => {
    it('formats multiple field validation errors', () => {
      const schema = z.object({
        email: EmailSchema,
        age: z.number().min(18),
        name: NonEmptyStringSchema
      })

      try {
        schema.parse({ email: 'invalid', age: 15, name: '' })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.message).toBe('Validation failed on 3 fields')
        expect(formatted.fields).toHaveProperty('email')
        expect(formatted.fields).toHaveProperty('age')
        expect(formatted.fields).toHaveProperty('name')
      }
    })

    it('formats refine validation errors on fields', () => {
      // Real-world pattern: custom validation using refine()
      const schema = z.object({
        password: z.string().refine((val) => val.length >= 8, 'Password must be at least 8 characters')
      })

      try {
        schema.parse({ password: 'short' })
      } catch (error) {
        if (error instanceof z.ZodError) {
          const formatted = formatZodValidationError(error)

          expect(formatted.fields.password).toHaveLength(1)
          expect(formatted.fields.password[0]).toContain('8 characters')
        } else {
          throw error
        }
      }
    })
  })

  describe('nested object errors', () => {
    it('formats nested field paths with dot notation', () => {
      const schema = z.object({
        user: z.object({
          profile: z.object({
            email: EmailSchema
          })
        })
      })

      try {
        schema.parse({ user: { profile: { email: 'invalid' } } })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('user.profile.email')
        expect(formatted.fields['user.profile.email'][0]).toContain('Invalid email')
      }
    })

    it('formats multiple nested errors', () => {
      const schema = z.object({
        user: z.object({
          email: EmailSchema,
          profile: z.object({
            age: z.number().min(18)
          })
        })
      })

      try {
        schema.parse({ user: { email: 'bad', profile: { age: 15 } } })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.message).toBe('Validation failed on 2 fields')
        expect(formatted.fields).toHaveProperty('user.email')
        expect(formatted.fields).toHaveProperty('user.profile.age')
      }
    })

    it('formats refine errors on nested fields', () => {
      // Real-world pattern: nested object with custom validation
      const schema = z.object({
        user: z.object({
          age: z.number().refine((val) => val >= 18, 'Must be 18 or older')
        })
      })

      try {
        schema.parse({ user: { age: 15 } })
      } catch (error) {
        if (error instanceof z.ZodError) {
          const formatted = formatZodValidationError(error)

          expect(formatted.fields).toHaveProperty('user.age')
          expect(formatted.fields['user.age'][0]).toContain('18 or older')
        } else {
          throw error
        }
      }
    })
  })

  describe('array errors', () => {
    it('formats array item validation errors', () => {
      const schema = z.object({
        items: z.array(z.object({ id: UuidSchema }))
      })

      try {
        schema.parse({ items: [{ id: 'invalid' }] })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('items.0.id')
      }
    })

    it('formats multiple array item errors', () => {
      const schema = z.object({
        emails: z.array(EmailSchema)
      })

      try {
        schema.parse({ emails: ['valid@test.com', 'invalid', 'also-invalid'] })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.message).toBe('Validation failed on 2 fields')
        expect(formatted.fields).toHaveProperty('emails.1')
        expect(formatted.fields).toHaveProperty('emails.2')
      }
    })

    it('formats refine errors in array items', () => {
      // Real-world pattern: array items with custom validation
      const ItemSchema = z.object({
        value: z.number().refine((val) => val > 0, 'Value must be positive')
      })

      const schema = z.object({
        items: z.array(ItemSchema)
      })

      try {
        schema.parse({
          items: [
            { value: 10 },
            { value: -5 }, // Invalid
            { value: 20 }
          ]
        })
      } catch (error) {
        if (error instanceof z.ZodError) {
          const formatted = formatZodValidationError(error)

          expect(formatted.fields).toHaveProperty('items.1.value')
          expect(formatted.fields['items.1.value'][0]).toContain('positive')
        } else {
          throw error
        }
      }
    })
  })

  describe('strict mode errors', () => {
    it('formats unknown field errors from strict mode (mass assignment prevention)', () => {
      const schema = z
        .object({
          name: NonEmptyStringSchema
        })
        .strict()

      try {
        schema.parse({ name: 'John', isAdmin: true, role: 'admin' })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        // Zod groups all unrecognized keys into one root error (security feature)
        expect(formatted.message).toContain('Validation failed')
        expect(formatted.fields._root).toBeDefined()
        expect(formatted.fields._root[0]).toContain('Unrecognized key')
      }
    })
  })

  describe('root-level errors', () => {
    it('formats root-level validation errors with _root key', () => {
      const schema = z.string().min(5)

      try {
        schema.parse('abc')
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('_root')
        expect(formatted.fields._root[0]).toContain('5')
      }
    })

    it('formats union type errors', () => {
      const schema = z.union([z.string(), z.number()])

      try {
        schema.parse(true)
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('_root')
      }
    })
  })

  describe('real-world API scenarios', () => {
    it('formats credential creation validation errors', () => {
      const schema = z
        .object({
          name: CredentialNameSchema,
          type: z.enum(['oauth', 'api-key']),
          value: z.record(z.unknown()).refine((val) => Object.keys(val).length > 0, 'Value must not be empty')
        })
        .strict()

      try {
        schema.parse({
          name: '../admin-cred',
          type: 'invalid-type',
          value: {},
          organizationId: 'injected-value'
        })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('name')
        expect(formatted.fields).toHaveProperty('type')
        expect(formatted.fields).toHaveProperty('value')
        expect(formatted.fields).toHaveProperty('_root') // organizationId causes unrecognized key error
      }
    })

    it('formats session turn execution validation errors', () => {
      const schema = z
        .object({
          input: z.unknown().refine((val) => JSON.stringify(val).length <= 10_000, 'Input exceeds 10,000 characters'),
          metadata: z.record(z.unknown()).optional()
        })
        .strict()

      try {
        schema.parse({
          input: { data: 'x'.repeat(20_000) },
          invalidField: 'test'
        })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('input')
        expect(formatted.fields.input[0]).toContain('10,000 characters')
        expect(formatted.fields).toHaveProperty('_root') // invalidField causes unrecognized key error
      }
    })

    it('formats pagination query validation errors', () => {
      const schema = PaginationSchema

      try {
        schema.parse({ limit: '500', offset: '-10' })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('limit')
        expect(formatted.fields).toHaveProperty('offset')
      }
    })
  })

  describe('edge cases', () => {
    it('handles empty error list gracefully', () => {
      // This shouldn't happen in practice, but test defensive coding
      const emptyError = new z.ZodError([])
      const formatted = formatZodValidationError(emptyError)

      expect(formatted.message).toBe('Validation failed on 0 fields')
      expect(formatted.fields).toEqual({})
    })

    it('handles very long field paths', () => {
      const schema = z.object({
        level1: z.object({
          level2: z.object({
            level3: z.object({
              level4: z.object({
                email: EmailSchema
              })
            })
          })
        })
      })

      try {
        schema.parse({
          level1: {
            level2: {
              level3: {
                level4: {
                  email: 'invalid'
                }
              }
            }
          }
        })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)

        expect(formatted.fields).toHaveProperty('level1.level2.level3.level4.email')
      }
    })

    it('uses correct singular/plural in message', () => {
      const schema = z.object({ email: EmailSchema })

      try {
        schema.parse({ email: 'invalid' })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)
        expect(formatted.message).toContain('1 field')
      }

      const multiSchema = z.object({
        email: EmailSchema,
        name: NonEmptyStringSchema
      })

      try {
        multiSchema.parse({ email: 'invalid', name: '' })
      } catch (error) {
        const formatted = formatZodValidationError(error as z.ZodError)
        expect(formatted.message).toContain('2 fields')
      }
    })
  })
})
