import "server-only";
import fs from "node:fs";
import path from "node:path";
import { marked } from "marked";

/**
 * Rehber makaleleri: content/rehber/*.md dosyaları build sırasında okunur ve
 * statik HTML'e gömülür (sayfalar generateStaticParams + dynamicParams=false
 * ile tamamen prerender edilir; runtime'da fs erişimi olmaz).
 *
 * Frontmatter elle parse edilir (yalnızca düz `anahtar: değer` satırları) —
 * 10 makale için gray-matter bağımlılığına gerek yok.
 *
 * ```mermaid çitleri HTML'e gömülmez: makale parçalara bölünür ve şemalar
 * sayfada ürünün ortak el çizimi bileşeniyle (ElCizimiSema) çizilir. Çitin
 * içinde `%% aria:` satırı zorunludur (erişilebilirlik metni), `%% altyazi:`
 * isteğe bağlı figcaption verir; iki satır da mermaid tanımından çıkarılır.
 */

export type RehberOzet = {
  slug: string;
  baslik: string;
  aciklama: string;
  /** ISO tarih — Article JSON-LD datePublished */
  tarih: string;
};

/**
 * Header'daki ⌘K aramasına giden asgari alan seti. `tarih` client'ta hiç
 * okunmadığı için RSC payload'ına taşınmaz (root layout üzerinden her
 * sayfaya serileşiyordu).
 */
export type RehberAramaKaydi = Pick<RehberOzet, "slug" | "baslik" | "aciklama">;

export type RehberSema = {
  /** Mermaid tanımı (aria/altyazi yönergeleri ayıklanmış hâli) */
  tanim: string;
  ariaLabel: string;
  altyazi?: string;
};

export type RehberParca =
  | { tip: "html"; html: string }
  | { tip: "sema"; sema: RehberSema };

export type RehberYazi = RehberOzet & { parcalar: RehberParca[] };

const REHBER_DIZIN = path.join(process.cwd(), "content", "rehber");

// SiteHeader root layout'ta olduğu için getAllRehberler her sayfa
// render/prerender'ında koşuyor; cache'siz hâli sayfa başına 24 dosya okuma +
// 24 marked.parse demekti. Yalnızca prod'da cache'lenir — dev'de içerik
// düzenlenirken taze fs okuması korunur (katalog.ts/db.ts globalThis deseni).
const globalRehber = globalThis as unknown as {
  rehberYazilar?: Map<string, RehberYazi | null>;
  rehberOzetler?: RehberOzet[];
};
const CACHE_AKTIF = process.env.NODE_ENV === "production";

