/**
 * POST /api/page-studio/generate — Data-fed AI page generation.
 * Gathers full brand context (keywords, conversions, reviews, competitors,
 * benchmarks, GSC queries) AND scrapes the brand's website for real
 * images before calling Claude to produce a landing page scaffold that
 * uses REAL brand data, not generic placeholders.
 *
 * Images are lifted from the brand's own site via scrapeWebsiteImages
 * (24h cache, same helper Page Studio's Website Images tab uses), then
 * handed to the AI as a shortlist keyed by category (hero / team /
 * product / logo / background). The prompt tells the AI which section
 * fields accept image URLs + which categories are appropriate for
 * each, so generated pages ship with real brand photography instead
 * of empty `imageUrl: ""` placeholders.
 */

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 { BLOCKS, DEFAULT_BRANDING, createSection } from "@/lib/page-studio/section-registry";
import type { Section, Branding } from "@/lib/page-studio/section-registry";
import { gatherPageBuildContext, type PageBuildContext } from "@/lib/services/page-build-context";
import { getEnrichmentContext, formatEnrichmentForPrompt } from "@/lib/services/enrichment-context";
import { scrapeWebsiteImages, type ScrapedImage } from "@/lib/services/image-scraper";

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

const VALID_TYPES = new Set(BLOCKS.map((b) => b.id));

interface Body {
  brandId?: string;
  purpose?: string;
  targetAudience?: string;
  primaryCta?: string;
  additionalContext?: string;
  targetKeyword?: string;
}

const SYSTEM_PROMPT =
  "You are a conversion-focused landing page designer who builds pages using REAL business data. " +
  "Respond ONLY with a JSON object (no markdown, no backticks, no commentary). " +
  "Write compelling, specific copy using the actual brand data provided — never generic placeholders. " +
  "Use real phone numbers, real review quotes, real search queries, and real statistics. " +
  "When image URLs are provided, USE them in the relevant section fields (hero.backgroundImage, " +
  "about_section.imageUrl, team_section.items[].photoUrl, image_gallery.images[].url, " +
  "logo_bar.logos[].url, testimonials.items[].avatarUrl). Never leave an image field as an empty " +
  "string when a suitable image exists in the provided list. " +
  `Allowed section types: ${Array.from(VALID_TYPES).join(", ")}.`;

/**
 * Format the scraped image list for the prompt. Groups by category so
 * the AI can pick a hero image from the hero bucket, team photos from
 * the team bucket, etc. Caps each bucket at 6 to keep the prompt tight.
 */
function formatImagesForPrompt(images: ScrapedImage[]): string {
  if (images.length === 0) return "";

  const buckets = new Map<ScrapedImage["category"], ScrapedImage[]>();
  for (const img of images) {
    const bucket = buckets.get(img.category) ?? [];
    if (bucket.length < 6) bucket.push(img);
    buckets.set(img.category, bucket);
  }

  const blocks: string[] = [];
  for (const [category, bucket] of buckets) {
    if (bucket.length === 0) continue;
    blocks.push(
      `${category.toUpperCase()} (${bucket.length}):\n` +
      bucket.map((img) => `  - ${img.url}${img.altText ? ` (alt: "${img.altText}")` : ""}`).join("\n"),
    );
  }
  return blocks.join("\n");
}

function imageGuidanceBlock(images: ScrapedImage[]): string {
  if (images.length === 0) return "";
  return `
AVAILABLE BRAND IMAGES — use these real URLs in the image fields of matching sections.
Pick images whose category matches the section's intent:
  • Hero / hero_with_form / hero_with_video → use a HERO or BACKGROUND url for backgroundImage / posterUrl
  • about_section → use a TEAM or OTHER url for imageUrl
  • team_section → use TEAM urls for items[].photoUrl (one per team member)
  • testimonials → use TEAM urls for items[].avatarUrl where available
  • logo_bar / trust_bar → use LOGO urls for logos[].url
  • image_gallery → use PRODUCT or OTHER urls for images[].url

${formatImagesForPrompt(images)}

Only reuse the same URL if you genuinely have no better option — pick distinct URLs per section.`;
}

