/**
 * POST /api/content-audit/page-recommendations
 * ─────────────────────────────────────────────
 * Body: { brandId, pageUrl, performance, behavior, technical }
 *
 * Generates 3-5 AI recommendations for a specific page, grounded in
 * the performance + behavior + technical blocks the caller just
 * received from /api/content-audit/page-detail. The POST shape lets
 * the client ship the already-fetched data through rather than
 * re-querying — saves ~200ms on every expansion.
 *
 * Cached for 24h in a SiteEvent row (eventType = "content_rec_cache")
 * so clicking the same row twice doesn't burn an LLM call. The cache
 * key is the normalised page path; the JSON payload lives in
 * eventData. Reusing SiteEvent here instead of adding a
 * ContentRecommendation model keeps the schema surface tight and
 * dodges the migration risk that's reverted other commits on this
 * branch.
 *
 * Returns { recommendations, cached: boolean, generatedAt }.
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { callLlm } from "@/lib/ai-avatar/llm-client";
import { logAiInteraction } from "@/lib/services/ai-interaction-log";
import { normalizePath } from "@/lib/services/url-normalize";

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

const CACHE_EVENT_TYPE = "content_rec_cache";
const CACHE_TTL_HOURS = 24;

interface Recommendation {
  title: string;
  description: string;
  currentState: string;
  suggestedChange: string;
  impact: string;
  effort: "quick_win" | "moderate" | "major";
  category: "title" | "meta" | "content" | "schema" | "internal_links" | "cta" | "speed" | "ux";
}

interface RequestBody {
  brandId?: string;
  pageUrl?: string;
  performance?: {
    clicks28d: number; impressions28d: number; avgPosition: number | null; ctr: number | null;
    trend: string; trendPct: number;
  };
  behavior?: {
    pageViews: number; landingSessions: number; bounceRate: number;
    outboundClicks: number; formStarts: number; conversions: number;
    siteTagInstalled: boolean;
  };
  technical?: {
    openIssues: Array<{ title: string; severity: string; category: string }>;
    performance: { lcpMs: number; inpMs: number; clsVal: number; performance: number } | null;
    deadPage: { priority: string; status: string } | null;
  };
  /** Force a cache bypass — "Regenerate" button. */
  refresh?: boolean;
}

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 RequestBody | null;
  if (!body?.brandId || !body?.pageUrl) {
    return NextResponse.json({ error: "brandId and pageUrl required" }, { status: 400 });
  }

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

  const normalized = normalizePath(body.pageUrl);

  // ── Cache check (SiteEvent-backed, 24h TTL) ───────────────────────
  // When `refresh` is true the cache is bypassed entirely so the
  // "Regenerate" button forces a fresh LLM call.
  if (!body.refresh) {
    const cutoff = new Date(Date.now() - CACHE_TTL_HOURS * 60 * 60 * 1000);
    const cached = await prisma.siteEvent.findFirst({
      where: {
        brandId: body.brandId,
        eventType: CACHE_EVENT_TYPE,
        pageUrl: normalized,
        timestamp: { gte: cutoff },
      },
      orderBy: { timestamp: "desc" },
    });
    if (cached) {
      const data = cached.eventData as { recommendations?: Recommendation[] } | null;
      if (data?.recommendations?.length) {
        return NextResponse.json({
          recommendations: data.recommendations,
          cached: true,
          generatedAt: cached.timestamp,
        });
      }
    }
  }

  // ── Build the prompt with whatever the caller provided ────────────
  const perf = body.performance;
  const beh = body.behavior;
  const tech = body.technical;

  const perfLine = perf
    ? `clicks=${perf.clicks28d}, impressions=${perf.impressions28d}, CTR=${perf.ctr ?? "?"}%, avgPosition=${perf.avgPosition ?? "?"}, trend=${perf.trend} (${perf.trendPct}%)`
    : "no performance data";
  const behLine = beh?.siteTagInstalled
    ? `pageviews=${beh.pageViews}, landings=${beh.landingSessions}, bounceRate=${beh.bounceRate}%, outbound=${beh.outboundClicks}, formInteractions=${beh.formStarts}, conversions=${beh.conversions}`
    : "Site Tag not installed — no behavioural data";
  const techLine = tech
    ? [
        tech.openIssues.length > 0 ? `${tech.openIssues.length} open tech issues (${tech.openIssues.slice(0, 3).map((i) => `${i.severity}:${i.category}`).join(", ")})` : "no open tech issues",
        tech.performance ? `LCP=${tech.performance.lcpMs}ms, INP=${tech.performance.inpMs}ms, CLS=${tech.performance.clsVal}` : "no Core Web Vitals data",
        tech.deadPage ? "page flagged as dead" : null,
      ].filter(Boolean).join("; ")
    : "no technical data";

  const prompt = `You are a senior SEO consultant analyzing a specific page on ${brand.domain} (${brand.industry || "general"} business).

Page: ${normalized}
Performance (28d): ${perfLine}
Behavior (30d): ${behLine}
Technical: ${techLine}

Provide 3-5 specific, actionable recommendations. Each MUST include:
- title: short action (under 50 chars)
- description: 2-3 sentences explaining what to do and why
- currentState: what exists now (1 sentence)
- suggestedChange: specific concrete change
- impact: estimated improvement quantified (e.g. "+15-25% CTR", "+40% conversions")
- effort: one of "quick_win" | "moderate" | "major"
- category: one of "title" | "meta" | "content" | "schema" | "internal_links" | "cta" | "speed" | "ux"

Prioritise:
- Fixing anything with trend="declining" or open technical issues first
- CTR optimisation when CTR is below 2%
- Content depth when position is 5-20 (page 1 within reach)
- Conversion path improvements when traffic exists but conversions are low
- Schema and internal linking as supporting wins

Respond ONLY with a JSON array. No markdown fences, no preamble.`;

  const llm = await callLlm(
    [
      { role: "system", content: "You are a senior SEO consultant. Respond ONLY with a valid JSON array — no markdown, no commentary." },
      { role: "user", content: prompt },
    ],
    () => "[]",
    { maxTokens: 2000 },
  );

  logAiInteraction({
    brandId: body.brandId,
    feature: "content_recommendations",
    userPrompt: prompt,
    aiResponse: llm.reply,
    model: llm.model,
    brandDomain: brand.domain,
  });

  // Parse defensively. The LLM client can return stray fences or
  // preamble when it falls back to OpenAI — strip both patterns.
  let recommendations: Recommendation[] = [];
  try {
    let s = llm.reply.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
    const f = s.indexOf("["), l = s.lastIndexOf("]");
    if (f >= 0 && l > f) s = s.slice(f, l + 1);
    const parsed = JSON.parse(s);
    if (Array.isArray(parsed)) {
      recommendations = parsed
        .filter((r): r is Recommendation => typeof r?.title === "string")
        .slice(0, 8)
        .map((r) => ({
          title: String(r.title).slice(0, 120),
          description: String(r.description ?? "").slice(0, 500),
          currentState: String(r.currentState ?? "").slice(0, 300),
          suggestedChange: String(r.suggestedChange ?? "").slice(0, 500),
          impact: String(r.impact ?? "").slice(0, 120),
          effort: (["quick_win", "moderate", "major"].includes(r.effort) ? r.effort : "moderate") as Recommendation["effort"],
          category: (["title", "meta", "content", "schema", "internal_links", "cta", "speed", "ux"].includes(r.category) ? r.category : "content") as Recommendation["category"],
        }));
    }
  } catch (err) {
    console.error("[content-audit/page-recommendations] LLM parse failed:", err instanceof Error ? err.message : err);
  }

  // ── Persist to cache. Best-effort — a DB write failure shouldn't
  //    break the response. ────────────────────────────────────────
  if (recommendations.length > 0) {
    try {
      await prisma.siteEvent.create({
        data: {
          brandId: body.brandId,
          pageUrl: normalized,
          eventType: CACHE_EVENT_TYPE,
          eventData: { recommendations } as object,
        },
      });
    } catch (err) {
      console.error("[content-audit/page-recommendations] cache write failed:", err instanceof Error ? err.message : err);
    }
  }

  return NextResponse.json({ recommendations, cached: false, generatedAt: new Date() });
}
