import { eq, desc, asc } from "drizzle-orm";
import type { NextRequest } from "next/server";
import { getSession } from "@/lib/session";
import { appDb, schema } from "@/lib/appdb";
import {
  spendCreditForMessage,
  grantCredits,
  krediBittiIsaretle,
} from "@/lib/credits";
import { anahtarVar, SORUMLULUK_REDDI } from "@/lib/ai/client";
import { kullanicininRaporu } from "@/features/rapor/rapor-queries";
import { sohbetAkisi } from "@/lib/ai/cagri";
import { listeOzetiCikar } from "@/lib/ai/rapor";
import type { RaporSonuc } from "@/features/rapor/types/rapor";
import type { RaporParams } from "@/features/rapor/types/rapor";
import { secimOzeti } from "@/features/sihirbaz/sihirbaz-sabitler";
import { PUAN_TURLERI } from "@/types/yokatlas";
import { ACIK_SATIR } from "@/lib/rapor-maske";

const MAX_GECMIS = 12;
const MAX_MESAJ_UZUNLUK = 2000;

function sistemPromptu(opts: {
  params: RaporParams;
  listeOzeti: string;
  kilitli?: boolean;
}): string {
  const { params } = opts;
  const secimVar =
    (params.kategoriler?.length ?? 0) > 0 ||
    (params.iller?.length ?? 0) > 0 ||
    (params.oncelikler?.length ?? 0) > 0 ||
    (params.universiteTipi && params.universiteTipi !== "farketmez");

  const parcalar = [
    `Sen KolayTercih'in YKS tercih danışmanısın. Türkçe, samimi ama profesyonel konuş.
Aday az önce sana verilen 24'lük tercih listesini oluşturdu. Görevin: adayın bu
liste ve seçimleri BAĞLAMINDA sorularını (bölüm kıyaslama, şehir, risk, kontenjan,
iş imkânları, tercih sırası) net ve dürüst yanıtlamak.

KURALLAR:
- Cevaplarını adayın 24'lük listesi ve seçimleri bağlamında ver; listedeki
  programlara referans verebilirsin.
- Listedeki her satırda dilim ve risk etiketi var: Güvenli (yeşil) = taban
  sıralaması adayın belirgin altında, Az riskli (sarı) = adayın sıralaması
  civarında, Riskli (kırmızı) = geçen yıl adayın sıralamasının üzerinde
  kapandı. Risk sorularında bu etiketleri ve taban sıralarını kullan.
- ASLA yerleşme garantisi verme; "yüksek/orta/düşük ihtimal" dili kullan.
- Bilmediğin güncel veriyi uydurma; emin değilsen YÖK Atlas'a bakmasını söyle.
- Taban sıralamalarının her yıl değiştiğini gerektiğinde hatırlat.
- Cevapları kısa ve öz tut (genelde 3-6 cümle); istenirse detaylandır.
- Tercih dışı konularda (ödev, kod, genel sohbet) kibarca reddet ve tercih
  konusuna dön.
- Kesin konuşmaktan kaçın: "${SORUMLULUK_REDDI}"`,
    `ADAY BİLGİSİ: başarı sıralaması ${params.sira.toLocaleString("tr-TR")}, puan türü ${PUAN_TURLERI[params.tur]}.`,
    `ADAYIN 24'LÜK LİSTESİ:\n${opts.listeOzeti}`,
  ];
  if (opts.kilitli) {
    parcalar.push(
      `ÖNEMLİ: Adayın paketi yok; listenin yalnızca ilk ${ACIK_SATIR} tercihi açık.
Sana da yalnızca bu satırlar verildi — kilitli tercihlerin içeriğini BİLMİYORSUN,
asla tahmin edip söyleme. Kilitli satırlar sorulursa sempatik biçimde tam listenin
Tercih Dönemi Paketi'yle (299 TL, /paket) açıldığını söyle; açık satırlar ve genel
tercih soruları üzerinden yardımcı olmaya devam et.`,
    );
  }
  if (secimVar) {
    parcalar.push(
      `ADAYIN SİHİRBAZ SEÇİMLERİ:\n${secimOzeti({
        kategoriler: params.kategoriler ?? [],
        iller: params.iller ?? [],
        universiteTipi: params.universiteTipi ?? "farketmez",
        oncelikler: params.oncelikler ?? [],
      })}`,
    );
  }
  return parcalar.join("\n\n");
}

