import { readFileSync } from 'node:fs'
import { describe, expect, it } from 'vitest'

const tokens = readFileSync(new URL('../src/tokens/index.css', import.meta.url), 'utf8')

function token(name: string, mode: 'light' | 'dark'): string {
  const block = mode === 'light'
    ? tokens.match(/:root \{([\s\S]*?)\n\}/)?.[1]
    : tokens.match(/html\.dark \{([\s\S]*?)\n\}/)?.[1]
  const value = block?.match(new RegExp(`--${name}:\\s*([^;]+)`))?.[1]
  if (!value) throw new Error(`Missing ${mode} --${name}`)
  return value.trim()
}

function parseOklch(value: string): [number, number, number] {
  const match = value.match(/oklch\((\d+(?:\.\d+)?)%\s+((?:\d+)?\.\d+)\s+(\d+(?:\.\d+)?)/)
  if (!match) throw new Error(`Not an OKLCH value: ${value}`)
  return [Number(match[1]) / 100, Number(match[2]), Number(match[3]) * Math.PI / 180]
}

function relativeLuminance(value: string): number {
  const [lightness, chroma, hue] = parseOklch(value)
  const a = chroma * Math.cos(hue)
  const b = chroma * Math.sin(hue)
  const l = lightness + 0.3963377774 * a + 0.2158037573 * b
  const m = lightness - 0.1055613458 * a - 0.0638541728 * b
  const s = lightness - 0.0894841775 * a - 1.291485548 * b
  const l3 = l ** 3
  const m3 = m ** 3
  const s3 = s ** 3
  const linear = [
    4.0767416621 * l3 - 3.3077115913 * m3 + 0.2309699292 * s3,
    -1.2684380046 * l3 + 2.6097574011 * m3 - 0.3413193965 * s3,
    -0.0041960863 * l3 - 0.7034186147 * m3 + 1.707614701 * s3,
  ]
  const encode = (channel: number) => channel <= 0.0031308
    ? 12.92 * channel
    : 1.055 * Math.max(0, channel) ** (1 / 2.4) - 0.055
  const [red, green, blue] = linear.map(encode)
  return 0.2126 * red + 0.7152 * green + 0.0722 * blue
}

function contrastRatio(first: string, second: string): number {
  const a = relativeLuminance(first)
  const b = relativeLuminance(second)
  return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05)
}

describe('computed token contrast', () => {
  it('calculates the dark non-text and faint-text ratios from OKLCH', () => {
    const darkSurface = token('surface', 'dark')
    const darkHover = token('hover', 'dark')
    expect(contrastRatio(token('control-border', 'dark'), darkHover)).toBeCloseTo(3.1223, 3)
    expect(contrastRatio(token('focus-ring', 'dark'), darkHover)).toBeCloseTo(2.9558, 3)
    expect(contrastRatio(token('ink-faint', 'dark'), darkHover)).toBeCloseTo(4.1442, 3)
    expect(contrastRatio(token('control-border', 'dark'), darkSurface)).toBeCloseTo(4.3133, 3)
  })

  it('calculates both light-theme semantic pairs without relying on browser rendering', () => {
    const lightSurface = token('surface', 'light')
    expect(contrastRatio(token('control-border', 'light'), lightSurface)).toBeCloseTo(1.7084, 3)
    expect(contrastRatio(token('focus-ring', 'light'), lightSurface)).toBeCloseTo(1.7421, 3)
  })
})
