/**
 * گفت‌وگوی AI Beauty: POST /api/ai/chat  { messages: [{ role, content }] }  ← پاسخ متنی به‌صورت جریانی (stream)
 *
 * امنیت و هزینه:
 * - فقط از همین سایت (بررسی Origin)، با محدودیت تعداد پیام برای هر IP و طول هر پیام
 * - مهمان (بدون ورود) فقط چند پیام اول را می‌فرستد (قابل تنظیم در پنل)؛ بعد باید ثبت‌نام/ورود کند تا گفت‌وگو ادامه یابد
 * - کلید API فقط در سرور است؛ مرورگر فقط متن پاسخ را می‌گیرد
 * - دستیار فقط محصولات واقعی کاتالوگ را با لینک /p/{id} پیشنهاد می‌کند
 */
import { z } from "zod";
import { db } from "@/lib/db";
import { formatToman } from "@/lib/format";
import { site } from "@/lib/site";
import { getCategoryIndex } from "@/server/services/catalog";
import { aiEngine, type SystemPart } from "@/server/services/ai";
import { getCurrentUser } from "@/server/auth/session";

export const dynamic = "force-dynamic";

const bodySchema = z.object({
  messages: z
    .array(z.object({ role: z.enum(["user", "assistant"]), content: z.string().trim().min(1).max(1500) }))
    .min(1)
    .max(20)
    .refine((m) => m[0].role === "user" && m[m.length - 1].role === "user", "ساختار گفت‌وگو نامعتبر است"),
});

// ── محدودیت تعداد پیام (در حافظه؛ برای چند سرور باید به Redis منتقل شود) ──
const WINDOW_MS = 10 * 60_000;
const MAX_PER_WINDOW = 20;
const hits = new Map<string, number[]>();
function rateLimited(ip: string) {
  const now = Date.now();
  const list = (hits.get(ip) ?? []).filter((t) => now - t < WINDOW_MS);
  list.push(now);
  hits.set(ip, list);
  if (hits.size > 5000) for (const [k, v] of hits) if (!v.some((t) => now - t < WINDOW_MS)) hits.delete(k);
  return list.length > MAX_PER_WINDOW;
}

// ── فهرست فشرده کاتالوگ برای دستیار (۵ دقیقه کش؛ ثابت ماندن متن، کش پرامپت را فعال نگه می‌دارد) ──
let catalogCache: { at: number; text: string } | undefined;
async function catalogText() {
  if (catalogCache && Date.now() - catalogCache.at < 5 * 60_000) return catalogCache.text;
  const [products, cats] = await Promise.all([
    db.product.findMany({
      where: { isActive: true },
      orderBy: { id: "asc" },
      select: {
        id: true,
        name: true,
        summary: true,
        minPrice: true,
        ratingAvg: true,
        categoryId: true,
        brand: { select: { name: true } },
        skinTypes: { select: { name: true } },
        variants: { where: { isActive: true }, select: { stock: true, colorName: true, size: true } },
      },
    }),
    getCategoryIndex(),
  ]);
  const lines = products.map((p) => {
    const cat = cats.path(p.categoryId).map((c) => c.name).join(" › ");
    const skins = p.skinTypes.map((s) => s.name).join("، ") || "همه";
    const stock = p.variants.some((v) => v.stock > 0) ? "موجود" : "ناموجود";
    const options = [...new Set(p.variants.map((v) => v.colorName || v.size).filter(Boolean))].slice(0, 6).join("، ");
    return `- [${p.id}] ${p.name} | برند ${p.brand.name} | ${cat} | پوست: ${skins} | از ${formatToman(p.minPrice)} | امتیاز ${p.ratingAvg.toFixed(1)} | ${stock}${options ? ` | گزینه‌ها: ${options}` : ""} | ${p.summary}`;
  });
  const catLinks = cats.all.map((c) => `- ${c.name}: ${cats.href(c.id)}`).join("\n");
  const text = `کاتالوگ محصولات زهورا (شناسه در کروشه):\n${lines.join("\n")}\n\nلینک دسته‌ها:\n${catLinks}`;
  catalogCache = { at: Date.now(), text };
  return text;
}

// شمارش پیام مهمان‌ها برای هر IP در ۲۴ ساعت (تا با پاک کردن تاریخچه گفت‌وگو دور زده نشود)
const GUEST_WINDOW_MS = 24 * 3_600_000;
const guestHits = new Map<string, number[]>();
function guestCount(ip: string, add: boolean) {
  const now = Date.now();
  const list = (guestHits.get(ip) ?? []).filter((t) => now - t < GUEST_WINDOW_MS);
  if (add) list.push(now);
  guestHits.set(ip, list);
  return list.length;
}