function buildDataFedPrompt(ctx: PageBuildContext, body: Body): string {
  const keyword = body.targetKeyword || ctx.topKeywords[0]?.keyword || ctx.gscQueries[0] || "services";
  const reviewBlock = ctx.reviews.slice(0, 5).map((r) =>
    `★${"★".repeat(r.rating - 1)} "${r.comment.slice(0, 150)}" — ${r.reviewerName}`
  ).join("\n") || "(no reviews available)";

  const ctaHint = ctx.conversions.callPercent > ctx.conversions.formPercent
    ? "Phone calls convert better — make the phone number prominent with a click-to-call CTA."
    : "Forms convert better — lead with an inline form CTA.";

  return `Build a high-converting landing page for ${ctx.brand.name} (${ctx.brand.industry}) in ${ctx.brand.city}${ctx.brand.state ? `, ${ctx.brand.state}` : ""}.
TARGET KEYWORD: ${keyword}

BRAND DATA:
Business: ${ctx.brand.name}, ${ctx.brand.industry}
Location: ${ctx.brand.city}${ctx.brand.state ? `, ${ctx.brand.state}` : ""}
Phone: ${ctx.brand.phone || "(not provided)"}
Website: ${ctx.brand.domain}
Services: ${ctx.brand.services.join(", ") || "(not specified)"}
${ctx.brand.shortDescription ? `Description: ${ctx.brand.shortDescription}` : ""}

CONVERSION INTELLIGENCE:
Total conversions (30d): ${ctx.conversions.total}
Top converting channel: ${ctx.conversions.topChannel}
Form submissions vs phone calls: ${ctx.conversions.formPercent}% / ${ctx.conversions.callPercent}%
${ctx.benchmarks ? `Industry avg CTR: ${ctx.benchmarks.avgCtr.toFixed(1)}%` : ""}
CTA guidance: ${ctaHint}

SEARCH DATA:
People find this brand searching for: ${ctx.gscQueries.slice(0, 10).join(", ") || "(no data)"}
Top ranking keywords: ${ctx.topKeywords.slice(0, 8).map((k) => `${k.keyword} (pos ${k.position})`).join(", ") || "(no data)"}
${ctx.competitorGaps.length ? `Content gaps vs competitors: ${ctx.competitorGaps.slice(0, 5).join(", ")}` : ""}
${ctx.topPages.length ? `Top pages: ${ctx.topPages.map((p) => p.url.replace(/^https?:\/\/[^/]+/, "")).join(", ")}` : ""}

REAL CUSTOMER REVIEWS:
${reviewBlock}

Purpose: ${body.purpose || "lead_generation"}
Target Audience: ${body.targetAudience || ctx.brand.targetAudience || "(not specified)"}
Primary CTA: ${body.primaryCta || "Get Started"}
${body.additionalContext ? `Additional Context: ${body.additionalContext}` : ""}
${ctx.brand.brandVoice ? `Brand Voice: ${ctx.brand.brandVoice}` : ""}

Generate a landing page with 6-8 sections using REAL data from above, not placeholder text:
- Hero with headline targeting "${keyword}", incorporating language from actual search queries
- Trust signals using real review count and average rating
- Services/benefits addressing what customers actually search for
- Social proof using 2-3 real review excerpts (with attribution)
- FAQ section based on common search queries
- CTA section with the actual phone number (${ctx.brand.phone || "use a form"}) and a form
${ctx.brand.phone ? `- Include the real phone number "${ctx.brand.phone}" in CTAs` : ""}

Use ONLY types from: ${Array.from(VALID_TYPES).join(", ")}.

Respond ONLY with JSON:
{"sections":[{"type":"hero","content":{"headline":"...","subheadline":"...","ctaText":"...","ctaUrl":"#contact","backgroundColor":"#1E293B","textColor":"#FFFFFF"}}],"branding":{"primaryColor":"#hex","secondaryColor":"#hex","accentColor":"#hex","fontFamily":"Inter"},"metaTitle":"...","metaDescription":"..."}`;
}

function buildFallbackPrompt(brand: { name: string; domain: string; industry: string | null }, body: Body): string {
  return `Create a landing page for:
Brand: ${brand.name}
Industry: ${brand.industry || "general"}
Domain: ${brand.domain}
Purpose: ${body.purpose || "lead_generation"}
Target Audience: ${body.targetAudience || "(not specified)"}
Primary CTA: ${body.primaryCta || "Get in touch"}
${body.additionalContext ? `Additional Context: ${body.additionalContext}` : ""}

Include 6-8 sections. Use ONLY types from: ${Array.from(VALID_TYPES).join(", ")}.
Respond ONLY with JSON:
{"sections":[{"type":"hero","content":{"headline":"...","subheadline":"...","ctaText":"...","ctaUrl":"#contact","backgroundColor":"#1E293B","textColor":"#FFFFFF"}}],"branding":{"primaryColor":"#hex","secondaryColor":"#hex","accentColor":"#hex","fontFamily":"Inter"},"metaTitle":"...","metaDescription":"..."}`;
}

function extractJson(raw: string): string {
  let s = raw.trim();
  s = s.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
  const first = s.indexOf("{");
  const last = s.lastIndexOf("}");
  if (first >= 0 && last > first) s = s.slice(first, last + 1);
  return s;
}

