import { z } from "zod";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { getAnthropic, RAPOR_MODEL } from "./client";
import {
  havuzOlustur,
  havuzuKompaktJson,
  turLabel,
  type RaporParams,
  type AdayProgram,
} from "../rapor-havuzu";
import { secimOzeti } from "../sihirbaz";
import { RISK_ETIKET, dilimdenRisk, riskHesapla } from "../risk";

const TercihSchema = z.object({
  programId: z.string(),
  sira: z.number().int(),
  dilim: z.enum(["hayal", "dengeli", "garanti"]),
  gerekce: z.string(),
  riskNotu: z.string(),
  trendOzeti: z.string(),
});

const RaporSchema = z.object({
  tercihler: z.array(TercihSchema),
  genelDegerlendirme: z.string(),
  uyarilar: z.array(z.string()).min(2).max(3),
});

// uyarilar şemada zorunlu ama eski kayıtlı raporlarda yok → tipte opsiyonel
export type RaporSonuc = Omit<z.infer<typeof RaporSchema>, "uyarilar"> & {
  uyarilar?: string[];
  // Render için havuzdan zenginleştirilmiş program bilgisi
  // efektifSira: COALESCE(sira2025, sira2024) — eski kayıtlı raporlarda yok
  // siraGecmisi: [2021..2025] taban sıralamaları — deterministik trend oku
  programlar: Record<
    string,
    {
      isim: string;
      universite: string;
      il: string | null;
      unitur: string | null;
      efektifSira?: number | null;
      siraGecmisi?: (number | null)[];
    }
  >;
};

const SISTEM = `Sen KolayTercih'in kıdemli YKS tercih danışmanısın. Görevin: sana verilen
GERÇEK YÖK Atlas aday havuzundan, adayın sıralamasına göre 24 tercihlik dengeli
bir liste kurmak.

KURALLAR (kesin):
- SADECE havuzdaki programId'leri kullan. Havuzda olmayan program ASLA önerme.
- Tam 24 tercih üret; sira alanı 1'den 24'e sıralı olsun.
- Liste yapısı: en üstte 4-6 hayal, ortada 12-14 dengeli, sonda 5-7 garanti.
  Tercih sırası MUTLAKA iyi taban sıralamasından kötüye doğru gitmeli
  (küçük taban sıralaması = daha zor bölüm üstte).
- METİN DİYETİ: aday 24 satır okuyacak; 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").
- Kontenjan artışı/azalışı ve yerleşen sayısını değerlendirmene kat.
- Adayın notlarındaki şehir/bölüm tercihlerine saygı göster ama dengeyi bozma.
- genelDegerlendirme: listenin genel stratejisi, EN FAZLA 2 cümle. Uyarıları
  buraya yazma.
- uyarilar: adaya 2-3 pratik, eyleme dönük uyarı; her biri tek kısa cümle.`;

export class RaporUretimHatasi extends Error {}

/** Havuz programını render için gereken bilgiye indirger. */
function programBilgisi(p: AdayProgram): RaporSonuc["programlar"][string] {
  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,
  };
}

// --- Dev mock: API key yokken (yalnızca development) gerçek havuzdan sahte
// ama tutarlı bir rapor üret. Akış (kredi, blur, risk renkleri, revizyon)
// gerçek AI olmadan uçtan uca test edilebilsin diye. Prod'da asla çalışmaz.

function mockAktif(): boolean {
  return (
    process.env.NODE_ENV !== "production" &&
    !process.env.ANTHROPIC_API_KEY &&
    !process.env.ANTHROPIC_AUTH_TOKEN
  );
}

