/**
 * POST /api/signals/recommendations
 *
 * Generates AI-powered, signal-specific recommendations using the
 * Anthropic-backed callLlm helper. Cached per (brandId, signalId,
 * signalHash) so repeated detail-panel opens don't re-spend tokens.
 *
 * Body: { signalId, brandId, signal: { title, description, source,
 *   category, priority, relatedPage?, suggestedAction? } }
 *
 * Returns: { recommendations: Array<{ priority, title, description,
 *   suggestedFix?, fixType?: "meta" | "h1" | "schema" | "canonical" |
 *   "content" }>, impactEstimate?: string, cached?: boolean }
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { callLlm } from "@/lib/ai-avatar/llm-client";

export const dynamic = "force-dynamic";
export const maxDuration = 30;

interface SignalSummary {
  title: string;
  description: string;
  source: string;
  category: string;
  priority: string;
  relatedPage?: string | null;
  suggestedAction?: string | null;
}

interface Recommendation {
  priority: "critical" | "high" | "medium" | "low";
  title: string;
  description: string;
  suggestedFix?: string;
  fixType?: "meta" | "h1" | "schema" | "canonical" | "content" | "code";
}

interface RecommendationsResponse {
  recommendations: Recommendation[];
  impactEstimate?: string;
  cached?: boolean;
}

const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24h
const cache = new Map<string, { data: RecommendationsResponse; ts: number }>();

function cacheKey(brandId: string, signalId: string, signal: SignalSummary): string {
  // Hash by content rather than just signal ID — when the same
  // signal type re-fires for new pages we want fresh recommendations.
  return `${brandId}:${signalId}:${signal.title}:${signal.relatedPage ?? ""}`;
}

const FALLBACK_RECS: Recommendation[] = [
  {
    priority: "high",
    title: "Review the issue manually",
    description: "AI recommendations couldn't be generated right now. Open the source page below to investigate.",
  },
];

export async function POST(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const body = await req.json().catch(() => null) as {
    signalId?: string;
    brandId?: string;
    signal?: SignalSummary;
  } | null;

  if (!body?.signalId || !body?.brandId || !body?.signal) {
    return NextResponse.json({ error: "signalId, brandId, signal required" }, { status: 400 });
  }

  // Authorize
  const brand = await prisma.brand.findUnique({
    where: { id: body.brandId },
    include: { organization: { include: { memberships: true } } },
  });
  if (!brand || !brand.organization.memberships.some((m) => m.userId === user.id)) {
    return NextResponse.json({ error: "Access denied" }, { status: 403 });
  }

  const key = cacheKey(body.brandId, body.signalId, body.signal);
  const cached = cache.get(key);
  if (cached && Date.now() - cached.ts < CACHE_TTL_MS) {
    return NextResponse.json({ ...cached.data, cached: true });
  }

  const sys = `You are an SEO + UX expert. Given a single platform signal (an issue, opportunity, win, or revenue alert detected on a website), generate 3-5 specific, actionable recommendations the user can apply right now.

For each recommendation, return:
- priority: critical | high | medium | low
- title: a short imperative ("Add Meta Description")
- description: 1-2 sentences explaining what to do and why
- suggestedFix (optional): the EXACT text or code the user should add (e.g. a meta description string, an H1 string, a canonical URL, a JSON-LD schema snippet)
- fixType (optional): meta | h1 | schema | canonical | content | code

Also return an impactEstimate: a 1-2 sentence forecast of expected improvement (clicks, conversions, traffic, etc.) if the recommendations are applied.

Return ONLY valid JSON in this shape:
{ "impactEstimate": "string", "recommendations": [...] }
No markdown, no commentary outside the JSON.

Brand: ${brand.name} (${brand.domain})`;

  const userMsg = `Signal title: ${body.signal.title}
Description: ${body.signal.description}
Source: ${body.signal.source}
Category: ${body.signal.category}
Priority: ${body.signal.priority}
${body.signal.relatedPage ? `Affected page: ${body.signal.relatedPage}` : ""}
${body.signal.suggestedAction ? `Existing suggestion: ${body.signal.suggestedAction}` : ""}`;

  let result: RecommendationsResponse;
  try {
    const llm = await callLlm(
      [
        { role: "system", content: sys },
        { role: "user", content: userMsg },
      ],
      () => JSON.stringify({ impactEstimate: undefined, recommendations: FALLBACK_RECS }),
      { maxTokens: 1500 },
    );

    let text = llm.reply.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
    const f = text.indexOf("{"), l = text.lastIndexOf("}");
    if (f >= 0 && l > f) text = text.slice(f, l + 1);
    const parsed = JSON.parse(text);
    const recs = Array.isArray(parsed?.recommendations) ? parsed.recommendations : FALLBACK_RECS;
    result = {
      recommendations: recs.slice(0, 5).map((r: Record<string, unknown>) => ({
        priority: (typeof r.priority === "string" && ["critical", "high", "medium", "low"].includes(r.priority) ? r.priority : "medium") as Recommendation["priority"],
        title: typeof r.title === "string" ? r.title.slice(0, 200) : "Recommendation",
        description: typeof r.description === "string" ? r.description.slice(0, 800) : "",
        suggestedFix: typeof r.suggestedFix === "string" ? r.suggestedFix.slice(0, 2000) : undefined,
        fixType: typeof r.fixType === "string" && ["meta", "h1", "schema", "canonical", "content", "code"].includes(r.fixType)
          ? r.fixType as Recommendation["fixType"]
          : undefined,
      })),
      impactEstimate: typeof parsed?.impactEstimate === "string" ? parsed.impactEstimate.slice(0, 500) : undefined,
    };
  } catch (err) {
    console.error("[signals/recommendations] LLM failed:", err instanceof Error ? err.message : err);
    result = { recommendations: FALLBACK_RECS };
  }

  cache.set(key, { data: result, ts: Date.now() });
  return NextResponse.json(result);
}
