import "server-only";
import { getDb, SELECT_COLS, EFEKTIF_SIRA } from "./db";
import type { Program } from "@/types/yokatlas";
import { kategoriBul, type Kategori } from "./kategoriler";
import { uniAdiNormalize } from "./harita";
import { bolumBazAdi, turkishSlugify, trBaslikDuzeni } from "./slug";

/**
 * Programatik SEO sayfalarının veri katmanı (/bolum, /universite, hub'lar).
 *
 * Slug haritaları modül-scope'ta lazy kurulur: DB readonly açıldığı için
 * slugs tablosu yazılamaz; SELECT DISTINCT (1.5K satır) süreç başına bir kez
 * <10ms sürer. Harita değerleri HAM isim/universite dizeleridir — sorgular
 * `WHERE isim IN (?, ...)` ile birebir eşleşip index'e vurur (WHERE içinde
 * TRIM/fonksiyon kullanmak index'i öldürür).
 */

// ---------- Bölüm tarafı ----------

export type BolumOzet = {
  slug: string;
  ad: string;
  programSayisi: number;
  kategoriler: Kategori[];
  lisans: boolean;
  onlisans: boolean;
  /** MIN(COALESCE(sira2025, sira2024)) — en iyi (en küçük) taban sıralaması */
  enIyiSira: number | null;
};

export type BolumStats = {
  toplam: number;
  devlet: number;
  vakif: number;
  /** unitur boş/NULL olan satırlar — vakıf SAYILMAZ, ayrı gösterilir */
  bilinmeyenTur: number;
  minSira: number | null;
  maxSira: number | null;
  minPuan: number | null;
  maxPuan: number | null;
  ilSayisi: number;
};

export type BolumDetay = BolumOzet & {
  lisansProgramlari: Program[];
  onlisansProgramlari: Program[];
  stats: BolumStats;
};

type BolumKayit = { slug: string; ad: string; hamIsimler: string[] };

const globalKatalog = globalThis as unknown as {
  bolumMap?: Map<string, BolumKayit>;
  bolumOzetler?: BolumOzet[];
  uniMap?: Map<string, UniKayit>;
  uniOzetler?: UniOzet[];
};

function bolumMapGetir(): Map<string, BolumKayit> {
  if (!globalKatalog.bolumMap) {
    const rows = getDb()
      .prepare("SELECT DISTINCT isim FROM programs")
      .all() as { isim: string }[];
    const map = new Map<string, BolumKayit>();
    for (const { isim } of rows) {
      const baz = bolumBazAdi(isim);
      if (!baz) continue;
      const slug = turkishSlugify(baz);
      if (!slug) continue;
      const kayit = map.get(slug);
      if (kayit) kayit.hamIsimler.push(isim);
      else map.set(slug, { slug, ad: baz, hamIsimler: [isim] });
    }
    if (map.size === 0) {
      // generateStaticParams boş dizi dönerse build hatası — boş DB'yi erken yakala
      throw new Error("katalog: programs tablosu boş görünüyor");
    }
    globalKatalog.bolumMap = map;
  }
  return globalKatalog.bolumMap;
}

export function getAllBolumler(): BolumOzet[] {
  if (!globalKatalog.bolumOzetler) {
    const map = bolumMapGetir();
    // Tek taramada tüm bölümlerin özeti: 23K satır, süreç başına bir kez
    const rows = getDb()
      .prepare(
        `SELECT isim, onlisans, ${EFEKTIF_SIRA} AS efektif FROM programs`,
      )
      .all() as { isim: string; onlisans: number; efektif: number | null }[];

    const ozet = new Map<string, BolumOzet>();
    const slugByIsim = new Map<string, string>();
    for (const kayit of map.values()) {
      for (const ham of kayit.hamIsimler) slugByIsim.set(ham, kayit.slug);
      ozet.set(kayit.slug, {
        slug: kayit.slug,
        ad: kayit.ad,
        programSayisi: 0,
        kategoriler: kategoriBul(kayit.ad),
        lisans: false,
        onlisans: false,
        enIyiSira: null,
      });
    }
    for (const row of rows) {
      const slug = slugByIsim.get(row.isim);
      if (!slug) continue;
      const b = ozet.get(slug)!;
      b.programSayisi += 1;
      if (row.onlisans === 1) b.onlisans = true;
      else b.lisans = true;
      if (row.efektif != null && (b.enIyiSira == null || row.efektif < b.enIyiSira)) {
        b.enIyiSira = row.efektif;
      }
    }
    globalKatalog.bolumOzetler = [...ozet.values()].sort((a, b) =>
      a.ad.localeCompare(b.ad, "tr-TR"),
    );
  }
  return globalKatalog.bolumOzetler;
}