function mockRapor(
  havuz: AdayProgram[],
  params: RaporParams,
  revizyonNotu?: string,
): RaporSonuc {
  const efektif = (p: AdayProgram) => p.sira2025 ?? p.sira2024 ?? 0;
  const dilimden = (dilim: AdayProgram["dilim"], adet: number) =>
    havuz
      .filter((p) => p.dilim === dilim)
      .sort((a, b) => efektif(a) - efektif(b))
      .slice(0, adet);

  // 5 hayal + 13 dengeli + 6 garanti; eksik dilim varsa havuzdan tamamla
  const secilen = [
    ...dilimden("hayal", 5),
    ...dilimden("dengeli", 13),
    ...dilimden("garanti", 6),
  ];
  for (const p of havuz) {
    if (secilen.length >= 24) break;
    if (!secilen.includes(p)) secilen.push(p);
  }

  const tercihler = secilen.slice(0, 24).map((p, i) => ({
    programId: p.id,
    sira: i + 1,
    dilim: p.dilim,
    gerekce: `(Dev mock) ${p.isim}, seçtiğin ilgi alanlarıyla ve ${params.sira.toLocaleString("tr-TR")}. sıralamanla uyumlu bir aday.`,
    riskNotu:
      p.dilim === "hayal"
        ? "(Dev mock) Taban sıralaması senin üstünde kapandı; düşük ihtimal ama listenin başında yeri var."
        : p.dilim === "dengeli"
          ? "(Dev mock) Taban sıralaması senin civarında; orta-yüksek ihtimalle yerleşilebilir."
          : "(Dev mock) Taban sıralaması senin belirgin altında; yüksek ihtimalle yerleşirsin.",
    trendOzeti:
      "(Dev mock) Son 4 yılda taban sıralaması görece stabil seyretti.",
  }));

  const programlar: RaporSonuc["programlar"] = {};
  for (const p of secilen.slice(0, 24)) {
    programlar[p.id] = programBilgisi(p);
  }

  return {
    tercihler,
    genelDegerlendirme: `(Dev mock — ANTHROPIC_API_KEY tanımlı değil, bu rapor gerçek AI çıktısı değildir.) ${params.sira.toLocaleString(
      "tr-TR",
    )}. sıra için hayal/dengeli/garanti dağılımlı 24 tercihlik örnek liste kuruldu.${
      revizyonNotu ? ` Revizyon isteğin not alındı: "${revizyonNotu}".` : ""
    }`,
    uyarilar: [
      "(Dev mock) Tercih dönemi kapanmadan listeyi ÖSYM sistemine kendin gir.",
      "(Dev mock) Garanti dilimindeki son 3 tercihi asla silme.",
    ],
    programlar,
  };
}

