// server-only guard'ı bilinçli yok: scripts/tadimlik-uret.ts bu modülü düz
// node (tsx) altında import ediyor; server-only paketi orada fırlar.
import { searchByRank } from "./db";
import {
  PUAN_TURLERI,
  type Program,
  type PuanTuruKey,
} from "@/types/yokatlas";
import type { RaporParams } from "@/features/rapor/types/rapor";
import { kategoriEslesir } from "./kategoriler";

export type Dilim = "hayal" | "dengeli" | "garanti";

export interface AdayProgram extends Program {
  dilim: Dilim;
}

export interface HavuzSonuc {
  havuz: AdayProgram[];
  // Havuz 24'e ulaşmak için hangi filtreler gevşetildi
  gevsetildi: ("kategori" | "il")[];
}

const HEDEF = 24;
const LIMIT = 60;

export type DilimHedef = Record<Dilim, number>;

// Listenin ideal iskeleti: 5 hayal / 13 dengeli / 6 garanti = 24 tercih.
export const IDEAL_HEDEF: DilimHedef = { hayal: 5, dengeli: 13, garanti: 6 };

/**
 * Havuzun gerçek dilim dağılımına uyarlanmış liste hedefi. İdeal 5/13/6'dan
 * başlar; bir dilimde yeterli aday yoksa eksik, kapasitesi kalan dilimlere
 * dağıtılır (öncelik: dengeli → garanti → hayal — omurga önce, sigorta sonra,
 * en riskli dilim en son). Havuz 24'ün altındaysa hedef havuzun tamamıdır.
 * Deterministiktir: aynı havuz her zaman aynı hedefi üretir.
 */
export function dilimHedefiHesapla(havuz: AdayProgram[]): DilimHedef {
  const mevcut: DilimHedef = { hayal: 0, dengeli: 0, garanti: 0 };
  for (const p of havuz) mevcut[p.dilim]++;

  const toplamHedef = Math.min(HEDEF, havuz.length);
  const hedef: DilimHedef = {
    hayal: Math.min(IDEAL_HEDEF.hayal, mevcut.hayal),
    dengeli: Math.min(IDEAL_HEDEF.dengeli, mevcut.dengeli),
    garanti: Math.min(IDEAL_HEDEF.garanti, mevcut.garanti),
  };

  const oncelik: Dilim[] = ["dengeli", "garanti", "hayal"];
  let eksik = toplamHedef - hedef.hayal - hedef.dengeli - hedef.garanti;
  while (eksik > 0) {
    let dagitildi = false;
    for (const d of oncelik) {
      if (eksik <= 0) break;
      if (mevcut[d] > hedef[d]) {
        hedef[d]++;
        eksik--;
        dagitildi = true;
      }
    }
    if (!dagitildi) break;
  }
  return hedef;
}
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama LIMIT ile geniş
// yapılır (filtre/gevşetme zenginliği için), filtre sonrası buraya kırpılır —
// 20×3=60 program ≈ 8k token; 180 program 24k token tutuyor ve lokal/ücretsiz
// modellerde prefill + isabet maliyeti yüksek (ölçüm: 6 Ağu 2026).
const PROMPT_LIMIT_DILIM = 20;

/** Dilim sırasını koruyarak her dilimden en fazla n aday bırakır. */
function dilimBasinaKirp(havuz: AdayProgram[], n: number): AdayProgram[] {
  const sayac: Record<Dilim, number> = { hayal: 0, dengeli: 0, garanti: 0 };
  return havuz.filter((p) => ++sayac[p.dilim] <= n);
}

function dilimEkle(r: {
  hayal: Program[];
  dengeli: Program[];
  garanti: Program[];
}): AdayProgram[] {
  return [
    ...r.hayal.map((p) => ({ ...p, dilim: "hayal" as const })),
    ...r.dengeli.map((p) => ({ ...p, dilim: "dengeli" as const })),
    ...r.garanti.map((p) => ({ ...p, dilim: "garanti" as const })),
  ];
}

/**
 * Deterministik aday havuzu: Claude bu havuzdan SEÇER, asla uydurmaz.
 * Sihirbaz seçimlerine (kategori/il/üniversite tipi) göre filtreler; havuz
 * 24'ün altına düşerse kademeli gevşetir (önce il, sonra kategori) ki liste
 * her zaman kurulabilsin.
 */
export function havuzOlustur(params: RaporParams): HavuzSonuc {
  const uniturGrubu =
    params.universiteTipi && params.universiteTipi !== "farketmez"
      ? params.universiteTipi
      : undefined;
  const kategoriler = params.kategoriler ?? [];
  const iller = [
    ...(params.il ? [params.il] : []),
    ...(params.iller ?? []),
  ].filter((v, i, a) => v && a.indexOf(v) === i);

  const kategoriFiltre = (havuz: AdayProgram[]) =>
    kategoriler.length > 0
      ? havuz.filter((p) => kategoriEslesir(p.isim, kategoriler))
      : havuz;

  // 1. Tam filtre: iller + üniversite tipi (SQL), kategori (JS)
  const tamHam = dilimEkle(
    searchByRank(params.sira, params.tur, {
      iller: iller.length > 0 ? iller : undefined,
      uniturGrubu,
      limitPerBucket: LIMIT,
    }),
  );
  const tamFiltreli = kategoriFiltre(tamHam);
  if (tamFiltreli.length >= HEDEF) {
    return {
      havuz: dilimBasinaKirp(tamFiltreli, PROMPT_LIMIT_DILIM),
      gevsetildi: [],
    };
  }

  // 2. Kategori gevşet: aynı illerdeki kategori-dışı programlarla doldur
  if (kategoriler.length > 0 && tamHam.length >= HEDEF) {
    return {
      havuz: dilimBasinaKirp(tamHam, PROMPT_LIMIT_DILIM),
      gevsetildi: ["kategori"],
    };
  }

  // 3. İl gevşet: il filtresini kaldırıp kategoriye geri dön
  const gevsetildi: ("kategori" | "il")[] = [];
  if (iller.length > 0) gevsetildi.push("il");
  const ilsizHam = dilimEkle(
    searchByRank(params.sira, params.tur, {
      uniturGrubu,
      limitPerBucket: LIMIT,
    }),
  );
  const ilsizFiltreli = kategoriFiltre(ilsizHam);
  if (ilsizFiltreli.length >= HEDEF) {
    return {
      havuz: dilimBasinaKirp(ilsizFiltreli, PROMPT_LIMIT_DILIM),
      gevsetildi,
    };
  }

  // 4. Hem il hem kategori gevşet: elde ne varsa
  if (kategoriler.length > 0) gevsetildi.push("kategori");
  return { havuz: dilimBasinaKirp(ilsizHam, PROMPT_LIMIT_DILIM), gevsetildi };
}

/** Prompt'a enjekte edilecek kompakt satır (token tasarrufu). */
export function havuzuKompaktJson(havuz: AdayProgram[]): string {
  return JSON.stringify(
    havuz.map((p) => ({
      id: p.id,
      isim: p.isim,
      uni: p.universite,
      unitur: p.unitur,
      il: p.il,
      dilim: p.dilim,
      sira: {
        y2025: p.sira2025,
        y2024: p.sira2024,
        y2023: p.sira2023,
        y2022: p.sira2022,
      },
      kontenjan2025: p.kontenjan2025,
      yerlesen2025: p.yerlesen2025,
    })),
  );
}

export function turLabel(tur: PuanTuruKey): string {
  return PUAN_TURLERI[tur];
}