export function getAllBolumSlugs(): string[] {
  return [...bolumMapGetir().keys()];
}

export function getBolumBySlug(slug: string): BolumDetay | null {
  const kayit = bolumMapGetir().get(slug);
  if (!kayit) return null;

  const placeholders = kayit.hamIsimler.map(() => "?").join(",");
  const programlar = getDb()
    .prepare(
      `SELECT ${SELECT_COLS} FROM programs
       WHERE isim IN (${placeholders})
       ORDER BY (${EFEKTIF_SIRA} IS NULL), ${EFEKTIF_SIRA} ASC`,
    )
    .all(...kayit.hamIsimler) as (Program & { onlisans?: number })[];

  // onlisans SELECT_COLS içinde yok; tur üzerinden ayır (TYT = önlisans)
  const lisansProgramlari = programlar.filter((p) => p.tur !== "TYT");
  const onlisansProgramlari = programlar.filter((p) => p.tur === "TYT");

  const iller = new Set<string>();
  let devlet = 0;
  let vakif = 0;
  let bilinmeyenTur = 0;
  let minSira: number | null = null;
  let maxSira: number | null = null;
  let minPuan: number | null = null;
  let maxPuan: number | null = null;
  for (const p of programlar) {
    if (p.il) iller.add(p.il);
    if (p.unitur === "DEVLET") devlet += 1;
    else if (p.unitur) vakif += 1;
    else bilinmeyenTur += 1;
    const efektif = p.sira2025 ?? p.sira2024;
    if (efektif != null) {
      if (minSira == null || efektif < minSira) minSira = efektif;
      if (maxSira == null || efektif > maxSira) maxSira = efektif;
    }
    if (p.puan2025 != null) {
      if (minPuan == null || p.puan2025 < minPuan) minPuan = p.puan2025;
      if (maxPuan == null || p.puan2025 > maxPuan) maxPuan = p.puan2025;
    }
  }

  const ozet = getAllBolumler().find((b) => b.slug === slug)!;
  return {
    ...ozet,
    lisansProgramlari,
    onlisansProgramlari,
    stats: {
      toplam: programlar.length,
      devlet,
      vakif,
      bilinmeyenTur,
      minSira,
      maxSira,
      minPuan,
      maxPuan,
      ilSayisi: iller.size,
    },
  };
}

/** Aynı kategorideki diğer bölümler, program sayısına göre (kendisi hariç). */
export function ilgiliBolumler(slug: string, limit = 8): BolumOzet[] {
  const hepsi = getAllBolumler();
  const kendi = hepsi.find((b) => b.slug === slug);
  if (!kendi) return [];
  const kendiKategoriler = new Set<string>(kendi.kategoriler);
  return hepsi
    .filter(
      (b) =>
        b.slug !== slug &&
        (kendiKategoriler.size === 0 ||
          b.kategoriler.some((k) => kendiKategoriler.has(k))),
    )
    .sort((a, b) => b.programSayisi - a.programSayisi)
    .slice(0, limit);
}

// ---------- Üniversite tarafı ----------

export type UniOzet = {
  slug: string;
  /** tr-TR başlık düzeninde görünen ad (İstanbul Teknik Üniversitesi) */
  ad: string;
  il: string | null;
  unitur: string | null;
  programSayisi: number;
  fakulteSayisi: number;
};

export type UniDetay = UniOzet & {
  fakulteler: { fakulte: string; programlar: Program[] }[];
  stats: {
    minSira: number | null;
    toplamKontenjan: number;
    devletMi: boolean;
  };
};

type UniKayit = { slug: string; ad: string; hamAdlar: string[] };

function uniMapGetir(): Map<string, UniKayit> {
  if (!globalKatalog.uniMap) {
    const rows = getDb()
      .prepare("SELECT DISTINCT universite FROM programs")
      .all() as { universite: string }[];
    const map = new Map<string, UniKayit>();
    for (const { universite } of rows) {
      const normal = uniAdiNormalize(universite);
      if (!normal) continue;
      const slug = turkishSlugify(normal);
      if (!slug) continue;
      const kayit = map.get(slug);
      if (kayit) kayit.hamAdlar.push(universite);
      else
        map.set(slug, {
          slug,
          ad: trBaslikDuzeni(normal),
          hamAdlar: [universite],
        });
    }
    if (map.size === 0) {
      throw new Error("katalog: programs tablosunda üniversite yok");
    }
    globalKatalog.uniMap = map;
  }
  return globalKatalog.uniMap;
}

