import { prisma } from "@/lib/prisma";
import { scoreBand } from "@/lib/predictive-scoring/score-session";
import type { PredictiveConversionScoring, ScoreBand } from "../types";

// Map channelGroup (GA4 taxonomy) → compact display name
function normalizeChannelGroup(cg: string | null | undefined): string {
  if (!cg) return "Other";
  const lower = cg.toLowerCase();
  if (lower.includes("paid search")) return "Paid Search";
  if (lower.includes("paid social")) return "Paid Social";
  if (lower.includes("organic search")) return "Organic Search";
  if (lower.includes("direct")) return "Direct";
  if (lower.includes("referral")) return "Referral";
  if (lower.includes("email")) return "Email";
  if (lower.includes("display")) return "Display";
  return "Other";
}

// Normalize raw referrer string to AI engine display name
function aiEngineFromReferrer(referrer: string | null | undefined): string | null {
  if (!referrer) return null;
  const lower = referrer.toLowerCase();
  if (lower.includes("chatgpt") || lower.includes("chat.openai") || lower.includes("openai")) return "ChatGPT";
  if (lower.includes("perplexity")) return "Perplexity";
  if (lower.includes("gemini") || lower.includes("bard")) return "Gemini";
  if (lower.includes("bing") || lower.includes("copilot.microsoft")) return "Microsoft Copilot";
  if (lower.includes("claude") || lower.includes("anthropic")) return "Claude";
  return null;
}

const PATENT_RELEVANCE =
  "Real-time behavioral scoring during active sessions enables proactive conversion optimization that traditional analytics — which report only after the fact — cannot deliver. This capability relies on cookieless session continuity provided by the meSEO Site Tag.";

