import { describe, it, expect } from 'vitest'
import { validatePaletteSet, validateContrast, validateContrastTokens } from './validate'
import { makeValidPaletteSet } from './valid-theme.fixture'

describe('validatePaletteSet', () => {
  it('accepts a complete package', () => {
    const r = validatePaletteSet(makeValidPaletteSet())
    expect(r.ok).toBe(true)
  })
  it('rejects non-object / missing palettes', () => {
    expect(validatePaletteSet(null).ok).toBe(false)
    expect(validatePaletteSet({ id: 'x', name: 'X', palettes: [], defaultPaletteId: 'p', shape: {} }).ok).toBe(false)
  })
  it('rejects when defaultPaletteId does not resolve', () => {
    const pkg = makeValidPaletteSet()
    pkg.defaultPaletteId = 'nope'
    const r = validatePaletteSet(pkg)
    expect(r.ok).toBe(false)
    if (!r.ok) expect(r.error.some((e) => e.code === 'THEME_VALIDATION')).toBe(true)
  })
  it('rejects non-kebab / duplicate ids', () => {
    const pkg = makeValidPaletteSet()
    pkg.palettes[0]!.id = 'Not_Kebab'
    pkg.defaultPaletteId = 'Not_Kebab'
    expect(validatePaletteSet(pkg).ok).toBe(false)
  })
  it('rejects a missing color key in one mode', () => {
    const pkg = makeValidPaletteSet()
    delete (pkg.palettes[0]!.colors.dark as Record<string, string>).accent
    expect(validatePaletteSet(pkg).ok).toBe(false)
  })
  it('FAIL-CLOSED: a non-hex color value is a THEME_COLOR_FORMAT error', () => {
    const pkg = makeValidPaletteSet()
    pkg.palettes[0]!.colors.light.accent = 'oklch(0.5 0.1 250)'
    const r = validatePaletteSet(pkg)
    expect(r.ok).toBe(false)
    if (!r.ok) expect(r.error.some((e) => e.code === 'THEME_COLOR_FORMAT')).toBe(true)
  })
  it('rejects a missing shape key', () => {
    const pkg = makeValidPaletteSet()
    delete (pkg.shape.light as Record<string, string>)['radius-md']
    expect(validatePaletteSet(pkg).ok).toBe(false)
  })
})

describe('validateContrast (Hard floor)', () => {
  it('PASSES the AA-clean fixture across every declared pair (proves fixture is AA-clean)', () => {
    const report = validateContrast(makeValidPaletteSet())
    if (!report.pass) console.error(JSON.stringify(report.failures, null, 2))
    expect(report.pass).toBe(true)
    expect(report.failures).toEqual([])
  })
  it('FAILS a sub-AA pair, naming palette/mode/fg/bg/ratio/required', () => {
    const pkg = makeValidPaletteSet()
    pkg.palettes[0]!.colors.light.fg = '#cccccc' // light gray text on white bg -> well under 4.5
    const report = validateContrast(pkg)
    expect(report.pass).toBe(false)
    const f = report.failures.find((x) => x.fg === 'fg' && x.bg === 'bg' && x.mode === 'light')
    expect(f).toBeDefined()
    expect(f!.required).toBe(4.5)
    expect(f!.ratio).toBeLessThan(4.5)
    expect(f!.paletteId).toBe('fixture-palette')
  })
  it('uses the 3.0 threshold for ui-level pairs', () => {
    const pkg = makeValidPaletteSet()
    pkg.palettes[0]!.colors.light['border-strong'] = '#ededed' // ~1.2:1 on white -> fails ui/3.0
    const report = validateContrast(pkg)
    const f = report.failures.find((x) => x.fg === 'border-strong' && x.bg === 'bg')
    expect(f?.required).toBe(3.0)
  })
  it('FAIL-CLOSED: an unparseable color counts as a failure (ratio 0), never a silent pass', () => {
    const pkg = makeValidPaletteSet()
    pkg.palettes[0]!.colors.light.fg = 'rgb(0,0,0)'
    const report = validateContrast(pkg)
    expect(report.pass).toBe(false)
    expect(report.failures.some((x) => x.fg === 'fg' && x.ratio === 0)).toBe(true)
  })
})

describe('validateContrastTokens (single-mode override-persist guard)', () => {
  it('PASSES a single AA-clean mode map (the override-persist happy path)', () => {
    const colors = makeValidPaletteSet().palettes[0]!.colors.light
    const report = validateContrastTokens(colors)
    if (!report.pass) console.error(JSON.stringify(report.failures, null, 2))
    expect(report.pass).toBe(true)
  })
  it('FAILS a sub-AA override, naming fg/bg/ratio/required WITHOUT palette/mode', () => {
    const colors = { ...makeValidPaletteSet().palettes[0]!.colors.light, fg: '#cccccc' }
    const report = validateContrastTokens(colors)
    expect(report.pass).toBe(false)
    const f = report.failures.find((x) => x.fg === 'fg' && x.bg === 'bg')
    expect(f).toBeDefined()
    expect(f!.required).toBe(4.5)
    expect(f).not.toHaveProperty('paletteId')
    expect(f).not.toHaveProperty('mode')
  })
  it('accepts a resolved EffectiveTokens (ColorTokens superset) — extra shape keys ignored', () => {
    const colors = makeValidPaletteSet().palettes[0]!.colors.light
    const effective = { ...colors, 'radius-sm': '0.125rem', 'font-weight-bold': '700' }
    expect(validateContrastTokens(effective as typeof colors).pass).toBe(true)
  })
  it('FAIL-CLOSED on an unparseable override value (ratio 0)', () => {
    const colors = { ...makeValidPaletteSet().palettes[0]!.colors.light, accent: 'oklch(0.5 0.1 250)' }
    const report = validateContrastTokens(colors)
    expect(report.pass).toBe(false)
    expect(report.failures.some((x) => x.fg === 'accent-fg' && x.bg === 'accent' && x.ratio === 0)).toBe(true)
  })
})
