import type { PaletteSet, ActiveSelection, EffectiveTokens, Result, ColorTokens, ShapeTokens } from './types'
import { ThemeResolutionError, ThemeOverrideError, isThemeError, type ThemeError } from './errors'
import { DEFAULT_RAMPS } from './ramps'

const DEFAULT_SHAPE: ShapeTokens = {
  'radius-none': '0',
  'radius-sm': '0.125rem',
  'radius-md': '0.375rem',
  'radius-lg': '0.5rem',
  'radius-xl': '0.75rem',
  'radius-full': '9999px',
  'font-size-xs': '0.75rem',
  'font-size-sm': '0.875rem',
  'font-size-base': '1rem',
  'font-size-lg': '1.125rem',
  'font-size-xl': '1.25rem',
  'font-size-2xl': '1.5rem',
  'font-size-3xl': '1.875rem',
  'font-weight-normal': '400',
  'font-weight-medium': '500',
  'font-weight-semibold': '600',
  'font-weight-bold': '700',
}

function colorsFor(mode: 'light' | 'dark'): ColorTokens {
  const intentStep = mode === 'light' ? 11 : 10
  const intentContrast = mode === 'light' ? '#ffffff' : null
  return {
    fg: DEFAULT_RAMPS.neutral[mode].steps[11]!,
    'fg-muted': DEFAULT_RAMPS.neutral[mode].steps[10]!,
    'fg-subtle': DEFAULT_RAMPS.neutral[mode].steps[9]!,
    bg: DEFAULT_RAMPS.neutral[mode].steps[0]!,
    surface: DEFAULT_RAMPS.neutral[mode].steps[1]!,
    'surface-raised': DEFAULT_RAMPS.neutral[mode].steps[2]!,
    border: DEFAULT_RAMPS.neutral[mode].steps[5]!,
    'border-strong': DEFAULT_RAMPS.neutral[mode].steps[9]!,
    accent: DEFAULT_RAMPS.accent[mode].steps[8]!,
    'accent-fg': DEFAULT_RAMPS.accent[mode].contrast,
    success: DEFAULT_RAMPS.success[mode].steps[intentStep]!,
    warning: DEFAULT_RAMPS.warning[mode].steps[intentStep]!,
    danger: DEFAULT_RAMPS.danger[mode].steps[intentStep]!,
    info: DEFAULT_RAMPS.info[mode].steps[intentStep]!,
    'success-fg': intentContrast ?? DEFAULT_RAMPS.success[mode].contrast,
    'warning-fg': intentContrast ?? DEFAULT_RAMPS.warning[mode].contrast,
    'danger-fg': intentContrast ?? DEFAULT_RAMPS.danger[mode].contrast,
    'info-fg': intentContrast ?? DEFAULT_RAMPS.info[mode].contrast,
  }
}

export const defaultPaletteSet: PaletteSet = {
  id: 'default-theme',
  name: 'Default Theme',
  palettes: [
    {
      id: 'default',
      name: 'Default',
      colors: {
        light: colorsFor('light'),
        dark: colorsFor('dark'),
      },
    },
  ],
  defaultPaletteId: 'default',
  shape: {
    light: DEFAULT_SHAPE,
    dark: DEFAULT_SHAPE,
  },
}

export function resolveMode(pref: 'light' | 'dark' | 'system', systemPrefersDark: boolean): 'light' | 'dark' {
  if (pref === 'system') return systemPrefersDark ? 'dark' : 'light'
  return pref
}

/** Returns a NEW EffectiveTokens; throws ThemeOverrideError (typed) on an unknown override key. */
export function mergeOverrides(base: EffectiveTokens, overrides?: Partial<EffectiveTokens>): EffectiveTokens {
  if (!overrides) return { ...base }
  for (const key of Object.keys(overrides)) {
    if (!(key in base)) throw new ThemeOverrideError(key)
  }
  return { ...base, ...overrides }
}

export function resolveActiveTheme(args: {
  themes: PaletteSet[]
  selection: ActiveSelection
  systemPrefersDark: boolean
}): Result<EffectiveTokens, ThemeError> {
  const { themes, selection, systemPrefersDark } = args
  const theme = themes.find((t) => t.id === selection.themeId)
  if (!theme) return { ok: false, error: new ThemeResolutionError(`unknown theme: ${selection.themeId}`) }
  const palette = theme.palettes.find((p) => p.id === selection.paletteId)
  if (!palette) return { ok: false, error: new ThemeResolutionError(`unknown palette: ${selection.paletteId}`) }
  const mode = resolveMode(selection.mode, systemPrefersDark)
  const base = { ...palette.colors[mode], ...theme.shape[mode] } as EffectiveTokens
  try {
    return { ok: true, value: mergeOverrides(base, selection.overrides) }
  } catch (e) {
    if (e instanceof ThemeOverrideError) return { ok: false, error: e }
    if (isThemeError(e)) return { ok: false, error: e as ThemeError }
    throw e
  }
}