export async function aggregatePredictiveConversionScoring(
  brandId: string,
  windowDays = 30,
): Promise<PredictiveConversionScoring | null> {
  const trackedSite = await prisma.trackedSite.findFirst({
    where: { brandId },
    select: { id: true },
  });
  if (!trackedSite) return null;

  const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1000);
  const active30Min = new Date(Date.now() - 30 * 60 * 1000);

  const [sessions, activeSessions, conversions] = await Promise.all([
    prisma.trackedSession.findMany({
      where: {
        trackedSiteId: trackedSite.id,
        startedAt: { gte: since },
        predictedConversionScore: { not: null },
      },
      select: {
        id: true,
        sessionId: true,
        predictedConversionScore: true,
        channelGroup: true,
        referrer: true,
        msclkid: true,
      },
    }),
    prisma.trackedSession.findMany({
      where: {
        trackedSiteId: trackedSite.id,
        lastSeenAt: { gte: active30Min },
        predictedConversionScore: { not: null },
      },
      select: { predictedConversionScore: true },
    }),
    // intentionally unfiltered: audit report; needs broader conversion history
    prisma.siteConversion.findMany({
      where: {
        brandId,
        timestamp: { gte: since },
        isDuplicateOf: null,
        invalidatedReason: null,
      },
      select: { sessionId: true },
    }),
  ]);

  if (sessions.length === 0) return null;

  const convertedSessionIds = new Set(conversions.map((c) => c.sessionId));

  // ── Score distribution ────────────────────────────────────────────────────
  const distribution: Record<ScoreBand, number> = {
    very_low: 0, low: 0, moderate: 0, high: 0, very_high: 0,
  };
  const conversionRateByBand: Record<ScoreBand, { converted: number; total: number }> = {
    very_low: { converted: 0, total: 0 },
    low: { converted: 0, total: 0 },
    moderate: { converted: 0, total: 0 },
    high: { converted: 0, total: 0 },
    very_high: { converted: 0, total: 0 },
  };

  for (const s of sessions) {
    const score = s.predictedConversionScore ?? 0;
    const band = scoreBand(score);
    distribution[band]++;
    conversionRateByBand[band].total++;
    if (convertedSessionIds.has(s.sessionId)) {
      conversionRateByBand[band].converted++;
    }
  }

  const conversionRateByBandOut: Partial<Record<ScoreBand, number>> = {};
  for (const [band, { converted, total }] of Object.entries(conversionRateByBand)) {
    if (total > 0) {
      conversionRateByBandOut[band as ScoreBand] = Math.round((converted / total) * 100);
    }
  }

  // ── Score by channel ──────────────────────────────────────────────────────
  const channelMap = new Map<string, { totalScore: number; count: number; bands: ScoreBand[] }>();
  for (const s of sessions) {
    const ch = normalizeChannelGroup(s.channelGroup);
    const score = s.predictedConversionScore ?? 0;
    const band = scoreBand(score);
    const existing = channelMap.get(ch) ?? { totalScore: 0, count: 0, bands: [] };
    existing.totalScore += score;
    existing.count++;
    existing.bands.push(band);
    channelMap.set(ch, existing);
  }

  const scoreByChannel = Array.from(channelMap.entries())
    .map(([channelName, { totalScore, count, bands }]) => {
      const bandCounts = bands.reduce((acc, b) => { acc[b] = (acc[b] ?? 0) + 1; return acc; }, {} as Record<string, number>);
      const topBand = Object.entries(bandCounts).sort((a, b) => b[1] - a[1])[0][0] as ScoreBand;
      return { channelName, avgScore: Math.round(totalScore / count), sessionCount: count, topBand };
    })
    .sort((a, b) => b.avgScore - a.avgScore);

  // ── Score by AI engine ────────────────────────────────────────────────────
  const engineMap = new Map<string, { totalScore: number; count: number }>();
  for (const s of sessions) {
    const engine = aiEngineFromReferrer(s.referrer);
    if (!engine) continue;
    const score = s.predictedConversionScore ?? 0;
    const existing = engineMap.get(engine) ?? { totalScore: 0, count: 0 };
    existing.totalScore += score;
    existing.count++;
    engineMap.set(engine, existing);
  }

  const scoreByAiEngine = Array.from(engineMap.entries())
    .map(([engineName, { totalScore, count }]) => ({
      engineName,
      avgScore: Math.round(totalScore / count),
      sessionCount: count,
    }))
    .sort((a, b) => b.avgScore - a.avgScore);

  // ── Top scoring pages (from HeatmapEvent — entry pages for high-score sessions) ──
  const highScoreSessions = sessions.filter((s) => (s.predictedConversionScore ?? 0) >= 61);
  const heatmapRows = highScoreSessions.length > 0
    ? await prisma.heatmapEvent.findMany({
        where: { sessionId: { in: highScoreSessions.map((s) => s.sessionId) } },
        select: { pageUrl: true, sessionId: true },
        take: 200,
      })
    : [];

  const pageScoreMap = new Map<string, { totalBoost: number; count: number }>();
  for (const h of heatmapRows) {
    const session = highScoreSessions.find((s) => s.sessionId === h.sessionId);
    if (!session) continue;
    const boost = (session.predictedConversionScore ?? 0) - 60; // boost above threshold
    const existing = pageScoreMap.get(h.pageUrl) ?? { totalBoost: 0, count: 0 };
    existing.totalBoost += boost;
    existing.count++;
    pageScoreMap.set(h.pageUrl, existing);
  }

  const topScoringPages = Array.from(pageScoreMap.entries())
    .map(([pageUrl, { totalBoost, count }]) => ({
      pageUrl,
      avgScoreBoost: Math.round(totalBoost / count),
      visitCount: count,
    }))
    .sort((a, b) => b.avgScoreBoost - a.avgScoreBoost)
    .slice(0, 10);

  // ── Live sessions snapshot ────────────────────────────────────────────────
  const highIntentActive = activeSessions.filter((s) => (s.predictedConversionScore ?? 0) >= 61);
  const avgScoreActive = activeSessions.length > 0
    ? Math.round(activeSessions.reduce((sum, s) => sum + (s.predictedConversionScore ?? 0), 0) / activeSessions.length)
    : 0;

  // ── Aggregate metrics ─────────────────────────────────────────────────────
  const totalScored = sessions.length;
  const avgScore = Math.round(
    sessions.reduce((sum, s) => sum + (s.predictedConversionScore ?? 0), 0) / totalScored,
  );

  // ── Insights ──────────────────────────────────────────────────────────────
  const insights: PredictiveConversionScoring["insights"] = [];

  const highPlusCount = distribution.high + distribution.very_high;
  if (highPlusCount > 0) {
    const pct = Math.round((highPlusCount / totalScored) * 100);
    insights.push({
      insight: `${pct}% of sessions in the last ${windowDays} days scored High or Very High intent.`,
      supportingMetric: `${highPlusCount} high-intent sessions out of ${totalScored} total`,
      businessImplication: "These sessions are your warmest leads — prioritize conversion path optimization for their landing pages.",
    });
  }

  const aiScorers = scoreByAiEngine.filter((e) => e.avgScore >= 60);
  if (aiScorers.length > 0) {
    const top = aiScorers[0];
    insights.push({
      insight: `Sessions from ${top.engineName} average a score of ${top.avgScore}/100 — above the high-intent threshold.`,
      supportingMetric: `${top.sessionCount} ${top.engineName} sessions, avg score ${top.avgScore}`,
      businessImplication: "AI-referred visitors arrive with strong purchase intent. Ensure your AI citation content matches their conversion journey.",
    });
  }

  const highConvBand = Object.entries(conversionRateByBandOut)
    .filter(([band]) => ["high", "very_high"].includes(band))
    .sort((a, b) => (b[1] ?? 0) - (a[1] ?? 0))[0];
  if (highConvBand && (highConvBand[1] ?? 0) > 0) {
    insights.push({
      insight: `${highConvBand[1]}% of ${highConvBand[0].replace("_", " ")} scoring sessions converted within ${windowDays} days.`,
      supportingMetric: `Conversion rate for ${highConvBand[0]} band`,
      businessImplication: "Score-based segmentation enables prioritizing chat follow-up and retargeting toward sessions most likely to close.",
    });
  }

  if (topScoringPages.length > 0) {
    try {
      const topPage = topScoringPages[0];
      const path = new URL(topPage.pageUrl).pathname;
      insights.push({
        insight: `${path} drives the highest average intent boost (+${topPage.avgScoreBoost} pts) among scored sessions.`,
        supportingMetric: `${topPage.visitCount} high-intent visits to ${path}`,
        businessImplication: "This page is a conversion catalyst — ensure it has a clear CTA and no friction in the next step.",
      });
    } catch { /* ignore invalid URLs */ }
  }

  return {
    windowDays,
    totalScoredSessions: totalScored,
    avgScore,
    scoreDistribution: distribution,
    conversionRateByBand: conversionRateByBandOut,
    scoreByChannel,
    scoreByAiEngine,
    topScoringPages,
    liveSessionsSnapshot: {
      activeLast30Min: activeSessions.length,
      highIntentCount: highIntentActive.length,
      avgScoreActive,
    },
    insights,
    patentRelevance: PATENT_RELEVANCE,
  };
}
