import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
import readingTime from "reading-time";
import type { Locale } from "next-intl";
import { type CategoryId, categorySlug } from "@/config/blog/categories";
import { routing } from "@/i18n/routing";
import "@/config/blog/guard";
import { type Post, type PostMeta, frontmatterSchema } from "./schema";

const LOCALES = ["en", "fr"] as const;

/**
 * Locale prefix honoring `localePrefix: "as-needed"`:
 * the default locale (`en`) is served WITHOUT a `/en` prefix, `fr` is `/fr`.
 */
export function localePrefix(locale: Locale): string {
  return locale === routing.defaultLocale ? "" : `/${locale}`;
}

/** As-needed path for an article, e.g. `/sourcing/<slug>` (en) or `/fr/sourcing/<slug>` (fr). */
export function articlePath(locale: Locale, category: CategoryId, slug: string): string {
  return `${localePrefix(locale)}/${categorySlug(category, locale)}/${slug}`;
}
const BLOG_DIR = path.join(process.cwd(), "content", "blog");

function readPost(postId: string, locale: "en" | "fr"): Post | null {
  const file = path.join(BLOG_DIR, postId, `${locale}.mdx`);
  if (!fs.existsSync(file)) return null;
  const raw = fs.readFileSync(file, "utf8");
  const { data, content } = matter(raw);
  const parsed = frontmatterSchema.safeParse(data);
  if (!parsed.success) {
    throw new Error(
      `[blog] invalid frontmatter in ${postId}/${locale}.mdx: ${parsed.error.message}`,
    );
  }
  const fm = parsed.data;
  return {
    ...fm,
    postId,
    locale,
    readingMinutes: Math.max(1, Math.round(readingTime(content).minutes)),
    body: content,
  };
}

function listPostIds(): string[] {
  if (!fs.existsSync(BLOG_DIR)) return [];
  return fs
    .readdirSync(BLOG_DIR, { withFileTypes: true })
    .filter((d) => d.isDirectory())
    .map((d) => d.name);
}

/** Strict-bilingual invariant: every post must exist in en AND fr, same category. */
function loadAll(): Post[] {
  const posts: Post[] = [];
  for (const postId of listPostIds()) {
    const byLocale = LOCALES.map((l) => readPost(postId, l));
    if (byLocale.some((p) => p === null)) {
      const have = byLocale
        .filter(Boolean)
        .map((p) => p!.locale)
        .join(",");
      throw new Error(`[blog] ${postId} must exist in en+fr (have: ${have || "none"})`);
    }
    const [en, fr] = byLocale as [Post, Post];
    if (en.category !== fr.category) {
      throw new Error(`[blog] ${postId}: category mismatch en=${en.category} fr=${fr.category}`);
    }
    posts.push(en, fr);
  }
  return posts;
}

const ALL = loadAll();

export function getAllPosts(locale: Locale): PostMeta[] {
  return ALL.filter((p) => p.locale === locale && !p.draft).toSorted((a, b) =>
    b.date.localeCompare(a.date),
  );
}

export function getPostsByCategory(locale: Locale, category: CategoryId): PostMeta[] {
  return getAllPosts(locale).filter((p) => p.category === category);
}

export function getFeatured(locale: Locale, n = 3): PostMeta[] {
  const all = getAllPosts(locale); // already date-desc, drafts excluded
  const flagged = all.filter((p) => p.featured);
  return (flagged.length > 0 ? flagged : all).slice(0, n);
}

export function getPost(locale: Locale, category: CategoryId, slug: string): Post | null {
  return (
    ALL.find(
      (p) => p.locale === locale && p.category === category && p.slug === slug && !p.draft,
    ) ?? null
  );
}

export function getRelated(locale: Locale, post: PostMeta, n = 3): PostMeta[] {
  return getPostsByCategory(locale, post.category)
    .filter((p) => p.postId !== post.postId)
    .slice(0, n);
}

/** Hreflang map: same postId across locales -> localized {category slug, post slug}. */
export function getAlternates(
  postId: string,
): Record<"en" | "fr", { categorySlug: string; slug: string }> {
  const out = {} as Record<"en" | "fr", { categorySlug: string; slug: string }>;
  for (const locale of LOCALES) {
    const p = ALL.find((x) => x.postId === postId && x.locale === locale);
    if (!p) throw new Error(`[blog] missing ${locale} for ${postId}`);
    out[locale] = { categorySlug: categorySlug(p.category, locale), slug: p.slug };
  }
  return out;
}

/** generateStaticParams for the article route: localized category + slug per locale. */
export function getArticleParams(): { locale: string; category: string; slug: string }[] {
  return ALL.filter((p) => !p.draft).map((p) => ({
    locale: p.locale,
    category: categorySlug(p.category, p.locale),
    slug: p.slug,
  }));
}
