import * as React from "react"
import { setupI18n, type AllMessages, type I18n } from "@lingui/core"
import {
  I18nProvider as LinguiI18nProvider,
  type I18nProviderProps as LinguiI18nProviderProps,
} from "@lingui/react"

import { I18nRuntimeContext } from "./context"
import type { LocaleDefinition, LocaleDirection, LocaleInput } from "./types"

const RTL_LANGUAGES = new Set([
  "ar",
  "ckb",
  "dv",
  "fa",
  "he",
  "ku",
  "ps",
  "sd",
  "ug",
  "ur",
  "yi",
])

function inferLocaleDirection(locale: string): LocaleDirection {
  const language = locale.split("-")[0]?.toLowerCase()

  return language && RTL_LANGUAGES.has(language) ? "rtl" : "ltr"
}

function normalizeLocales(
  locale: string,
  locales: readonly LocaleInput[] | undefined,
  catalogs: AllMessages
): readonly LocaleDefinition[] {
  const inputs =
    locales && locales.length > 0
      ? locales
      : Array.from(new Set([locale, ...Object.keys(catalogs)]))

  const definitions = inputs.map<LocaleDefinition>((input) => {
    const definition = typeof input === "string" ? { locale: input } : input

    return {
      ...definition,
      direction:
        definition.direction ?? inferLocaleDirection(definition.locale),
      label: definition.label ?? definition.locale,
    }
  })

  if (!definitions.some((definition) => definition.locale === locale)) {
    return [
      ...definitions,
      {
        direction: inferLocaleDirection(locale),
        label: locale,
        locale,
      },
    ]
  }

  return definitions
}

export interface I18nProviderProps extends Pick<
  LinguiI18nProviderProps,
  "children" | "defaultComponent"
> {
  catalogs?: AllMessages
  i18n?: I18n
  locale: string
  locales?: readonly LocaleInput[]
  missing?: ConstructorParameters<typeof I18n>[0]["missing"]
}

export function I18nProvider({
  catalogs = {},
  children,
  defaultComponent,
  i18n: providedI18n,
  locale,
  locales,
  missing,
}: I18nProviderProps) {
  const i18n = React.useMemo(() => {
    const instance =
      providedI18n ??
      setupI18n({
        locale,
        messages: catalogs,
        missing,
      })

    if (providedI18n) {
      providedI18n.load(catalogs)
      providedI18n.activate(locale)
    }

    return instance
  }, [catalogs, locale, missing, providedI18n])
  const normalizedLocales = React.useMemo(
    () => normalizeLocales(locale, locales, catalogs),
    [catalogs, locale, locales]
  )
  const contextValue = React.useMemo(
    () => ({
      catalogs,
      locale,
      locales: normalizedLocales,
    }),
    [catalogs, locale, normalizedLocales]
  )

  return (
    <I18nRuntimeContext.Provider value={contextValue}>
      <LinguiI18nProvider i18n={i18n} defaultComponent={defaultComponent}>
        {children}
      </LinguiI18nProvider>
    </I18nRuntimeContext.Provider>
  )
}
