import { siteRoutes as rawSiteRoutes } from '~/generated/site/site-routes'
import { DEFAULT_PUBLIC_LOCALE, normalizePublicLocale } from '~/utils/public-locales'

const routeGroupCache = new Map()

const siteRouteEntries = Array.isArray(rawSiteRoutes)
  ? rawSiteRoutes
  : (Array.isArray(rawSiteRoutes?.routes) ? rawSiteRoutes.routes : [])

function normalizeRouteLookupValue(value = '') {
  const [pathname = ''] = String(value || '').trim().split(/[?#]/, 1)

  if (!pathname) {
    return ''
  }

  return pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname
}

function uniqueValues(values = []) {
  return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))]
}

function pathSlugSegment(path = '') {
  return normalizeRouteLookupValue(path).split('/').filter(Boolean).at(-1) || ''
}

function normalizeRouteEntry(entry = {}) {
  const entityType = String(entry?.entity_type || entry?.entityType || '').trim().toLowerCase()
  const contentKey = String(entry?.content_key || entry?.contentKey || '').trim()
  const locale = normalizePublicLocale(entry?.locale || DEFAULT_PUBLIC_LOCALE)
  const path = normalizeRouteLookupValue(entry?.path || '')
  const href = String(entry?.href || entry?.canonical || '').trim()
  const canonical = String(entry?.canonical || href).trim()
  const redirectFrom = uniqueValues([
    ...(Array.isArray(entry?.redirectFrom) ? entry.redirectFrom : []),
    ...(Array.isArray(entry?.redirect_from) ? entry.redirect_from : []),
  ]).map((value) => normalizeRouteLookupValue(value)).filter(Boolean)

  return {
    entityType,
    contentKey,
    locale,
    path,
    href,
    canonical,
    xDefault: String(entry?.xDefault || entry?.x_default || '').trim(),
    redirectFrom,
    lastModified: entry?.lastModified || entry?.last_modified || null,
    title: String(entry?.title || '').trim(),
  }
}

function buildRouteGroup(contentKey, entries = []) {
  const localized = entries
    .map((entry) => ({
      locale: entry.locale,
      path: entry.path,
      href: entry.href || entry.canonical || '',
      canonical: entry.canonical || entry.href || '',
      title: entry.title,
      redirectFrom: entry.redirectFrom,
      redirect_from: entry.redirectFrom,
      lastModified: entry.lastModified,
      last_modified: entry.lastModified,
    }))
    .filter((entry) => entry.path || entry.href)

  const defaultLocalized = localized.find((entry) => entry.locale === DEFAULT_PUBLIC_LOCALE) || localized[0] || null
  const xDefault = uniqueValues(entries.map((entry) => entry.xDefault)).at(0) || defaultLocalized?.href || defaultLocalized?.canonical || ''
  const redirectFrom = uniqueValues(entries.flatMap((entry) => entry.redirectFrom || []))

  return {
    contentKey,
    path: defaultLocalized?.path || '',
    canonical: defaultLocalized?.canonical || defaultLocalized?.href || '',
    xDefault,
    x_default: xDefault,
    availableLocales: localized.map((entry) => entry.locale),
    available_locales: localized.map((entry) => entry.locale),
    localized,
    redirectFrom,
    redirect_from: redirectFrom,
  }
}

