/**
 * Prediction Engine — traffic forecasting, scenario modeling, and
 * normalized performance (actual vs "what would have been").
 */

import { prisma } from "@/lib/prisma";
import { callLlm } from "@/lib/ai-avatar/llm-client";

// CTR curve by position (industry averages)
export const CTR_BY_POSITION: Record<number, number> = { 1: 28.5, 2: 15.7, 3: 11.0, 4: 8.0, 5: 7.2, 6: 5.1, 7: 4.0, 8: 3.2, 9: 2.8, 10: 2.5 };

function extractJson(raw: string): string {
  let s = raw.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);
  return s;
}

// ─── Traffic forecasting ────────────────────────────────────────────────────

export interface ForecastResult {
  baseline: { day30: number; day60: number; day90: number };
  optimistic: { day30: number; day60: number; day90: number };
  pessimistic: { day30: number; day60: number; day90: number };
  confidence: string;
  factors: string[];
  recommendation: string;
}

export async function forecastTraffic(brandId: string, metric = "clicks", days = 90): Promise<ForecastResult> {
  const brand = await prisma.brand.findUnique({ where: { id: brandId }, select: { name: true, domain: true, industry: true } });
  const d90 = new Date(Date.now() - 90 * 86400000);
  const gsc = await prisma.gscPage.aggregate({ where: { brandId, date: { gte: d90 } }, _sum: { clicks: true, impressions: true } });
  const d180 = new Date(Date.now() - 180 * 86400000);
  const gscPrev = await prisma.gscPage.aggregate({ where: { brandId, date: { gte: d180, lt: d90 } }, _sum: { clicks: true } });

  const cur = gsc._sum.clicks ?? 0;
  const prev = gscPrev._sum.clicks ?? 0;
  const growth = prev > 0 ? ((cur - prev) / prev * 100) : 0;

  // Get seasonal context if available
  const briefing = await prisma.industryBriefing.findFirst({ where: { brandId }, orderBy: { generatedAt: "desc" }, select: { seasonalInsights: true, externalEvents: true } });
  const seasonal = briefing?.seasonalInsights ? JSON.stringify(briefing.seasonalInsights).slice(0, 500) : "none";

  const result = await callLlm([
    { role: "system", content: "You are a traffic forecasting analyst. Respond ONLY with JSON." },
    { role: "user", content: `Forecast ${metric} for ${brand?.name} (${brand?.industry || "general"}).\nLast 90d: ${cur} total ${metric} (${growth.toFixed(1)}% vs prev 90d)\nSeasonal context: ${seasonal}\n\nReturn: {"baseline":{"day30":N,"day60":N,"day90":N},"optimistic":{"day30":N,"day60":N,"day90":N},"pessimistic":{"day30":N,"day60":N,"day90":N},"confidence":"high|medium|low","factors":["..."],"recommendation":"..."}` },
  ], () => JSON.stringify({ baseline: { day30: cur / 3, day60: cur / 1.5, day90: cur }, optimistic: { day30: cur / 3 * 1.2, day60: cur / 1.5 * 1.2, day90: cur * 1.2 }, pessimistic: { day30: cur / 3 * 0.8, day60: cur / 1.5 * 0.8, day90: cur * 0.8 }, confidence: "medium", factors: [], recommendation: "" }), { maxTokens: 1500 });

  try { return JSON.parse(extractJson(result.reply)) as ForecastResult; }
  catch { return { baseline: { day30: 0, day60: 0, day90: 0 }, optimistic: { day30: 0, day60: 0, day90: 0 }, pessimistic: { day30: 0, day60: 0, day90: 0 }, confidence: "low", factors: [], recommendation: "" }; }
}

// ─── Scenario modeling ──────────────────────────────────────────────────────

export interface ScenarioResult {
  currentTrajectory: { clicks30: number; clicks60: number; clicks90: number };
  withScenario: { clicks30: number; clicks60: number; clicks90: number };
  clicksGained: { day30: number; day60: number; day90: number };
  effort: string;
  confidence: string;
  implementation: string;
}

const SCENARIOS: Record<string, string> = {
  fix_technical_issues: "Fix all critical technical audit issues (broken links, crawl errors, Core Web Vitals)",
  optimize_ctr: "Optimize title tags and meta descriptions for top 10 pages to improve CTR toward position-average",
  improve_positions: "Move all striking-distance keywords (positions 4-10) to top 3 through content optimization",
  increase_content: "Publish 4 new targeted content pages per month",
  fix_speed: "Improve Core Web Vitals (LCP < 2.5s, CLS < 0.1, INP < 200ms)",
};

