import { describe, it, expect } from 'vitest'
import { normalizeRules, runRules, type FormRule } from '../src/canonical/composables/form/validators'

const model = {}

describe('normalizeRules', () => {
  it('wraps a single rule into an array', () => {
    expect(normalizeRules({ required: true })).toEqual([{ required: true }])
  })
  it('passes an array through', () => {
    const r: FormRule[] = [{ required: true }, { type: 'email' }]
    expect(normalizeRules(r)).toBe(r)
  })
  it('maps undefined to empty array', () => {
    expect(normalizeRules(undefined)).toEqual([])
  })
})

describe('runRules — required', () => {
  it('fails empty required with default message', () => {
    expect(runRules('', [{ required: true }], model)).toBe('This field is required')
  })
  it('uses custom message', () => {
    expect(runRules('', [{ required: true, message: 'Email required' }], model)).toBe('Email required')
  })
  it('passes a filled required', () => {
    expect(runRules('a', [{ required: true }], model)).toBe('')
  })
  it('skips non-required checks when value is empty', () => {
    expect(runRules('', [{ type: 'email' }], model)).toBe('')
  })
})

describe('runRules — type', () => {
  it('fails non-email', () => {
    expect(runRules('nope', [{ type: 'email' }], model)).toBe('Please enter a valid email')
  })
  it('passes email', () => {
    expect(runRules('a@b.com', [{ type: 'email' }], model)).toBe('')
  })
  it('fails non-integer', () => {
    expect(runRules(1.5, [{ type: 'integer' }], model)).toBe('Please enter a valid integer')
  })
  it('passes integer', () => {
    expect(runRules(3, [{ type: 'integer' }], model)).toBe('')
  })
  it('fails non-url', () => {
    expect(runRules('not a url', [{ type: 'url' }], model)).toBe('Please enter a valid URL')
  })
  it('passes url', () => {
    expect(runRules('https://x.io', [{ type: 'url' }], model)).toBe('')
  })
})

describe('runRules — min/max/len (numeric domain vs string length)', () => {
  it('number below min', () => {
    expect(runRules(0, [{ type: 'integer', min: 1, max: 65535 }], model)).toBe('Must be at least 1')
  })
  it('number above max', () => {
    expect(runRules(70000, [{ type: 'integer', min: 1, max: 65535 }], model)).toBe('Must be at most 65535')
  })
  it('number in range passes', () => {
    expect(runRules(8080, [{ type: 'integer', min: 1, max: 65535 }], model)).toBe('')
  })
  it('string shorter than min length', () => {
    expect(runRules('ab', [{ min: 3 }], model)).toBe('Must be at least 3 characters')
  })
  it('string exact len mismatch', () => {
    expect(runRules('abcd', [{ len: 3 }], model)).toBe('Must be exactly 3 characters')
  })
  it('string exact len match passes', () => {
    expect(runRules('abc', [{ len: 3 }], model)).toBe('')
  })
})

describe('runRules — pattern + custom validator', () => {
  it('fails pattern', () => {
    expect(runRules('abc', [{ pattern: /^\d+$/ }], model)).toBe('Invalid format')
  })
  it('passes pattern', () => {
    expect(runRules('123', [{ pattern: /^\d+$/ }], model)).toBe('')
  })
  it('custom validator returning string fails with that string', () => {
    expect(runRules('x', [{ validator: () => 'bad' }], model)).toBe('bad')
  })
  it('custom validator returning true passes', () => {
    expect(runRules('x', [{ validator: () => true }], model)).toBe('')
  })
  it('custom validator receives value + model', () => {
    const m = { other: 5 }
    const rule: FormRule = { validator: (v, mm) => (v === (mm as { other: number }).other ? true : 'mismatch') }
    expect(runRules(5, [rule], m)).toBe('')
    expect(runRules(6, [rule], m)).toBe('mismatch')
  })
})

describe('runRules — order + short-circuit', () => {
  it('returns the first failing rule error', () => {
    expect(runRules('', [{ required: true }, { type: 'email' }], model)).toBe('This field is required')
  })
})