function buildRegistry(entityType) {
  const normalizedEntityType = String(entityType || '').trim().toLowerCase()
  const groupedEntries = new Map()

  for (const item of siteRouteEntries) {
    const entry = normalizeRouteEntry(item)

    if (!entry.entityType || entry.entityType !== normalizedEntityType || !entry.contentKey) {
      continue
    }

    if (!groupedEntries.has(entry.contentKey)) {
      groupedEntries.set(entry.contentKey, [])
    }

    groupedEntries.get(entry.contentKey).push(entry)
  }

  const byLookup = new Map()

  for (const [contentKey, entries] of groupedEntries.entries()) {
    const group = buildRouteGroup(contentKey, entries)

    for (const candidate of uniqueValues([
      contentKey,
      group.path,
      pathSlugSegment(group.path),
      ...group.redirectFrom,
      ...group.localized.flatMap((entry) => [
        entry.path,
        pathSlugSegment(entry.path),
        ...(entry.redirectFrom || []),
      ]),
    ])) {
      const normalizedCandidate = candidate.startsWith('/') ? normalizeRouteLookupValue(candidate) : candidate

      if (normalizedCandidate) {
        byLookup.set(normalizedCandidate, group)
      }
    }
  }

  return {
    byLookup,
  }
}

function registryFor(entityType) {
  const normalizedEntityType = String(entityType || '').trim().toLowerCase()

  if (!routeGroupCache.has(normalizedEntityType)) {
    routeGroupCache.set(normalizedEntityType, buildRegistry(normalizedEntityType))
  }

  return routeGroupCache.get(normalizedEntityType)
}

function routeLookupCandidates(identifier) {
  if (typeof identifier === 'string') {
    const normalizedPath = normalizeRouteLookupValue(identifier)

    return uniqueValues([
      identifier,
      normalizedPath,
      pathSlugSegment(normalizedPath),
    ])
  }

  return uniqueValues([
    identifier?.contentKey,
    identifier?.content_key,
    identifier?.slug,
    identifier?.publicSlug,
    identifier?.id,
    identifier?.path,
    normalizeRouteLookupValue(identifier?.path),
    pathSlugSegment(identifier?.path),
  ])
}

export function resolveContentRouteGroup(entityType, identifier) {
  if (!identifier) {
    return null
  }

  const registry = registryFor(entityType)

  for (const candidate of routeLookupCandidates(identifier)) {
    const lookupValue = candidate.startsWith('/') ? normalizeRouteLookupValue(candidate) : candidate

    if (registry.byLookup.has(lookupValue)) {
      return registry.byLookup.get(lookupValue) || null
    }
  }

  return null
}

export function resolveContentLocalizedEntry(group, locale = DEFAULT_PUBLIC_LOCALE) {
  if (!group) {
    return null
  }

  const normalizedLocale = normalizePublicLocale(locale)

  return group.localized.find((entry) => entry.locale === normalizedLocale)
    || group.localized.find((entry) => entry.locale === DEFAULT_PUBLIC_LOCALE)
    || group.localized[0]
    || null
}

export function resolveContentRoutePath(group, locale = DEFAULT_PUBLIC_LOCALE, fallbackPath = '') {
  const localizedEntry = resolveContentLocalizedEntry(group, locale)

  if (localizedEntry?.path) {
    return localizedEntry.path
  }

  const defaultEntry = group?.localized?.find((entry) => entry.locale === DEFAULT_PUBLIC_LOCALE)

  if (defaultEntry?.path) {
    return defaultEntry.path
  }

  return normalizeRouteLookupValue(fallbackPath)
}

export function buildContentRouteSeo(group, locale = DEFAULT_PUBLIC_LOCALE, fallbackSeo = {}) {
  const localizedEntry = resolveContentLocalizedEntry(group, locale)
  const hreflang = Array.isArray(fallbackSeo?.hreflang) && fallbackSeo.hreflang.length
    ? fallbackSeo.hreflang
    : (group?.localized || [])
      .filter((entry) => entry.locale && (entry.href || entry.canonical || entry.path))
      .map((entry) => ({
        locale: entry.locale,
        href: entry.href || entry.canonical || entry.path,
      }))

  const xDefault = group?.xDefault
    || group?.x_default
    || fallbackSeo?.xDefault
    || fallbackSeo?.x_default
    || ''

  return {
    ...(fallbackSeo || {}),
    canonical: localizedEntry?.canonical || localizedEntry?.href || fallbackSeo?.canonical || '',
    hreflang,
    xDefault,
    x_default: xDefault,
  }
}
