import { createContext, Fragment, useContext, useMemo, type ReactNode } from 'react'
import { t, type I18n } from '@platform-modules/i18n'
import { dir } from '@platform-modules/i18n/rtl'

interface I18nContextValue {
  i18n: I18n
  locale: string
}

const I18nContext = createContext<I18nContextValue | null>(null)
const DirectionContext = createContext<'rtl' | 'ltr' | null>(null)

export class I18nProviderError extends Error {
  constructor() {
    super('useTranslations / useLocale / LanguageSwitcher must be used within <I18nProvider>')
    this.name = 'I18nProviderError'
  }
}

export class DirectionProviderError extends Error {
  constructor() {
    super('useDirection must be used within <DirectionProvider>')
    this.name = 'DirectionProviderError'
  }
}

export function I18nProvider({
  i18n,
  locale,
  children,
}: {
  i18n: I18n
  locale: string
  children: ReactNode
}) {
  const value = useMemo(() => ({ i18n, locale }), [i18n, locale])
  return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
}

function useI18nContext(): I18nContextValue {
  const ctx = useContext(I18nContext)
  if (!ctx) throw new I18nProviderError()
  return ctx
}

export function useLocale(): string {
  return useI18nContext().locale
}

export function useT(ns?: string): (key: string, vars?: Record<string, string | number>) => string {
  const { i18n, locale } = useI18nContext()
  return useMemo(() => {
    return (key, vars) => t(i18n, locale, ns ? `${ns}.${key}` : key, vars)
  }, [i18n, locale, ns])
}

export function useTranslations(): (key: string, vars?: Record<string, string | number>) => string {
  return useT()
}

export function DirectionProvider({ children }: { children: ReactNode }) {
  const locale = useLocale()
  const direction = dir(locale)
  return <DirectionContext.Provider value={direction}>{children}</DirectionContext.Provider>
}

export function useDirection(): 'rtl' | 'ltr' {
  const ctx = useContext(DirectionContext)
  if (!ctx) throw new DirectionProviderError()
  return ctx
}

// Native autonym (language name in its own language) via Intl.DisplayNames — no baked
// per-app label data. Construction is the heavy part, so cache by locale (memoize).
// Guard the malformed-tag path: a bad BCP-47 tag throws RangeError, fall back to the code.
const _autonymCache = new Map<string, string>()
function autonym(locale: string): string {
  let name = _autonymCache.get(locale)
  if (name === undefined) {
    try {
      name = new Intl.DisplayNames([locale], { type: 'language' }).of(locale) ?? locale
    } catch {
      name = locale
    }
    _autonymCache.set(locale, name)
  }
  return name
}

export interface LanguageSwitcherProps {
  locales: readonly string[]
  onChange: (locale: string) => void
  /**
   * Escape hatch: render each option yourself. Receives `name` (the autonym), so you can
   * drop any per-app label map. `active` is whether this option is the current locale.
   */
  render?: (props: { locale: string; name: string; active: boolean; select: () => void }) => ReactNode
}

/**
 * Headless language switcher — renders the option controls only, **no landmark**.
 * Wrap it in your own `<nav aria-label="…">` (or another labeled container): the host owns
 * the landmark so it gets a unique, localized name. Emitting a landmark here would nest
 * inside yours, and multiple unlabeled navs on a page are a WCAG landmark failure.
 *
 * Default buttons are labeled by autonym (the language's name in its own language, via
 * `Intl.DisplayNames`), mark the active option with `aria-current`, and set `lang` per
 * option. Use {@link LanguageSwitcherProps.render} to supply your own option markup.
 */
export function LanguageSwitcher({ locales, onChange, render }: LanguageSwitcherProps) {
  const { locale: current } = useI18nContext()
  return (
    <>
      {locales.map((l) => {
        const active = l === current
        const select = () => onChange(l)
        return render ? (
          <Fragment key={l}>{render({ locale: l, name: autonym(l), active, select })}</Fragment>
        ) : (
          <button key={l} type="button" lang={l} aria-current={active ? 'true' : undefined} onClick={select}>
            {autonym(l)}
          </button>
        )
      })}
    </>
  )
}
