// OKLab/OKLCH <-> sRGB conversion (Björn Ottosson, https://bottosson.github.io/posts/oklab/).
// Self-contained: theme-engine's equivalent math is internal-only, not part of its public API.

export interface Oklab {
  L: number
  a: number
  b: number
}

function linearToByte(c: number): number {
  const x = Math.min(1, Math.max(0, c))
  const v = x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055
  return Math.round(Math.min(1, Math.max(0, v)) * 255)
}

function srgbToLinear(c: number): number {
  const x = c / 255
  return x <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)
}

export function oklchToOklab(L: number, C: number, hDeg: number): Oklab {
  const hr = (hDeg * Math.PI) / 180
  return { L, a: C * Math.cos(hr), b: C * Math.sin(hr) }
}

export function oklabToHex({ L, a, b }: Oklab): string {
  const l_ = L + 0.3963377774 * a + 0.2158037573 * b
  const m_ = L - 0.1055613458 * a - 0.0638541728 * b
  const s_ = L - 0.0894841775 * a - 1.291485548 * b
  const l = l_ ** 3
  const m = m_ ** 3
  const s = s_ ** 3
  const r = +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s
  const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s
  const bl = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
  return (
    '#' +
    [linearToByte(r), linearToByte(g), linearToByte(bl)]
      .map((x) => x.toString(16).padStart(2, '0'))
      .join('')
  )
}

export function oklchToHex(L: number, C: number, hDeg: number): string {
  return oklabToHex(oklchToOklab(L, C, hDeg))
}

export function hexToOklab(hex: string): Oklab {
  const n = hex.replace('#', '')
  const r = srgbToLinear(parseInt(n.slice(0, 2), 16))
  const g = srgbToLinear(parseInt(n.slice(2, 4), 16))
  const b = srgbToLinear(parseInt(n.slice(4, 6), 16))
  const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)
  const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)
  const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)
  return {
    L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
    a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
    b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,
  }
}

/** Euclidean OKLab distance, scaled ×100 to sit on a CIE-ΔE-comparable range. */
export function oklabDeltaE(hexA: string, hexB: string): number {
  const a = hexToOklab(hexA)
  const b = hexToOklab(hexB)
  return 100 * Math.hypot(a.L - b.L, a.a - b.a, a.b - b.b)
}
