import { parseFrontmatter } from '@tale/ui/parse-frontmatter';

import frontmatterManifest from '@/app/content/frontmatter.json';
import { ALL_LOCALES, type Locale } from '@/lib/i18n/locales';

interface DocFrontmatter {
  title: string;
  description: string;
  noindex: boolean;
  sidebarTitle?: string;
  icon?: string;
}

interface DocPage {
  /** URL path relative to the locale root, e.g. `platform/agents/concepts`. */
  slug: string;
  locale: Locale;
  frontmatter: DocFrontmatter;
  body: string;
}

interface ManifestEntry {
  slug: string;
  locale: string;
  frontmatter: Record<string, string | boolean>;
}

const ALL_LOCALE_SET: ReadonlySet<string> = new Set(ALL_LOCALES);

function isLocale(value: string): value is Locale {
  return ALL_LOCALE_SET.has(value);
}

function normaliseFrontmatter(
  raw: Record<string, string | boolean>,
): DocFrontmatter {
  return {
    title: typeof raw.title === 'string' ? raw.title : '',
    description: typeof raw.description === 'string' ? raw.description : '',
    noindex: raw.noindex === true,
    sidebarTitle:
      typeof raw.sidebarTitle === 'string' ? raw.sidebarTitle : undefined,
    icon: typeof raw.icon === 'string' ? raw.icon : undefined,
  };
}

// Frontmatter for every page, generated by `scripts/build-search-index.ts` and
// imported synchronously. `getDocPage` resolves titles/icons from this map with
// no body access at all — the sidebar, breadcrumbs, prev/next and locale
// alternates only ever read frontmatter. Bodies live behind `ensureDocBody`.
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- generated manifest, fixed shape
const manifest = frontmatterManifest as Record<string, ManifestEntry>;
const documents = new Map<string, DocPage>();
for (const entry of Object.values(manifest)) {
  if (!isLocale(entry.locale)) continue;
  documents.set(`${entry.locale}:${entry.slug}`, {
    slug: entry.slug,
    locale: entry.locale,
    frontmatter: normaliseFrontmatter(entry.frontmatter),
    body: '',
  });
}

/** Parse a glob path like `…/docs/en/platform/x.md` into a `locale:slug` key. */
function keyFromPath(path: string): string | null {
  const match = /\/docs\/([^/]+)\/(.+)\.mdx?$/.exec(path);
  if (!match) return null;
  const [, locale, slug] = match;
  return isLocale(locale) ? `${locale}:${slug}` : null;
}

// Per-page body loaders keyed by `locale:slug`. A LAZY glob: each markdown file
// is its own dynamic chunk, fetched only when `ensureDocBody` is called for
// that page from the route loader. This MUST stay lazy: an eager glob compiles
// to hoisted static imports that run on module load (pulling the whole content
// tree) regardless of any SSR guard — and dev has no dead-code elimination to
// drop the unused branch. SSR is fine too: `router.load()` awaits the loader,
// so the dynamic import resolves before the page renders during prerender.
const bodyLoaders = new Map<string, () => Promise<string>>();
{
  const lazy = import.meta.glob('../../../../docs/**/*.{md,mdx}', {
    query: '?raw',
    import: 'default',
  }) as unknown as Record<string, () => Promise<string>>;
  for (const [path, load] of Object.entries(lazy)) {
    const key = keyFromPath(path);
    if (key) bodyLoaders.set(key, load);
  }
}

const bodyCache = new Map<string, string>();

// Build the per-locale fallback chain dynamically from `ALL_LOCALES`. For a
// regional code (`xx-YY`), the chain is `[locale, base, 'en']` — but only if
// the base is itself a published locale; otherwise drop straight to English.
// For the English root, just `['en']`; for any other base, `[locale, 'en']`.
function buildFallbackChain(locale: Locale): readonly Locale[] {
  if (locale === 'en') return ['en'];
  const base = locale.split('-')[0];
  if (locale === base) return [locale, 'en'];
  return isLocale(base) ? [locale, base, 'en'] : [locale, 'en'];
}

const FALLBACK_CHAIN = Object.fromEntries(
  ALL_LOCALES.map((locale) => [locale, buildFallbackChain(locale)] as const),
) as Record<Locale, readonly Locale[]>;

/**
 * Resolve a (locale, slug) request to the matching document key, following the
 * locale fallback chain and treating a section root as its `index` file — so
 * `('en', 'platform')` resolves to `platform/index`. Returns null when nothing
 * matches.
 */
function resolveKey(locale: Locale, slug: string): string | null {
  const candidates = [slug];
  if (slug !== 'index' && !slug.endsWith('/index')) {
    candidates.push(slug === '' ? 'index' : `${slug}/index`);
  }
  for (const localeKey of FALLBACK_CHAIN[locale]) {
    for (const candidateSlug of candidates) {
      const key = `${localeKey}:${candidateSlug}`;
      if (documents.has(key)) return key;
    }
  }
  return null;
}

/**
 * Load a single page's body into the cache. Call from a route loader so the
 * body is ready before the page component renders (SSR awaits it during
 * prerender; the client awaits one dynamic import on navigation). Idempotent,
 * and a no-op for a missing page.
 */
export async function ensureDocBody(
  locale: Locale,
  slug: string,
): Promise<void> {
  const key = resolveKey(locale, slug);
  if (!key || bodyCache.has(key)) return;
  const load = bodyLoaders.get(key);
  if (!load) return;
  const raw = await load();
  bodyCache.set(key, parseFrontmatter(raw).content);
}

/**
 * Resolve a doc page by (locale, slug). Frontmatter is always available; the
 * `body` is populated only once {@link ensureDocBody} has run for the page
 * (the route loader does this), and is an empty string otherwise — callers that
 * render the body (the page itself) load it via the loader, while callers that
 * only need frontmatter (sidebar, breadcrumbs, alternates) never trigger a body
 * fetch.
 */
export function getDocPage(locale: Locale, slug: string): DocPage | null {
  const key = resolveKey(locale, slug);
  if (!key) return null;
  const doc = documents.get(key);
  if (!doc) return null;
  const body = bodyCache.get(key);
  return body === undefined ? doc : { ...doc, body };
}