export async function raporUret(
  params: RaporParams,
  revizyonNotu?: string,
  oncekiListeOzeti?: string,
): Promise<RaporSonuc> {
  const { havuz, gevsetildi } = havuzOlustur(params);
  if (havuz.length < 24) {
    throw new RaporUretimHatasi(
      "Bu sıralama ve filtrelerle yeterli program bulunamadı. Daha geniş kategori veya il seçmeyi dene.",
    );
  }

  const secimVar =
    (params.kategoriler?.length ?? 0) > 0 ||
    (params.iller?.length ?? 0) > 0 ||
    (params.oncelikler?.length ?? 0) > 0 ||
    (params.universiteTipi && params.universiteTipi !== "farketmez");

  const gevsetmeNotu: string[] = [];
  if (gevsetildi.includes("il")) {
    gevsetmeNotu.push(
      "Adayın seçtiği illerde yeterli program yoktu; havuza başka illerden de program eklendi. Seçili illere öncelik ver ama listeyi 24'e tamamlamak için diğerlerini de kullan.",
    );
  }
  if (gevsetildi.includes("kategori")) {
    gevsetmeNotu.push(
      "Adayın seçtiği ilgi alanlarında yeterli program yoktu; havuza yakın alanlardan program eklendi. Seçili alanlara öncelik ver.",
    );
  }

  // Dev'de API key yoksa gerçek AI yerine mock rapor (üretim gecikmesi taklidi)
  if (mockAktif()) {
    await new Promise((r) => setTimeout(r, 1500));
    return mockRapor(havuz, params, revizyonNotu);
  }

  const client = getAnthropic();
  const havuzJson = havuzuKompaktJson(havuz);
  const kullaniciMesaji = [
    `Aday bilgisi: başarı sıralaması ${params.sira.toLocaleString("tr-TR")}, puan türü ${turLabel(params.tur)}.`,
    secimVar ? `ADAYIN SİHİRBAZ SEÇİMLERİ:\n${secimOzeti({
      kategoriler: params.kategoriler ?? [],
      iller: params.iller ?? [],
      universiteTipi: params.universiteTipi ?? "farketmez",
      oncelikler: params.oncelikler ?? [],
    })}\nSeçili kategori ve illere öncelik ver; havuzun elverdiği ölçüde bu seçimlere sadık kal.`
      : null,
    params.notlar ? `Adayın ek notları: ${params.notlar}` : null,
    gevsetmeNotu.length > 0 ? gevsetmeNotu.join("\n") : null,
    revizyonNotu
      ? `REVİZYON İSTEĞİ: Aday mevcut listede şu değişikliği istiyor: ${revizyonNotu}\nÖnceki listenin özeti: ${oncekiListeOzeti ?? "yok"}`
      : null,
    `ADAY HAVUZU (yalnızca buradan seç): ${havuzJson}`,
  ]
    .filter(Boolean)
    .join("\n\n");

  const gecerliIdler = new Set(havuz.map((p) => p.id));

  let sonuc: z.infer<typeof RaporSchema> | null = null;
  let sonHata = "";
  for (let deneme = 0; deneme < 2; deneme++) {
    const response = await client.messages.parse({
      model: RAPOR_MODEL,
      max_tokens: 16000,
      thinking: { type: "adaptive" },
      system: SISTEM,
      messages: [
        {
          role: "user",
          content:
            deneme === 0
              ? kullaniciMesaji
              : `${kullaniciMesaji}\n\nÖNCEKİ DENEMENDE HATA VARDI: ${sonHata} Kurallara tam uy.`,
        },
      ],
      output_config: { format: zodOutputFormat(RaporSchema) },
    });

    const parsed = response.parsed_output;
    if (!parsed) {
      sonHata = "Çıktı şemaya uymadı.";
      continue;
    }
    // Halüsinasyon koruması: her programId havuzda olmalı + tam 24 öğe
    const bilinmeyen = parsed.tercihler.filter(
      (t) => !gecerliIdler.has(t.programId),
    );
    if (parsed.tercihler.length !== 24 || bilinmeyen.length > 0) {
      sonHata =
        parsed.tercihler.length !== 24
          ? `Liste ${parsed.tercihler.length} öğe içeriyor, tam 24 olmalı.`
          : `Havuzda olmayan programId kullanıldı: ${bilinmeyen
              .map((t) => t.programId)
              .slice(0, 3)
              .join(", ")}.`;
      continue;
    }
    sonuc = parsed;
    break;
  }

  if (!sonuc) {
    throw new RaporUretimHatasi(
      "Rapor üretilemedi (doğrulama iki denemede de başarısız). Tekrar dener misin? Revizyon hakkın kullanılmadı.",
    );
  }

  const programlar: RaporSonuc["programlar"] = {};
  for (const p of havuz) {
    programlar[p.id] = programBilgisi(p);
  }
  return { ...sonuc, programlar };
}

export function listeOzetiCikar(rapor: RaporSonuc, adaySira?: number): string {
  return rapor.tercihler
    .map((t) => {
      const p = rapor.programlar[t.programId];
      const risk =
        (adaySira != null ? riskHesapla(p?.efektifSira, adaySira) : null) ??
        dilimdenRisk(t.dilim);
      const taban =
        p?.efektifSira != null
          ? ` (taban sırası ~${p.efektifSira.toLocaleString("tr-TR")})`
          : "";
      return `${t.sira}. [${t.dilim} | ${RISK_ETIKET[risk]}] ${p?.isim ?? "?"} — ${p?.universite ?? "?"}${taban}`;
    })
    .join("\n");
}

export type { AdayProgram };