interface AiResponse {
  sections?: Array<{ type?: string; content?: Record<string, unknown> }>;
  branding?: Partial<Branding>;
  metaTitle?: string;
  metaDescription?: string;
}

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

  const brand = await prisma.brand.findUnique({
    where: { id: body.brandId },
    include: { organization: { include: { memberships: true } }, profile: 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 });
  }

  // Gather full brand context (non-blocking — returns null if brand missing)
  const ctx = await gatherPageBuildContext(body.brandId);
  const industry = brand.profile?.industry || brand.industry || null;

  // Scrape the brand's website for usable images. Best-effort — a
  // failed scrape still produces a page, just without real imagery.
  // The helper caches per domain (24h) so repeated generations don't
  // hammer the customer's site.
  let scrapedImages: ScrapedImage[] = [];
  if (brand.domain) {
    try { scrapedImages = await scrapeWebsiteImages(brand.domain, body.brandId); }
    catch { /* leave images empty */ }
  }

  const userPrompt = ctx
    ? buildDataFedPrompt(ctx, body)
    : buildFallbackPrompt({ name: brand.name, domain: brand.domain, industry }, body);

  // Append brand profile extras if no full context
  if (!ctx && brand.profile) {
    const extras = [
      brand.profile.shortDescription && `Short description: ${brand.profile.shortDescription}`,
      brand.profile.targetAudience && `Audience: ${brand.profile.targetAudience}`,
      brand.profile.services?.length && `Services: ${brand.profile.services.join(", ")}`,
      brand.profile.brandVoice && `Brand voice: ${brand.profile.brandVoice}`,
    ].filter(Boolean).join("\n");
    if (extras) userPrompt + `\n\nBrand profile:\n${extras}`;
  }

  // Include enrichment data for brand-specific content
  const enrichment = await getEnrichmentContext(body.brandId);
  const enrichmentBlock = formatEnrichmentForPrompt(enrichment);
  const imagesBlock = imageGuidanceBlock(scrapedImages);

  const fullPrompt = [
    userPrompt,
    enrichmentBlock ? `${enrichmentBlock}\n\nUse the enrichment documents to reference the brand's actual services, expertise, and terminology.` : "",
    imagesBlock,
  ].filter(Boolean).join("\n\n");

  const result = await callLlm(
    [{ role: "system", content: SYSTEM_PROMPT }, { role: "user", content: fullPrompt }],
    () => JSON.stringify({
      sections: [{ type: "hero", content: { headline: brand.name, subheadline: "Set ANTHROPIC_API_KEY to generate real copy.", ctaText: body.primaryCta || "Get Started", ctaUrl: "#contact", backgroundColor: DEFAULT_BRANDING.primaryColor, textColor: "#FFFFFF" } }],
      branding: DEFAULT_BRANDING,
      metaTitle: `${brand.name} — ${body.purpose}`,
      metaDescription: "Mock response — configure an LLM API key.",
    }),
    { maxTokens: 8000 },
  );

  logAiInteraction({
    userId: user.id,
    brandId: body.brandId,
    feature: "other",
    userPrompt: fullPrompt,
    systemPrompt: SYSTEM_PROMPT,
    aiResponse: result.reply,
    model: result.model,
    brandDomain: brand.domain,
  });

  let parsed: AiResponse;
  try {
    parsed = JSON.parse(extractJson(result.reply)) as AiResponse;
  } catch {
    return NextResponse.json(
      { error: "AI returned invalid JSON", raw: result.reply.slice(0, 500) },
      { status: 500 },
    );
  }

  const rawSections = Array.isArray(parsed.sections) ? parsed.sections : [];
  const sections: Section[] = rawSections
    .filter((s): s is { type: string; content?: Record<string, unknown> } =>
      !!s && typeof s.type === "string" && VALID_TYPES.has(s.type))
    .map((s) => {
      const base = createSection(s.type);
      return { ...base, content: { ...base.content, ...(s.content ?? {}) } };
    });

  if (sections.length === 0) {
    return NextResponse.json(
      { error: "AI response contained no valid sections", raw: result.reply.slice(0, 500) },
      { status: 500 },
    );
  }

  const branding: Branding = { ...DEFAULT_BRANDING, ...(parsed.branding ?? {}) };

  return NextResponse.json({
    sections,
    branding,
    metaTitle: typeof parsed.metaTitle === "string" ? parsed.metaTitle : `${brand.name} — ${body.purpose}`,
    metaDescription: typeof parsed.metaDescription === "string" ? parsed.metaDescription : "",
    provider: result.provider,
    model: result.model,
    dataFed: !!ctx,
    // Return the full scraped-image library so the editor can use it
    // for the per-section image picker without re-scraping.
    images: scrapedImages,
    imageCount: scrapedImages.length,
  });
}