function parseFrontmatter(raw: string): {
  meta: Record<string, string>;
  body: string;
} {
  const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
  if (!match) return { meta: {}, body: raw };
  const meta: Record<string, string> = {};
  for (const satir of match[1].split(/\r?\n/)) {
    const ayrac = satir.indexOf(":");
    if (ayrac === -1) continue;
    const anahtar = satir.slice(0, ayrac).trim();
    const deger = satir
      .slice(ayrac + 1)
      .trim()
      .replace(/^["']|["']$/g, "");
    if (anahtar) meta[anahtar] = deger;
  }
  return { meta, body: raw.slice(match[0].length) };
}

const SEMA_CITI = /^```mermaid[ \t]*\r?\n([\s\S]*?)^```[ \t]*$/gm;

function parseSema(slug: string, ham: string): RehberSema {
  let ariaLabel = "";
  let altyazi: string | undefined;
  const satirlar: string[] = [];
  for (const satir of ham.split(/\r?\n/)) {
    const aria = satir.match(/^%%\s*aria:\s*(.+)$/);
    const alt = satir.match(/^%%\s*altyazi:\s*(.+)$/);
    if (aria) ariaLabel = aria[1].trim();
    else if (alt) altyazi = alt[1].trim();
    else satirlar.push(satir);
  }
  if (!ariaLabel) {
    throw new Error(
      `rehber: ${slug}.md içindeki mermaid şemasında "%% aria: ..." satırı zorunlu`,
    );
  }
  return { tanim: satirlar.join("\n").trim(), ariaLabel, altyazi };
}

function parcala(slug: string, body: string): RehberParca[] {
  const parcalar: RehberParca[] = [];
  let son = 0;
  for (const eslesme of body.matchAll(SEMA_CITI)) {
    const md = body.slice(son, eslesme.index).trim();
    if (md) {
      parcalar.push({
        tip: "html",
        html: marked.parse(md, { gfm: true, async: false }),
      });
    }
    parcalar.push({ tip: "sema", sema: parseSema(slug, eslesme[1]) });
    son = eslesme.index + eslesme[0].length;
  }
  const kalan = body.slice(son).trim();
  if (kalan) {
    parcalar.push({
      tip: "html",
      html: marked.parse(kalan, { gfm: true, async: false }),
    });
  }
  return parcalar;
}

function metaDogrula(
  slug: string,
  meta: Record<string, string>,
): asserts meta is { baslik: string; aciklama: string; tarih: string } {
  if (!meta.baslik || !meta.aciklama || !meta.tarih) {
    throw new Error(
      `rehber: ${slug}.md frontmatter eksik (baslik/aciklama/tarih zorunlu)`,
    );
  }
}

function oku(slug: string): RehberYazi | null {
  if (CACHE_AKTIF) {
    const onbellek = globalRehber.rehberYazilar?.get(slug);
    if (onbellek !== undefined) return onbellek;
  }
  const dosya = path.join(REHBER_DIZIN, `${slug}.md`);
  let yazi: RehberYazi | null = null;
  if (fs.existsSync(dosya)) {
    const { meta, body } = parseFrontmatter(fs.readFileSync(dosya, "utf8"));
    metaDogrula(slug, meta);
    yazi = {
      slug,
      baslik: meta.baslik,
      aciklama: meta.aciklama,
      tarih: meta.tarih,
      parcalar: parcala(slug, body),
    };
  }
  if (CACHE_AKTIF) {
    (globalRehber.rehberYazilar ??= new Map()).set(slug, yazi);
  }
  return yazi;
}

/**
 * Liste görünümleri için fast-path: yalnızca frontmatter parse edilir,
 * `parcala()`/`marked.parse` hiç çağrılmaz (özetler `parcalar`ı kullanmıyor).
 * Tam parse zaten cache'teyse dosyaya inmeden onu kırpar.
 */
function ozetOku(slug: string): RehberOzet | null {
  const tam = CACHE_AKTIF ? globalRehber.rehberYazilar?.get(slug) : undefined;
  if (tam !== undefined) {
    if (tam === null) return null;
    const { slug: s, baslik, aciklama, tarih } = tam;
    return { slug: s, baslik, aciklama, tarih };
  }
  const dosya = path.join(REHBER_DIZIN, `${slug}.md`);
  if (!fs.existsSync(dosya)) return null;
  const { meta } = parseFrontmatter(fs.readFileSync(dosya, "utf8"));
  metaDogrula(slug, meta);
  return {
    slug,
    baslik: meta.baslik,
    aciklama: meta.aciklama,
    tarih: meta.tarih,
  };
}

export function getAllRehberSlugs(): string[] {
  if (!fs.existsSync(REHBER_DIZIN)) return [];
  return fs
    .readdirSync(REHBER_DIZIN)
    .filter((f) => f.endsWith(".md"))
    .map((f) => f.replace(/\.md$/, ""))
    .sort();
}

export function getRehber(slug: string): RehberYazi | null {
  // path traversal koruması: slug yalnızca dosya adı olabilir
  if (!/^[a-z0-9-]+$/.test(slug)) return null;
  return oku(slug);
}

export function getAllRehberler(): RehberOzet[] {
  if (CACHE_AKTIF && globalRehber.rehberOzetler) {
    return globalRehber.rehberOzetler;
  }
  const ozetler = getAllRehberSlugs()
    .map((slug) => ozetOku(slug))
    .filter((y): y is RehberOzet => y !== null)
    .sort((a, b) => b.tarih.localeCompare(a.tarih));
  if (CACHE_AKTIF) globalRehber.rehberOzetler = ozetler;
  return ozetler;
}

/** Header aramasının RSC payload'ına giden kırpılmış liste. */
export function getRehberAramaKayitlari(): RehberAramaKaydi[] {
  return getAllRehberler().map(({ slug, baslik, aciklama }) => ({
    slug,
    baslik,
    aciklama,
  }));
}