function systemPrompt(assistantName: string, extra: string, catalog: string): SystemPart[] {
  const base = `تو «${assistantName}» هستی، مشاور زیبایی فروشگاه آنلاین ${site.name} (آرایشی، مراقبت پوست، مو، عطر و بدن). همیشه فارسی، گرم، کوتاه و کاربردی جواب بده.

قواعد:
- فقط محصولاتی را پیشنهاد کن که در کاتالوگ زیر هستند؛ هرگز محصول یا برند دیگری را نام نبر و قیمت یا ویژگی از خودت نساز.
- هر محصول را با لینک Markdown به شکل [نام محصول](/p/شناسه) بیاور. برای دسته‌ها از لینک دسته استفاده کن.
- اگر نوع پوست یا نیاز کاربر روشن نیست، یک سؤال کوتاه بپرس. حداکثر ۳ تا ۴ محصول پیشنهاد کن و دلیل کوتاه هر کدام را بگو.
- محصولات ناموجود را پیشنهاد نکن مگر کاربر بپرسد.
- توصیه پزشکی نکن؛ برای مشکلات پوستی جدی (جوش شدید، حساسیت، بارداری) مراجعه به پزشک را پیشنهاد کن.
- درباره سفارش، ارسال و پرداخت: ارسال با پست پیشتاز، تیپاکس و پیک تهران است؛ پیگیری سفارش از ${"/track"}. اطلاعات شخصی یا شماره کارت نخواه.
- اگر سؤال ربطی به زیبایی یا فروشگاه ندارد، مؤدبانه به موضوع فروشگاه برگرد.
- قالب پاسخ: متن ساده با فهرست «- » و لینک؛ بدون جدول و بدون تیتر.`;
  return [
    { text: base },
    // کاتالوگ ثابت است و (در Claude) کش می‌شود؛ هزینه و سرعت بهتر در پیام‌های بعدی
    { text: catalog, cache: true },
    ...(extra.trim() ? [{ text: `دستورالعمل مدیر فروشگاه:\n${extra.trim()}` }] : []),
  ];
}

export async function POST(req: Request) {
  // فقط درخواست از خود سایت
  const origin = req.headers.get("origin");
  const host = req.headers.get("host");
  if (!origin || !host || new URL(origin).host !== host) return Response.json({ error: "درخواست نامعتبر" }, { status: 403 });

  const ip = (req.headers.get("x-forwarded-for") ?? "").split(",")[0].trim() || req.headers.get("x-real-ip") || "local";
  if (rateLimited(ip)) return Response.json({ error: "تعداد پیام‌ها زیاد شد؛ چند دقیقه دیگر دوباره امتحان کنید" }, { status: 429 });

  const parsed = bodySchema.safeParse(await req.json().catch(() => null));
  if (!parsed.success) return Response.json({ error: "پیام نامعتبر است" }, { status: 400 });

  const ai = await aiEngine();
  if (!ai || !ai.settings.chatEnabled) return Response.json({ error: "دستیار هوشمند فعلاً در دسترس نیست" }, { status: 503 });
  const { engine, settings } = ai;

  // مهمان: بعد از چند پیام اول، برای ادامه باید وارد شود
  const user = await getCurrentUser();
  if (!user) {
    const inHistory = parsed.data.messages.filter((m) => m.role === "user").length;
    if (inHistory > settings.guestMessages || guestCount(ip, false) >= settings.guestMessages) {
      return Response.json({ needsAuth: true, error: "برای ادامه گفت‌وگو با دستیار، وارد شوید یا ثبت‌نام کنید (فقط با شماره موبایل)." }, { status: 401 });
    }
    guestCount(ip, true);
  }
  const encoder = new TextEncoder();
  const stream = new ReadableStream<Uint8Array>({
    async start(controller) {
      try {
        const chunks = engine.streamChat({
          maxTokens: 4000,
          system: systemPrompt(settings.assistantName, settings.instructions, await catalogText()),
          messages: parsed.data.messages,
        });
        for await (const text of chunks) controller.enqueue(encoder.encode(text));
      } catch (e) {
        console.error("[ai-chat]", e);
        controller.enqueue(encoder.encode("\n\n⚠ اتصال به دستیار برقرار نشد. لطفاً کمی بعد دوباره امتحان کنید."));
      } finally {
        controller.close();
      }
    },
  });
  return new Response(stream, { headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" } });
}
