import { publicLocaleCatalog } from '../generated/site/locale-catalog.js'

const FALLBACK_DEFAULT_LOCALE = 'en'
const FALLBACK_SUPPORTED_LOCALES = ['en', 'fr', 'es', 'ru', 'de', 'ka', 'it', 'ar', 'he', 'pl', 'tr']

const localeDisplayNames = {
  en: 'English',
  fr: 'Français',
  es: 'Español',
  ru: 'Русский',
  de: 'Deutsch',
  ka: 'ქართული',
  it: 'Italiano',
  ar: 'العربية',
  he: 'עברית',
  pl: 'Polski',
  tr: 'Türkçe',
}

export const DEFAULT_PUBLIC_LOCALE = String(
  publicLocaleCatalog?.defaultLocale || FALLBACK_DEFAULT_LOCALE,
).trim().toLowerCase() || FALLBACK_DEFAULT_LOCALE

const supportedLocales = Array.isArray(publicLocaleCatalog?.supportedLocales)
  && publicLocaleCatalog.supportedLocales.length
  ? publicLocaleCatalog.supportedLocales
  : FALLBACK_SUPPORTED_LOCALES

export const PUBLIC_LOCALES = supportedLocales.map((locale) => ({
  locale,
  code: String(locale).toUpperCase(),
  name: localeDisplayNames[locale] || String(locale).toUpperCase(),
}))

export const NON_DEFAULT_PUBLIC_LOCALES = PUBLIC_LOCALES
  .map((item) => item.locale)
  .filter((locale) => locale !== DEFAULT_PUBLIC_LOCALE)

export const PUBLIC_PAGE_PATHS = Object.fromEntries(
  Object.entries(publicLocaleCatalog?.pagePaths || {}).map(([key, paths]) => [
    key,
    Object.fromEntries(
      Object.entries(paths || {}).filter(([, value]) => typeof value === 'string' && value.trim() !== ''),
    ),
  ]),
)

const supportedLocaleSet = new Set(PUBLIC_LOCALES.map((item) => item.locale))

const legacyLocaleMap = {
  ge: 'ka',
}

const rtlLocales = new Set(['ar', 'he'])

function normalizeBarePath(path) {
  const rawPath = String(path || '').trim() || '/'
  const [pathname] = rawPath.split(/[?#]/)

  return pathname || '/'
}

export function normalizePublicLocale(value) {
  const normalized = String(value || '')
    .trim()
    .toLowerCase()

  if (!normalized) {
    return DEFAULT_PUBLIC_LOCALE
  }

  const mapped = legacyLocaleMap[normalized] || normalized

  return supportedLocaleSet.has(mapped) ? mapped : DEFAULT_PUBLIC_LOCALE
}

export function toPublicLocaleCode(locale) {
  return normalizePublicLocale(locale).toUpperCase()
}

export function isDefaultPublicLocale(locale) {
  return normalizePublicLocale(locale) === DEFAULT_PUBLIC_LOCALE
}

export function publicLocaleDirection(locale) {
  return rtlLocales.has(normalizePublicLocale(locale)) ? 'rtl' : 'ltr'
}

export function detectExplicitPublicLocaleFromPath(path) {
  const segments = normalizeBarePath(path)
    .split('/')
    .filter(Boolean)

  if (!segments.length) {
    return null
  }

  const locale = normalizePublicLocale(segments[0])

  return locale !== DEFAULT_PUBLIC_LOCALE && segments[0].toLowerCase() === locale ? locale : null
}

export function stripPublicLocalePrefix(path) {
  const pathname = normalizeBarePath(path)
  const locale = detectExplicitPublicLocaleFromPath(pathname)

  if (!locale) {
    return pathname
  }

  const suffix = pathname.slice(locale.length + 1)
  return suffix ? (suffix.startsWith('/') ? suffix : `/${suffix}`) : '/'
}

export function withPublicLocalePrefix(path, locale = DEFAULT_PUBLIC_LOCALE) {
  const normalizedLocale = normalizePublicLocale(locale)
  const barePath = stripPublicLocalePrefix(path)

  if (normalizedLocale === DEFAULT_PUBLIC_LOCALE) {
    return barePath
  }

  return barePath === '/'
    ? `/${normalizedLocale}`
    : `/${normalizedLocale}${barePath}`
}

function buildCanonicalPagePathMap() {
  const entries = []

  for (const paths of Object.values(PUBLIC_PAGE_PATHS)) {
    const canonical = stripPublicLocalePrefix(paths?.[DEFAULT_PUBLIC_LOCALE] || '')

    if (!canonical) {
      continue
    }

    for (const path of Object.values(paths || {})) {
      const bare = stripPublicLocalePrefix(path)

      if (!bare) {
        continue
      }

      entries.push([bare, canonical])
    }
  }

  return Object.fromEntries(entries)
}

export const PUBLIC_CANONICAL_PAGE_PATHS = buildCanonicalPagePathMap()

function firstBarePathSegment(path) {
  const barePath = stripPublicLocalePrefix(path)
  const match = barePath.match(/^\/[^/]+/)

  return match?.[0] || null
}

function buildPagePrefixPathMap() {
  const registry = new Map()

  const addPrefix = (canonicalPrefix, locale, localizedPrefix) => {
    if (!canonicalPrefix || canonicalPrefix === '/' || !localizedPrefix || localizedPrefix === '/') {
      return
    }

    const normalizedLocale = normalizePublicLocale(locale)

    if (!registry.has(canonicalPrefix)) {
      registry.set(canonicalPrefix, {})
    }

    registry.get(canonicalPrefix)[normalizedLocale] = localizedPrefix
  }

  for (const paths of Object.values(PUBLIC_PAGE_PATHS)) {
    const canonicalPrefix = firstBarePathSegment(paths?.[DEFAULT_PUBLIC_LOCALE] || '')

    if (!canonicalPrefix) {
      continue
    }

    for (const [locale, path] of Object.entries(paths || {})) {
      addPrefix(canonicalPrefix, locale, firstBarePathSegment(path))
    }
  }

  return Object.fromEntries(registry.entries())
}

export const PUBLIC_PAGE_PREFIX_PATHS = buildPagePrefixPathMap()
export const PUBLIC_DYNAMIC_PREFIX_PATHS = {}
export const PUBLIC_PREFIX_PATHS = Object.fromEntries(
  [...new Set([
    ...Object.keys(PUBLIC_PAGE_PREFIX_PATHS),
  ])].map((canonicalPrefix) => [
    canonicalPrefix,
    {
      ...(PUBLIC_PAGE_PREFIX_PATHS[canonicalPrefix] || {}),
    },
  ]),
)

const PUBLIC_CANONICAL_PREFIX_PATHS = Object.fromEntries(
  Object.entries(PUBLIC_PREFIX_PATHS).flatMap(([canonicalPrefix, localizedPrefixes]) => {
    return Object.values(localizedPrefixes || {})
      .filter((value) => typeof value === 'string' && value.trim() !== '')
      .map((value) => [value, canonicalPrefix])
  }),
)

export function canonicalizePublicSectionBarePath(path) {
  const barePath = stripPublicLocalePrefix(path)
  const match = barePath.match(/^\/([^/]+)(\/.*)?$/)

  if (!match) {
    return barePath
  }

  const canonicalSectionPath = PUBLIC_CANONICAL_PREFIX_PATHS[`/${match[1]}`]

  if (!canonicalSectionPath) {
    return barePath
  }

  return `${canonicalSectionPath}${match[2] || ''}`
}

export function localizedSectionPublicPath(path, locale = DEFAULT_PUBLIC_LOCALE) {
  const canonicalBarePath = canonicalizePublicSectionBarePath(path)
  const match = canonicalBarePath.match(/^\/([^/]+)(\/.*)?$/)

  if (!match) {
    return null
  }

  const normalizedLocale = normalizePublicLocale(locale)
  const canonicalPrefix = `/${match[1]}`
  const localizedSegment = (PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix] && Object.keys(PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix]).length)
    ? PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix]?.[normalizedLocale]
    : PUBLIC_PAGE_PREFIX_PATHS[canonicalPrefix]?.[normalizedLocale] || PUBLIC_PREFIX_PATHS?.[canonicalPrefix]?.[normalizedLocale]

  if (!localizedSegment) {
    return null
  }

  return withPublicLocalePrefix(`${localizedSegment}${match[2] || ''}`, normalizedLocale)
}