// Widget açıldığında geçmişi ve kredi bakiyesini döner
export async function GET() {
  const session = await getSession();
  if (!session) {
    return Response.json({ error: "Oturum gerekli." }, { status: 401 });
  }
  const [mesajlar, kullanici] = await Promise.all([
    appDb
      .select({
        id: schema.chatMessages.id,
        role: schema.chatMessages.role,
        content: schema.chatMessages.content,
      })
      .from(schema.chatMessages)
      .where(eq(schema.chatMessages.userId, session.user.id))
      .orderBy(asc(schema.chatMessages.createdAt))
      .limit(100),
    appDb.query.user.findFirst({
      where: eq(schema.user.id, session.user.id),
      columns: { creditBalance: true },
    }),
  ]);
  return Response.json({
    mesajlar,
    kredi: kullanici?.creditBalance ?? 0,
  });
}

export async function POST(request: NextRequest) {
  // Oturum ve gövde ayrıştırma bağımsız — paralel başlar, hata önceliği aynı
  // kalır (önce 401, sonra 400).
  const [session, hamBody] = await Promise.all([
    getSession(),
    request.json().catch(() => null),
  ]);
  if (!session) {
    return Response.json({ error: "Oturum gerekli." }, { status: 401 });
  }
  if (hamBody === null || typeof hamBody !== "object") {
    return Response.json({ error: "Geçersiz istek." }, { status: 400 });
  }
  const body = hamBody as { message?: unknown; clientMessageId?: unknown };
  const mesaj = typeof body.message === "string" ? body.message.trim() : "";
  const clientMessageId =
    typeof body.clientMessageId === "string" ? body.clientMessageId : "";
  if (!mesaj || mesaj.length > MAX_MESAJ_UZUNLUK || !clientMessageId) {
    return Response.json({ error: "Geçersiz mesaj." }, { status: 400 });
  }

  // Üç okuma da yalnızca session.user.id'ye bağlı — tek round trip'te paralel.
  // Geçmiş krediyi düşmeden ÖNCE okunur ki kullanıcı mesajı dahil olmasın;
  // hasPaket'i spendCreditForMessage değiştirmez, erken okumak güvenli.
  const [rapor, gecmisHam, kullanici] = await Promise.all([
    // Soru sormak için önce liste oluşturulmuş olmalı (bayat dev mock sayılmaz)
    kullanicininRaporu(session.user.id),
    appDb
      .select()
      .from(schema.chatMessages)
      .where(eq(schema.chatMessages.userId, session.user.id))
      .orderBy(desc(schema.chatMessages.createdAt))
      .limit(MAX_GECMIS),
    appDb.query.user.findFirst({
      where: eq(schema.user.id, session.user.id),
      columns: { hasPaket: true },
    }),
  ]);
  if (!rapor?.result) {
    return Response.json(
      { error: "Önce listeni oluşturmalısın.", code: "NO_REPORT" },
      { status: 409 },
    );
  }
  const gecmis = gecmisHam.reverse();

  // Kredi düşümü stream'den önce — yetersizse hiç başlama
  const harcama = await spendCreditForMessage({
    userId: session.user.id,
    content: mesaj,
    clientMessageId,
  });
  if (!harcama.ok) {
    if (harcama.error === "INSUFFICIENT") {
      await krediBittiIsaretle(session.user.id);
      return Response.json(
        { error: "Kredin bitti.", code: "INSUFFICIENT" },
        { status: 402 },
      );
    }
    return Response.json(
      { error: "Bu mesaj zaten işlendi.", code: "DUPLICATE" },
      { status: 409 },
    );
  }
  const kullaniciMesajId = harcama.messageId!;

  // Paketsiz kullanıcının prompt'una yalnızca açık satırlar girer — aksi
  // halde danışman kilitli listeyi sözle sızdırabilir.
  const kilitli = !kullanici?.hasPaket;
  const tamOzet = listeOzetiCikar(
    rapor.result as RaporSonuc,
    (rapor.params as RaporParams).sira,
  );
  const listeOzeti = kilitli
    ? [
        ...tamOzet.split("\n").slice(0, ACIK_SATIR),
        `… kalan ${24 - ACIK_SATIR} tercih kilitli.`,
      ].join("\n")
    : tamOzet;

  const system = sistemPromptu({
    params: rapor.params as RaporParams,
    listeOzeti,
    kilitli,
  });

  // Dev'de API key yoksa danışman mock cevap stream'ler (prod'da asla)
  if (process.env.NODE_ENV !== "production" && !anahtarVar()) {
    const mockEncoder = new TextEncoder();
    const mockUserId = session.user.id;
    const cevap =
      `(Dev mock — Yapay Zeka anahtarı tanımlı değil.) Sorunu aldım: "${mesaj.slice(0, 120)}". ` +
      `Gerçek danışman burada listen ve seçimlerin bağlamında yanıt verecek. ` +
      `${SORUMLULUK_REDDI}`;
    const mockReadable = new ReadableStream({
      async start(controller) {
        for (const parca of cevap.split(/(?<= )/)) {
          controller.enqueue(mockEncoder.encode(parca));
          await new Promise((r) => setTimeout(r, 25));
        }
        await appDb.insert(schema.chatMessages).values({
          id: crypto.randomUUID(),
          userId: mockUserId,
          role: "assistant",
          content: cevap,
          createdAt: new Date(),
        });
        controller.close();
      },
    });
    return new Response(mockReadable, {
      headers: {
        "Content-Type": "text/plain; charset=utf-8",
        "X-Content-Type-Options": "nosniff",
        "Cache-Control": "no-store",
      },
    });
  }

  if (!anahtarVar()) {
    await grantCredits({
      userId: session.user.id,
      delta: 1,
      reason: "refund",
      refId: kullaniciMesajId,
    });
    console.error("[soru] Yapay Zeka anahtarı tanımlı değil");
    return Response.json(
      {
        error:
          "Danışman şu anda yanıt veremiyor; kredin iade edildi. Birazdan tekrar dener misin?",
      },
      { status: 503 },
    );
  }

  const mesajlar = [
    ...gecmis.map((m) => ({
      role: m.role as "user" | "assistant",
      content: m.content,
    })),
    { role: "user" as const, content: mesaj },
  ];

  const encoder = new TextEncoder();
  const userId = session.user.id;

  const readable = new ReadableStream({
    async start(controller) {
      let tamamlandi = false;
      let metin = "";
      try {
        // Sağlayıcıya bağlanma hatası da ilk parçada burada yakalanır
        for await (const parca of sohbetAkisi({
          system,
          mesajlar,
          maxTokens: 1024,
        })) {
          metin += parca;
          controller.enqueue(encoder.encode(parca));
        }
        await appDb.insert(schema.chatMessages).values({
          id: crypto.randomUUID(),
          userId,
          role: "assistant",
          content: metin,
          createdAt: new Date(),
        });
        tamamlandi = true;
        controller.close();
      } catch (err) {
        if (!tamamlandi) {
          // Cevap tamamlanmadı: kredi iade edilir. Yarıda kalan metin varsa
          // yine de kaydedilir ki kullanıcı ekranda gördüğü kısmı sayfa
          // yenilenince kaybetmesin.
          if (metin) {
            try {
              await appDb.insert(schema.chatMessages).values({
                id: crypto.randomUUID(),
                userId,
                role: "assistant",
                content: metin,
                createdAt: new Date(),
              });
            } catch {}
          }
          await grantCredits({
            userId,
            delta: 1,
            reason: "refund",
            refId: kullaniciMesajId,
          }).catch(() => {});
        }
        console.error("[soru] stream hatası:", err);
        controller.error(err);
      }
    },
  });

  return new Response(readable, {
    headers: {
      "Content-Type": "text/plain; charset=utf-8",
      "X-Content-Type-Options": "nosniff",
      "Cache-Control": "no-store",
    },
  });
}
