/**
 * Tadımlık havuzu batch üretimi: kova × puan türü × (kategori + "genel")
 * kombinasyonları için 1'er örnek tercih satırı (gerekce/riskNotu/trendOzeti)
 * üretip app.db'deki tadimlik_havuzu tablosuna yazar.
 *
 *   pnpm tadimlik                  # boş anahtarları doldurur (idempotent)
 *   pnpm tadimlik --force          # mevcut satırları da yeniden üretir
 *   pnpm tadimlik --kova=10-50k --tur=say --kategori=muhendislik-teknoloji
 *   pnpm tadimlik --mock           # anahtar olsa da mock metin (dev/test)
 *   pnpm tadimlik --bekleme=13000  # LLM çağrıları arası bekleme (ms)
 *
 * AI anahtarı yoksa (dev) mock metin üretir — akış uçtan uca test edilebilir.
 * OpenRouter'da katı bir dakikalık limit yok; --bekleme varsayılanı (500 ms)
 * genelde yeterli, 429 görürsen tempoyu artır.
 * Anonim trafik LLM'e asla istek atmaz; tek maliyet bu script'in koşusudur.
 */
import { randomUUID } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { z } from "zod";
import { appDb, schema } from "../src/lib/appdb";
import { dilimAra } from "../src/lib/db";
import {
  PUAN_TURLERI,
  type DilimKey,
  type Program,
  type PuanTuruKey,
} from "../src/types/yokatlas";
import { KATEGORILER, kategoriEslesir } from "../src/lib/kategoriler";
import { havuzuKompaktJson, turLabel } from "../src/lib/rapor-havuzu";
import { GENEL_KATEGORI, KOVALAR, kategoriSlugBul } from "../src/lib/tadimlik-havuzu";
import { anahtarVar } from "../src/lib/ai/client";
import { yapilandirilmisUret } from "../src/lib/ai/cagri";

// Next dışında koşuyoruz; .env dosyalarını elle yükle (mevcut env kazanır).
function envYukle(dosya: string): void {
  if (!existsSync(dosya)) return;
  for (const satir of readFileSync(dosya, "utf8").split("\n")) {
    const m = satir.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
    if (m && process.env[m[1]] === undefined) {
      process.env[m[1]] = m[2].replace(/^"(.*)"$/, "$1").trim();
    }
  }
}
envYukle(".env.local");
envYukle(".env");

const TadimlikSchema = z.object({
  gerekce: z.string(),
  riskNotu: z.string(),
  trendOzeti: z.string(),
});

// rapor.ts'teki SISTEM kurallarının tek satırlık versiyonu.
const SISTEM = `Sen KolayTercih'in kıdemli YKS tercih danışmanısın. Sana GERÇEK
YÖK Atlas verisiyle TEK bir program ve temsili bir aday tanımı verilecek; bu
program için tercih listesi satırı metinleri üreteceksin.

KURALLAR (kesin):
- METİN DİYETİ: her alan TEK kısa cümle, dolgu söz yok.
- gerekce: bu programın bu aday için neden mantıklı olduğu (TEK cümle,
  en fazla ~15 kelime, Türkçe).
- riskNotu: yerleşme ihtimali değerlendirmesi (TEK kısa cümle). ASLA "garanti",
  "kesin", "yüzde yüz" deme; "yüksek/orta/düşük ihtimal" dili kullan.
- trendOzeti: son 4 yılın taban sıralaması hareketi, TEK cümle (ör. "2022'den
  beri taban 45B'den 38B'ye indi, bölüm zorlaşıyor").
- Adayın sıralaması yaklaşıktır; kesin sıra telaffuz etme, banda göre konuş.`;

function mockAktif(): boolean {
  if (process.argv.includes("--mock")) return true;
  return process.env.NODE_ENV !== "production" && !anahtarVar();
}

function mockMetin(p: Program, temsiliSira: number, kategori: string | null) {
  return {
    gerekce: `(Dev mock) ${p.isim}, ${kategori ? `${kategori} ilgisiyle ve ` : ""}~${temsiliSira.toLocaleString("tr-TR")} bandındaki sıralamanla uyumlu bir aday.`,
    riskNotu:
      "(Dev mock) Taban sıralaması senin civarında; orta-yüksek ihtimalle yerleşilebilir.",
    trendOzeti:
      "(Dev mock) Son 4 yılda taban sıralaması görece stabil seyretti.",
  };
}

/** rapor.ts programBilgisi ile aynı snapshot şekli. */
function programBilgisi(p: Program) {
  const gecmis = [p.sira2021, p.sira2022, p.sira2023, p.sira2024, p.sira2025];
  return {
    isim: p.isim,
    universite: p.universite,
    il: p.il,
    unitur: p.unitur,
    efektifSira: p.sira2025 ?? p.sira2024,
    siraGecmisi: gecmis.some((s) => s != null) ? gecmis : undefined,
  };
}

/**
 * Deterministik program seçimi: kovanın temsili sırası için "dengeli" dilimden
 * (boşsa "garanti") kategoriye uyan, 2025'te en çok yerleşen alan program.
 * Tanınır/dolu programlar tadımlıkta daha ikna edici.
 */
