import { describe, it, expect } from 'vitest'
import { resolveMode, resolveActiveTheme, mergeOverrides, defaultPaletteSet } from './resolve'
import { makeValidPaletteSet } from './valid-theme.fixture'
import { ThemeOverrideError } from './errors'
import { DEFAULT_RAMPS } from './ramps'
import { validateContrast } from './validate'

describe('resolveMode', () => {
  it('passes through explicit modes', () => {
    expect(resolveMode('light', true)).toBe('light')
    expect(resolveMode('dark', false)).toBe('dark')
  })
  it('resolves system from the OS signal', () => {
    expect(resolveMode('system', true)).toBe('dark')
    expect(resolveMode('system', false)).toBe('light')
  })
})

describe('mergeOverrides', () => {
  it('returns a NEW object, base unmutated', () => {
    const base = { ...makeValidPaletteSet().palettes[0]!.colors.light, ...makeValidPaletteSet().shape.light } as Record<string, string>
    const out = mergeOverrides(base as never, { accent: '#123456' } as never)
    expect(out).not.toBe(base)
    expect((out as Record<string, string>).accent).toBe('#123456')
    expect((base as Record<string, string>).accent).not.toBe('#123456')
  })
  it('throws ThemeOverrideError on an unknown key', () => {
    const base = { fg: '#000000' } as never
    expect(() => mergeOverrides(base, { 'radius-zz': '1px' } as never)).toThrow(ThemeOverrideError)
  })
  it('no-ops with no overrides', () => {
    const base = { fg: '#000000' } as never
    expect(mergeOverrides(base)).toEqual({ fg: '#000000' })
  })
})

describe('resolveActiveTheme', () => {
  const pkg = makeValidPaletteSet()
  it('resolves theme × palette × mode → effective tokens', () => {
    const r = resolveActiveTheme({
      themes: [pkg],
      selection: { themeId: pkg.id, paletteId: pkg.palettes[0]!.id, mode: 'dark' },
      systemPrefersDark: false,
    })
    expect(r.ok).toBe(true)
    if (r.ok) {
      expect(r.value.fg).toBe('#f5f5f5')         // dark mode fg
      expect(r.value['radius-sm']).toBe('0.125rem')
    }
  })
  it('honors system mode via systemPrefersDark', () => {
    const r = resolveActiveTheme({
      themes: [pkg],
      selection: { themeId: pkg.id, paletteId: pkg.palettes[0]!.id, mode: 'system' },
      systemPrefersDark: true,
    })
    if (r.ok) expect(r.value.bg).toBe('#121212')  // resolved to dark
  })
  it('applies overrides', () => {
    const r = resolveActiveTheme({
      themes: [pkg],
      selection: { themeId: pkg.id, paletteId: pkg.palettes[0]!.id, mode: 'light', overrides: { accent: '#abcdef' } },
      systemPrefersDark: false,
    })
    if (r.ok) expect(r.value.accent).toBe('#abcdef')
  })
  it('unknown themeId → typed error (NOT throw, NOT silent default)', () => {
    const r = resolveActiveTheme({ themes: [pkg], selection: { themeId: 'nope', paletteId: 'p', mode: 'light' }, systemPrefersDark: false })
    expect(r.ok).toBe(false)
    if (!r.ok) expect(r.error.code).toBe('THEME_RESOLUTION')
  })
  it('unknown paletteId → typed error', () => {
    const r = resolveActiveTheme({ themes: [pkg], selection: { themeId: pkg.id, paletteId: 'nope', mode: 'light' }, systemPrefersDark: false })
    expect(r.ok).toBe(false)
    if (!r.ok) expect(r.error.code).toBe('THEME_RESOLUTION')
  })
  it('bad override → typed error (caught from mergeOverrides)', () => {
    const r = resolveActiveTheme({
      themes: [pkg],
      selection: { themeId: pkg.id, paletteId: pkg.palettes[0]!.id, mode: 'light', overrides: { bogusKey: 'x' } as never },
      systemPrefersDark: false,
    })
    expect(r.ok).toBe(false)
    if (!r.ok) expect(r.error.code).toBe('THEME_OVERRIDE')
  })
})

describe('defaultPaletteSet', () => {
  it('maps semantic tokens onto the shipped default ramps in both modes', () => {
    expect(defaultPaletteSet.palettes[0]?.colors.light.bg).toBe(DEFAULT_RAMPS.neutral.light.steps[0])
    expect(defaultPaletteSet.palettes[0]?.colors.light.surface).toBe(DEFAULT_RAMPS.neutral.light.steps[1])
    expect(defaultPaletteSet.palettes[0]?.colors.light['surface-raised']).toBe(DEFAULT_RAMPS.neutral.light.steps[2])
    expect(defaultPaletteSet.palettes[0]?.colors.light.accent).toBe(DEFAULT_RAMPS.accent.light.steps[8])
    expect(defaultPaletteSet.palettes[0]?.colors.light['accent-fg']).toBe(DEFAULT_RAMPS.accent.light.contrast)
    expect(defaultPaletteSet.palettes[0]?.colors.dark.bg).toBe(DEFAULT_RAMPS.neutral.dark.steps[0])
    expect(defaultPaletteSet.palettes[0]?.colors.dark.fg).toBe(DEFAULT_RAMPS.neutral.dark.steps[11])
    expect(defaultPaletteSet.palettes[0]?.colors.dark.info).toBe(DEFAULT_RAMPS.info.dark.steps[10])
    expect(defaultPaletteSet.palettes[0]?.colors.dark['info-fg']).toBe(DEFAULT_RAMPS.info.dark.contrast)
  })

  it('passes the contrast gate in both shipped modes', () => {
    expect(validateContrast(defaultPaletteSet)).toEqual({ pass: true, failures: [] })
  })
})