export function localizedExactPublicPath(path, locale = DEFAULT_PUBLIC_LOCALE) {
  const barePath = stripPublicLocalePrefix(path)
  const canonicalBarePath = PUBLIC_CANONICAL_PAGE_PATHS[barePath] || barePath

  for (const paths of Object.values(PUBLIC_PAGE_PATHS)) {
    const canonical = stripPublicLocalePrefix(paths?.[DEFAULT_PUBLIC_LOCALE] || '')

    if (canonical !== canonicalBarePath) {
      continue
    }

    return paths?.[normalizePublicLocale(locale)] || paths?.[DEFAULT_PUBLIC_LOCALE] || canonical
  }

  return null
}

export function supportedPublicLocalesForPath(path) {
  const barePath = stripPublicLocalePrefix(path)

  if (!barePath || barePath === '/') {
    return PUBLIC_LOCALES.map((item) => item.locale)
  }

  const canonicalBarePath = PUBLIC_CANONICAL_PAGE_PATHS[barePath] || canonicalizePublicSectionBarePath(barePath)

  for (const paths of Object.values(PUBLIC_PAGE_PATHS)) {
    const canonicalPath = stripPublicLocalePrefix(paths?.[DEFAULT_PUBLIC_LOCALE] || '')

    if (canonicalPath === canonicalBarePath) {
      return Object.keys(paths || {}).map((locale) => normalizePublicLocale(locale))
    }
  }

  const canonicalPrefix = firstBarePathSegment(canonicalBarePath)

  if (!canonicalPrefix) {
    return [DEFAULT_PUBLIC_LOCALE]
  }

  if (barePath.includes('/:') && PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix]) {
    return Object.keys(PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix]).map((locale) => normalizePublicLocale(locale))
  }

  if (PUBLIC_PAGE_PREFIX_PATHS[canonicalPrefix]) {
    return Object.keys(PUBLIC_PAGE_PREFIX_PATHS[canonicalPrefix]).map((locale) => normalizePublicLocale(locale))
  }

  if (PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix]) {
    return Object.keys(PUBLIC_DYNAMIC_PREFIX_PATHS[canonicalPrefix]).map((locale) => normalizePublicLocale(locale))
  }

  return [DEFAULT_PUBLIC_LOCALE]
}