function programSec(
  temsiliSira: number,
  tur: PuanTuruKey,
  kategori: string | null,
): { program: Program; dilim: DilimKey } | null {
  for (const dilim of ["dengeli", "garanti"] as const) {
    const { programlar } = dilimAra(temsiliSira, tur, dilim, { limit: 60 });
    const uygunlar = kategori
      ? programlar.filter((p) => kategoriEslesir(p.isim, [kategori]))
      : programlar;
    if (uygunlar.length === 0) continue;
    const secilen = [...uygunlar].sort(
      (a, b) => (b.yerlesen2025 ?? -1) - (a.yerlesen2025 ?? -1),
    )[0];
    return { program: secilen, dilim };
  }
  return null;
}

async function metinUret(
  p: Program,
  dilim: DilimKey,
  temsiliSira: number,
  tur: PuanTuruKey,
  kategori: string | null,
): Promise<z.infer<typeof TadimlikSchema> | null> {
  if (mockAktif()) return mockMetin(p, temsiliSira, kategori);

  const kullanici = [
    `Temsili aday: başarı sıralaması ~${temsiliSira.toLocaleString("tr-TR")} bandında, puan türü ${turLabel(tur)}.`,
    kategori ? `Adayın ilgi alanı: ${kategori}.` : null,
    `PROGRAM (yalnızca bunun için metin üret): ${havuzuKompaktJson([{ ...p, dilim }])}`,
  ]
    .filter(Boolean)
    .join("\n\n");

  return yapilandirilmisUret({
    system: SISTEM,
    kullanici,
    sema: TadimlikSchema,
    maxTokens: 4000,
  });
}

function bayrak(ad: string): string | undefined {
  const arg = process.argv.find((a) => a.startsWith(`--${ad}=`));
  return arg?.slice(ad.length + 3);
}

async function main() {
  const force = process.argv.includes("--force");
  const kovaFiltre = bayrak("kova");
  const turFiltre = bayrak("tur");
  const kategoriFiltre = bayrak("kategori");

  const turlar = (Object.keys(PUAN_TURLERI) as PuanTuruKey[]).filter(
    (t) => !turFiltre || t === turFiltre,
  );
  const kategoriler: { ad: string | null; slug: string }[] = [
    { ad: null, slug: GENEL_KATEGORI },
    ...KATEGORILER.map((ad) => ({ ad: ad as string, slug: kategoriSlugBul(ad) })),
  ].filter((k) => !kategoriFiltre || k.slug === kategoriFiltre);
  const kovalar = KOVALAR.filter((k) => !kovaFiltre || k.slug === kovaFiltre);

  const mevcutlar = new Set(
    (
      await appDb
        .select({
          kovaSlug: schema.tadimlikHavuzu.kovaSlug,
          tur: schema.tadimlikHavuzu.tur,
          kategoriSlug: schema.tadimlikHavuzu.kategoriSlug,
        })
        .from(schema.tadimlikHavuzu)
    ).map((s) => `${s.kovaSlug}|${s.tur}|${s.kategoriSlug}`),
  );

  if (mockAktif()) {
    console.log("AI anahtarı yok — mock metinlerle üretiliyor (dev).");
  }

  let uretilen = 0;
  let atlananMevcut = 0;
  let atlananBos = 0;

  for (const kova of kovalar) {
    for (const tur of turlar) {
      for (const kategori of kategoriler) {
        const anahtar = `${kova.slug}|${tur}|${kategori.slug}`;
        if (!force && mevcutlar.has(anahtar)) {
          atlananMevcut++;
          continue;
        }

        const secim = programSec(kova.temsiliSira, tur, kategori.ad);
        if (!secim) {
          atlananBos++;
          continue;
        }

        const metin = await metinUret(
          secim.program,
          secim.dilim,
          kova.temsiliSira,
          tur,
          kategori.ad,
        );
        if (!metin) {
          console.warn(`Şemaya uymayan çıktı, atlandı: ${anahtar}`);
          atlananBos++;
          continue;
        }

        const now = new Date();
        await appDb
          .insert(schema.tadimlikHavuzu)
          .values({
            id: randomUUID(),
            kovaSlug: kova.slug,
            tur,
            kategoriSlug: kategori.slug,
            programId: secim.program.id,
            dilim: secim.dilim,
            gerekce: metin.gerekce,
            riskNotu: metin.riskNotu,
            trendOzeti: metin.trendOzeti,
            program: programBilgisi(secim.program),
            ornekSira: kova.temsiliSira,
            uretimAt: now,
          })
          .onConflictDoUpdate({
            target: [
              schema.tadimlikHavuzu.kovaSlug,
              schema.tadimlikHavuzu.tur,
              schema.tadimlikHavuzu.kategoriSlug,
            ],
            set: {
              programId: secim.program.id,
              dilim: secim.dilim,
              gerekce: metin.gerekce,
              riskNotu: metin.riskNotu,
              trendOzeti: metin.trendOzeti,
              program: programBilgisi(secim.program),
              ornekSira: kova.temsiliSira,
              uretimAt: now,
            },
          });

        uretilen++;
        console.log(`✓ ${anahtar} → ${secim.program.isim} (${secim.dilim})`);
        // Gerçek LLM'de rate limit dostu ardışık tempo
        if (!mockAktif()) {
          const bekleme = Number.parseInt(bayrak("bekleme") ?? "500", 10);
          await new Promise((r) => setTimeout(r, bekleme));
        }
      }
    }
  }

  console.log(
    `Bitti: ${uretilen} üretildi, ${atlananMevcut} mevcut atlandı, ${atlananBos} içeriksiz/şemasız atlandı.`,
  );
}

main().catch((hata) => {
  console.error(hata);
  process.exit(1);
});
