import { NextRequest, NextResponse } from "next/server";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export const maxDuration = 60;

// Local Ollama on AI_002 (10.10.0.67). No paid AI services per policy.
const OLLAMA_HOST = process.env.OLLAMA_HOST || "http://10.10.0.67:11434";
const MODEL = process.env.BB_ASK_AI_MODEL || "llama3:latest";

const RATE_LIMIT_MAX = 30;
const RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000;
const buckets = new Map<string, { count: number; resetAt: number }>();

function rateLimit(key: string): { ok: boolean; remaining: number } {
  const now = Date.now();
  let b = buckets.get(key);
  if (!b || now > b.resetAt) {
    b = { count: 0, resetAt: now + RATE_LIMIT_WINDOW_MS };
    buckets.set(key, b);
  }
  if (b.count >= RATE_LIMIT_MAX) return { ok: false, remaining: 0 };
  b.count += 1;
  return { ok: true, remaining: RATE_LIMIT_MAX - b.count };
}

const SYSTEM_PROMPT = `You are BB AI, AV Beat's intelligent research assistant.

You help readers — broadcast engineers, post-production supervisors,
streaming architects, and broadcast executives — find information from the
AV Beat archive.

Tone: trade-press, factual, lightly conversational. Plain English.

Citations: whenever you reference an article, cite it inline as
[title](/news/{slug}). When you reference an event, cite as
[Event name](/show-coverage/{slug}). Use real slugs.

Constraints:
- Never reveal which AI model or vendor you run on.
- Never invent facts. If you don't know, say so.
- Keep responses short and useful — 1-3 short paragraphs unless asked for more.
- If the user asks about something outside broadcast/production technology,
  politely steer them back.
`;

interface ClientMsg { role: "user" | "assistant"; content: string }

export async function POST(req: NextRequest) {
  const ip = (req.headers.get("x-forwarded-for") || "").split(",")[0]?.trim() || "anon";
  const rl = rateLimit(ip);
  if (!rl.ok) {
    return NextResponse.json(
      { error: "Too many questions — try again in an hour." },
      { status: 429 },
    );
  }

  const body = await req.json().catch(() => null) as { messages?: ClientMsg[] } | null;
  const msgs = body?.messages || [];
  if (!msgs.length || msgs.length > 30) {
    return NextResponse.json({ error: "Invalid message list" }, { status: 400 });
  }

  // Format as Ollama chat
  const chatMessages = [
    { role: "system", content: SYSTEM_PROMPT },
    ...msgs.map((m) => ({ role: m.role, content: m.content })),
  ];

  // Retry up to 2x with backoff if Ollama returns 503/busy
  let lastErr = "";
  for (let attempt = 1; attempt <= 3; attempt++) {
    try {
      const res = await fetch(`${OLLAMA_HOST}/api/chat`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          model: MODEL,
          messages: chatMessages,
          stream: false,
          options: { temperature: 0.4, num_predict: 512 },
        }),
        signal: AbortSignal.timeout(50_000),
      });
      if (res.ok) {
        const data: any = await res.json();
        const reply = (data?.message?.content || "").trim();
        if (!reply) {
          lastErr = "empty response";
          continue;
        }
        return NextResponse.json({ reply });
      }
      const txt = await res.text();
      lastErr = `Ollama ${res.status}: ${txt.slice(0, 180)}`;
      if (res.status === 503 || /busy|pending requests exceeded/i.test(txt)) {
        // Server busy — wait and retry
        await new Promise((r) => setTimeout(r, attempt * 1500));
        continue;
      }
      break; // non-retriable
    } catch (err: any) {
      lastErr = "Upstream error: " + (err?.message || String(err));
      if (attempt < 3) await new Promise((r) => setTimeout(r, attempt * 1500));
    }
  }

  return NextResponse.json(
    { error: lastErr || "AI temporarily unavailable. Please try again in a moment." },
    { status: 502 },
  );
}
