/**
 * AI Analysis Runner — sends the full data package to Claude and
 * returns structured findings, recommendations, predictions, and
 * narrative. Falls back to empty results on failure.
 */

import { callLlm } from "@/lib/ai-avatar/llm-client";
import type { AnalysisDataPackage } from "./analysis-data-package";
import type { Finding, Prediction, AnalysisRecommendation } from "./analysis-schema";

export interface AiAnalysisResult {
  executiveSummary: string;
  kpiCommentary: Record<string, string>;
  findings: Finding[];
  recommendations: AnalysisRecommendation[];
  predictions: Prediction[];
  correlations: Array<{ event: string; metric: string; direction: string; explanation: string }>;
  competitiveInsight: string;
  behavioralInsight: string;
  strategicOutlook: string;
}

function buildPrompt(data: AnalysisDataPackage): string {
  const { brand: b, gsc, ga4, topPages, topQueries, audit, benchmarks, successRates, memoryContext, competitorSummary, industryBriefingSummary, industryEvents, backlinks, conversions, heatmap, lifecycle, siteTag } = data;
  const bmStr = Object.entries(benchmarks).slice(0, 10).map(([k, v]) => `${k.replace(/_/g, " ")}: ${v.value}`).join(", ");
  const srStr = successRates.slice(0, 5).map((r) => `${r.type}: ${Math.round((r.improved / Math.max(r.total, 1)) * 100)}% success, +${r.avgImpact}%`).join("; ");
  const pagesStr = topPages.slice(0, 8).map((p) => `${p.url}: ${p.clicks} clicks, pos ${p.position}`).join("\n");
  const queriesStr = topQueries.slice(0, 10).map((q) => `"${q.query}": ${q.clicks} clicks, pos ${q.position}, ${q.impressions} imps`).join("\n");
  const eventsStr = industryEvents.slice(0, 5).map((e) => `- ${e.title} (${e.severity})`).join("\n");

  // First-Party Site Tag block — cookieless, server-side endpoint that
  // captures the visitors GA4 loses to ad-blockers / ITP / consent
  // rejection. Skipped entirely when the brand has no TrackedSite, so
  // the prose never hallucinates Site Tag numbers for brands that
  // never installed it.
  let siteTagStr = "N/A (Site Tag not installed)";
  if (siteTag) {
    const ageLabel = siteTag.installDays == null
      ? "install date unknown"
      : `${siteTag.installDays} day${siteTag.installDays === 1 ? "" : "s"} since install`;
    const convByTypeStr = Object.entries(siteTag.conversionsByType)
      .map(([k, v]) => `${k}=${v}`)
      .sort()
      .join(", ") || "none";
    const topConvPages = siteTag.topConvertingPages.length > 0
      ? siteTag.topConvertingPages.map((p) => `${p.url} (${p.count})`).join("; ")
      : "none";
    siteTagStr = [
      `${ageLabel} · ${data.dateRange.days}-day window`,
      `Page views: ${siteTag.pageViews}, Sessions: ${siteTag.sessions}`,
      `form_start: ${siteTag.formStarts}, form_submit: ${siteTag.formSubmits}, click_to_call: ${siteTag.clickToCalls}, sms_click: ${siteTag.smsClicks}`,
      `booking_widget_opened: ${siteTag.bookingOpens}, booking_submit: ${siteTag.bookingSubmits}, booking_confirmed: ${siteTag.bookingConfirmed}, outbound_click: ${siteTag.outboundClicks}`,
      `Conversions: ${siteTag.conversionsTotal} total, ${siteTag.conversionRate}% rate (conversions ÷ sessions). By type: ${convByTypeStr}`,
      `Top converting pages: ${topConvPages}`,
    ].join("\n");
  }

  return `Analyze ${b.name} (${b.domain}) in ${b.industry || "general"} for ${data.dateRange.days} days (${data.dateRange.start} to ${data.dateRange.end}).

SEARCH PERFORMANCE:
Clicks: ${gsc.clicks} (prev: ${gsc.prevClicks}), Impressions: ${gsc.impressions} (prev: ${gsc.prevImpressions}), Avg Position: ${gsc.avgPosition}, CTR: ${gsc.ctr}%
${ga4 ? `Sessions: ${ga4.sessions} (prev: ${ga4.prevSessions}), Conversions: ${ga4.conversions}, Bounce: ${ga4.bounceRate}%, New Users: ${ga4.newUsers}` : "No GA4 data"}

TOP PAGES:\n${pagesStr || "No data"}

TOP QUERIES:\n${queriesStr || "No data"}

TECHNICAL: ${audit ? `Health ${audit.healthScore}/100, ${audit.errors} errors` : "No audit data"}
BENCHMARKS: ${bmStr || "None available"}
RECOMMENDATION SUCCESS RATES: ${srStr || "None available"}
COMPETITORS: ${competitorSummary || "None tracked"}
EXTERNAL FACTORS: ${industryBriefingSummary || "None"}\n${eventsStr}
BACKLINKS: ${backlinks ? `${backlinks.total} total, +${backlinks.new} new, -${backlinks.lost} lost` : "N/A"}
CONVERSIONS: ${conversions ? `${conversions.total} total, top channel: ${conversions.topChannel}, ${conversions.forms} forms, ${conversions.calls} calls` : "N/A"}
BEHAVIOR: ${heatmap ? `Avg scroll ${heatmap.avgScroll ?? "?"}%, ${heatmap.rageClicks} rage clicks` : "N/A"}
PAGES: ${lifecycle ? `${lifecycle.newPages} new, ${lifecycle.pages404} 404s` : "N/A"}

FIRST-PARTY SITE TAG DATA (meSEO Site Tag — cookieless, server-side endpoint that bypasses ad-blockers, ITP, and consent cookie rejections, capturing visitors GA4 misses):
${siteTagStr}

HISTORY:\n${memoryContext || "None"}

WRITTEN REPORT TONE:
This analysis powers the "Written Report" tab — a structured document
the user reads like a consulting deliverable, not a chat answer. Write
prose that is specific, data-dense, and confident. Every claim cites
a concrete number from the data above. No generic advice ("improve
your SEO"). No placeholder language. When a metric is missing, say
so explicitly — do not fabricate a value.

PROSE FIELD REQUIREMENTS:
- executiveSummary: 2-3 PARAGRAPHS separated by \\n\\n. Paragraph 1:
  the overall performance verdict (direction + headline numbers vs
  prior period). Paragraph 2: what's working (wins, growing segments,
  quality wins like rising conversion rate despite traffic dip).
  Paragraph 3: what's at risk (declining queries, technical debt,
  competitive pressure). Write like a strategist briefing an exec —
  direct, specific, no hedging.
- competitiveInsight: 1 full paragraph (3-4 sentences) grounded in
  the COMPETITORS data block above. Reference competitor names and
  specific movements.
- behavioralInsight: 1 full paragraph (3-4 sentences) grounded in
  the BEHAVIOR / CONVERSIONS data. Cite rage clicks, conversion
  channel mix, scroll depth, etc. When the FIRST-PARTY SITE TAG DATA
  block is present, anchor this paragraph in the Site Tag funnel
  (page_view → form_start → form_submit and booking_widget_opened →
  booking_submit → booking_confirmed), the soft-conversion mix
  (click_to_call, sms_click, outbound_click), and the top converting
  pages — these are the behavioral signals GA4 cannot report.
- strategicOutlook: 1 full paragraph (4-6 sentences). Forward-looking.
  Call out the single biggest lever for the next 90 days with the
  expected upside in concrete numbers ("+15-20% CTR on Spanish
  pages" not "improved visibility").

FIRST-PARTY SITE TAG ANALYSIS RULES (only when the FIRST-PARTY SITE TAG DATA block is non-null):
- At least ONE finding MUST cite Site Tag data — a funnel conversion rate, a
  specific soft-conversion count (click_to_call, sms_click, outbound_click), or
  a top converting page. Site Tag measures what GA4 misses to ad-blockers / ITP
  / consent rejections, and that is a first-class signal the report must
  surface.
- When Site Tag has been installed for 30+ days AND both Site Tag sessions and
  GA4 sessions are present, treat the gap (Site Tag − GA4) as the ad-blocker /
  consent-opt-out impact — an INSIGHT about measurement coverage, never a
  tracking failure.
- When Site Tag has been installed for LESS than 30 days, do NOT compare Site
  Tag sessions to GA4 sessions, and do NOT compute percentage deltas between
  them. The Site Tag numbers are baseline-forming; the report may still cite
  Site Tag counts on their own (e.g. "23 click_to_call events in the period")
  but must not assert Site Tag is undercounting vs GA4.
- Funnel analysis: form_start → form_submit and booking_widget_opened →
  booking_submit → booking_confirmed are the two canonical funnels. Compute
  step-drop rates only when the upstream count is ≥ 20; otherwise cite raw
  counts without percentages.
- Revenue/CRO recommendations: when top converting pages are present, at
  least ONE recommendation should direct SEO/CRO investment toward those
  URLs rather than generic advice about "the website".

FINDING & RECOMMENDATION REQUIREMENTS:
- Findings: minimum 5, across 3+ categories. Each description is 2-3
  sentences, every sentence cites a specific page / keyword / number.
  Evidence field names the underlying data (e.g. "GSC: 28-day clicks
  down from 12,450 to 10,312").
- Recommendations: minimum 5, ordered by priority. Each expectedImpact
  quantifies the uplift ("+12-18% organic clicks within 30 days").
  Each steps array has 3-5 concrete actions a marketer can execute.
- Predictions: minimum 2. basis cites the trend data supporting the
  projection.

Return JSON (no markdown, no preamble):
{"executiveSummary":"2-3 paragraphs, \\n\\n separated","kpiCommentary":{"clicks":"why clicks changed","impressions":"...","position":"...","ctr":"...","sessions":"...","conversions":"..."},"findings":[{"id":"f1","type":"insight|anomaly|trend|risk|opportunity","severity":"critical|high|medium|low|info","title":"...","description":"2-3 sentences with numbers","evidence":"data points","impact":"business impact","impactLevel":"high|medium|low","category":"performance|technical|content|competitive|opportunity","recommendedAction":"..."}],"recommendations":[{"title":"...","description":"...","priority":"critical|high|medium|low","priorityLabel":"P1|P2|P3|P4","effort":"low|medium|high","effortLabel":"quick-win|moderate|major","category":"performance|technical|content|competitive","expectedImpact":"quantified uplift","steps":["concrete action 1","concrete action 2"]}],"predictions":[{"metric":"...","current":N,"predicted":N,"confidence":"high|medium|low","timeframe":"30|60|90 days","basis":"...","predictionType":"trajectory|seasonal|risk|opportunity","outlook":"..."}],"correlations":[{"event":"...","metric":"...","direction":"positive|negative","explanation":"..."}],"competitiveInsight":"1 paragraph, 3-4 sentences","behavioralInsight":"1 paragraph, 3-4 sentences","strategicOutlook":"1 paragraph, 4-6 sentences"}

CRITICAL:
- NEVER fabricate numbers. Use only values that appear in the data above.
- NEVER write generic advice. Every finding + recommendation must cite specifics.
- If a metric isn't available, say "Data not available" — don't make one up.
- Min 5 findings across 3+ categories. Min 5 recommendations by priority. Min 2 predictions.

TRACKING-FAILURE GUARDRAIL:
- Do NOT generate a finding titled or worded as "tracking failure", "GA4 tracking
  failure", "session drop", or any equivalent — UNLESS GA4 sessions are exactly 0
  AND the SEARCH PERFORMANCE block above shows non-zero clicks for the same period.
  Only that combination is a real tracking issue.
- Site Tag vs GA4 session differences are EXPECTED when Site Tag is 30+ days
  mature — Site Tag captures ad-blocker / ITP / consent-opt-out visitors that
  GA4 loses. Report the gap as a measurement insight, not a tracking failure.
  When Site Tag is under 30 days old, do NOT compare the two at all.
- A connected GA4 with normal sessions = tracking is fine. Do NOT downgrade to
  "needs investigation" without specific evidence of zero or near-zero data.`;
}

export async function runAiAnalysis(data: AnalysisDataPackage): Promise<AiAnalysisResult | null> {
  const result = await callLlm(
    [
      {
        role: "system",
        content:
          "You are a senior SEO strategist writing a consulting-grade performance report. " +
          "Every claim cites a specific number from the data provided — no fabrication, no generic advice. " +
          "Tone: direct, confident, data-dense, no hedging. Write the way an agency principal briefs a client on quarterly performance. " +
          "Respond ONLY with valid JSON — no markdown fences, no preamble.",
      },
      { role: "user", content: buildPrompt(data) },
    ],
    () => "", // empty mock → triggers null return below
    { maxTokens: 8000 },
  );

  if (result.provider === "mock" || !result.reply.trim()) return null;

  try {
    let s = result.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) as AiAnalysisResult;

    // Validate minimum structure
    if (!parsed.executiveSummary || !Array.isArray(parsed.findings)) return null;

    // Ensure finding IDs
    parsed.findings = parsed.findings.map((fi, i) => ({ ...fi, id: fi.id || `ai_f${i + 1}` }));

    return parsed;
  } catch (err) {
    console.error("[ai-analysis] Failed to parse Claude response", err);
    return null;
  }
}