export function getAllUniversiteler(): UniOzet[] {
  if (!globalKatalog.uniOzetler) {
    const map = uniMapGetir();
    const rows = getDb()
      .prepare(
        `SELECT universite, MAX(il) AS il, MAX(unitur) AS unitur,
                COUNT(*) AS adet
         FROM programs GROUP BY universite`,
      )
      .all() as {
      universite: string;
      il: string | null;
      unitur: string | null;
      adet: number;
    }[];
    // Fakülte sayısı: aynı üniversitenin iki yazımı ("X ÜNİV." / "X ÜNİV. (İL)")
    // birleştiği için GROUP BY universite üzerinden saymak çift sayardı;
    // (slug, fakulte) çifti üzerinden kesin sayılır.
    const fakulteRows = getDb()
      .prepare(
        "SELECT DISTINCT universite, fakulte FROM programs WHERE fakulte IS NOT NULL",
      )
      .all() as { universite: string; fakulte: string }[];

    const bySlug = new Map<string, UniOzet>();
    const slugByHam = new Map<string, string>();
    for (const kayit of map.values()) {
      for (const ham of kayit.hamAdlar) slugByHam.set(ham, kayit.slug);
      bySlug.set(kayit.slug, {
        slug: kayit.slug,
        ad: kayit.ad,
        il: null,
        unitur: null,
        programSayisi: 0,
        fakulteSayisi: 0,
      });
    }
    for (const row of rows) {
      const slug = slugByHam.get(row.universite);
      if (!slug) continue;
      const u = bySlug.get(slug)!;
      u.programSayisi += row.adet;
      if (!u.il && row.il) u.il = row.il;
      if (!u.unitur && row.unitur) u.unitur = row.unitur;
    }
    const fakulteSetleri = new Map<string, Set<string>>();
    for (const row of fakulteRows) {
      const slug = slugByHam.get(row.universite);
      if (!slug) continue;
      let set = fakulteSetleri.get(slug);
      if (!set) {
        set = new Set();
        fakulteSetleri.set(slug, set);
      }
      set.add(row.fakulte);
    }
    for (const [slug, set] of fakulteSetleri) {
      bySlug.get(slug)!.fakulteSayisi = set.size;
    }
    globalKatalog.uniOzetler = [...bySlug.values()].sort((a, b) =>
      a.ad.localeCompare(b.ad, "tr-TR"),
    );
  }
  return globalKatalog.uniOzetler;
}

export function getAllUniversiteSlugs(): string[] {
  return [...uniMapGetir().keys()];
}

export function getUniversiteBySlug(slug: string): UniDetay | null {
  const kayit = uniMapGetir().get(slug);
  if (!kayit) return null;

  const placeholders = kayit.hamAdlar.map(() => "?").join(",");
  const programlar = getDb()
    .prepare(
      `SELECT ${SELECT_COLS} FROM programs
       WHERE universite IN (${placeholders})
       ORDER BY fakulte, (${EFEKTIF_SIRA} IS NULL), ${EFEKTIF_SIRA} ASC`,
    )
    .all(...kayit.hamAdlar) as Program[];

  const gruplar = new Map<string, Program[]>();
  let minSira: number | null = null;
  let toplamKontenjan = 0;
  for (const p of programlar) {
    const anahtar = p.fakulte ?? "Diğer";
    const liste = gruplar.get(anahtar);
    if (liste) liste.push(p);
    else gruplar.set(anahtar, [p]);
    const efektif = p.sira2025 ?? p.sira2024;
    if (efektif != null && (minSira == null || efektif < minSira)) {
      minSira = efektif;
    }
    toplamKontenjan += p.kontenjan2025 ?? 0;
  }

  const ozet = getAllUniversiteler().find((u) => u.slug === slug)!;
  return {
    ...ozet,
    fakulteler: [...gruplar.entries()]
      .map(([fakulte, programlar]) => ({ fakulte, programlar }))
      .sort((a, b) => a.fakulte.localeCompare(b.fakulte, "tr-TR")),
    stats: {
      minSira,
      toplamKontenjan,
      devletMi: ozet.unitur === "DEVLET",
    },
  };
}

// ---------- Yardımcılar ----------

/** Ham program isminden /bolum sayfası slug'ı (tablolarda çapraz link için). */
export function bolumSlugFromIsim(isim: string): string | null {
  const slug = turkishSlugify(bolumBazAdi(isim));
  return bolumMapGetir().has(slug) ? slug : null;
}

/** Ham üniversite adından /universite sayfası slug'ı. */
export function uniSlugFromAd(universite: string): string | null {
  const slug = turkishSlugify(uniAdiNormalize(universite));
  return uniMapGetir().has(slug) ? slug : null;
}