export async function modelScenario(brandId: string, scenario: string): Promise<ScenarioResult> {
  const brand = await prisma.brand.findUnique({ where: { id: brandId }, select: { name: true, industry: true } });
  const d30 = new Date(Date.now() - 30 * 86400000);
  const clicks30 = (await prisma.gscPage.aggregate({ where: { brandId, date: { gte: d30 } }, _sum: { clicks: true } }))._sum.clicks ?? 0;
  const desc = SCENARIOS[scenario] || scenario;

  const result = await callLlm([
    { role: "system", content: "You are an SEO scenario modeler. Respond ONLY with JSON." },
    { role: "user", content: `Model scenario for ${brand?.name} (${brand?.industry || "general"}).\nScenario: ${desc}\nCurrent: ${clicks30} clicks/30d\n\nReturn: {"currentTrajectory":{"clicks30":N,"clicks60":N,"clicks90":N},"withScenario":{"clicks30":N,"clicks60":N,"clicks90":N},"clicksGained":{"day30":N,"day60":N,"day90":N},"effort":"quick-win|moderate|major","confidence":"high|medium|low","implementation":"..."}` },
  ], () => JSON.stringify({ currentTrajectory: { clicks30, clicks60: clicks30 * 2, clicks90: clicks30 * 3 }, withScenario: { clicks30: clicks30 * 1.1, clicks60: clicks30 * 2.3, clicks90: clicks30 * 3.5 }, clicksGained: { day30: clicks30 * 0.1, day60: clicks30 * 0.3, day90: clicks30 * 0.5 }, effort: "moderate", confidence: "medium", implementation: "" }), { maxTokens: 1500 });

  try { return JSON.parse(extractJson(result.reply)) as ScenarioResult; }
  catch { return { currentTrajectory: { clicks30: 0, clicks60: 0, clicks90: 0 }, withScenario: { clicks30: 0, clicks60: 0, clicks90: 0 }, clicksGained: { day30: 0, day60: 0, day90: 0 }, effort: "unknown", confidence: "low", implementation: "" }; }
}

// ─── Normalized performance ─────────────────────────────────────────────────

export interface NormalizedData {
  actual: Array<{ date: string; value: number }>;
  normalized: Array<{ date: string; value: number }>;
  events: Array<{ date: string; name: string; impactPct: number }>;
  totalImpact: string;
}

export async function calculateNormalized(brandId: string, days = 90): Promise<NormalizedData> {
  const since = new Date(Date.now() - days * 86400000);
  const daily = await prisma.gscPage.groupBy({ by: ["date"], where: { brandId, date: { gte: since } }, _sum: { clicks: true }, orderBy: { date: "asc" } });

  const actual = daily.map((d) => ({ date: d.date.toISOString().split("T")[0], value: d._sum.clicks ?? 0 }));

  // Get correlations from latest briefing
  const briefing = await prisma.industryBriefing.findFirst({ where: { brandId }, orderBy: { generatedAt: "desc" }, select: { performanceCorrelation: true, externalEvents: true } });
  const correlations = (briefing?.performanceCorrelation as Array<{ event?: string; correlation?: string }>) ?? [];
  const events = (briefing?.externalEvents as Array<{ title?: string; severity?: string }>) ?? [];

  // Simple normalized: add back estimated negative impact
  const negativeEvents = correlations.filter((c) => c.correlation === "negative");
  const impactPct = negativeEvents.length * 5; // rough 5% per negative event
  const normalized = actual.map((d) => ({ date: d.date, value: Math.round(d.value * (1 + impactPct / 100)) }));
  const eventMarkers = events.slice(0, 5).map((e) => ({ date: actual[Math.floor(actual.length / 2)]?.date ?? "", name: e.title ?? "Event", impactPct: e.severity === "critical" ? -15 : e.severity === "major" ? -10 : -5 }));

  const totalLost = actual.reduce((s, d, i) => s + (normalized[i].value - d.value), 0);
  return { actual, normalized, events: eventMarkers, totalImpact: totalLost > 0 ? `External events cost ~${totalLost.toLocaleString()} clicks this period` : "No significant external impact detected" };
}

// ─── Position impact calculator ─────────────────────────────────────────────

export interface PositionImpact {
  keyword: string; currentPosition: number; targetPosition: number;
  currentClicks: number; estimatedClicksAtTarget: number; delta: number;
  impressions: number;
}

export async function calculatePositionImpact(brandId: string): Promise<{ impacts: PositionImpact[]; totalDelta: number }> {
  const kws = await prisma.trackedKeyword.findMany({
    where: { brandId, isActive: true },
    include: { rankings: { orderBy: { checkedAt: "desc" }, take: 1 } },
  });

  const d30 = new Date(Date.now() - 30 * 86400000);
  const impacts: PositionImpact[] = [];

  for (const kw of kws) {
    const pos = kw.rankings[0]?.position;
    if (!pos || pos > 20 || pos <= 3) continue;
    const gsc = await prisma.gscQuery.findFirst({ where: { brandId, query: kw.keyword, date: { gte: d30 } }, orderBy: { clicks: "desc" }, select: { clicks: true, impressions: true } });
    const impressions = gsc?.impressions ?? 0;
    if (impressions < 50) continue;
    const currentCtr = CTR_BY_POSITION[Math.round(pos)] ?? 2;
    const targetCtr = CTR_BY_POSITION[3] ?? 11;
    const currentClicks = Math.round(impressions * currentCtr / 100);
    const targetClicks = Math.round(impressions * targetCtr / 100);
    impacts.push({ keyword: kw.keyword, currentPosition: pos, targetPosition: 3, currentClicks, estimatedClicksAtTarget: targetClicks, delta: targetClicks - currentClicks, impressions });
  }

  impacts.sort((a, b) => b.delta - a.delta);
  return { impacts: impacts.slice(0, 15), totalDelta: impacts.reduce((s, i) => s + i.delta, 0) };
}
