/**
 * Analysis Data Package Builder — gathers ALL platform data for a brand
 * into a single structured object that Claude can analyze holistically.
 * Uses Promise.allSettled so missing data never blocks the analysis.
 */

import { prisma } from "@/lib/prisma";
import { getIndustryBenchmarks } from "./benchmark-engine";
import { getRecommendationSuccessRates } from "./outcome-tracker";
import { getMemoryContext } from "./client-memory";
import { getSiteTagInstalledAt, siteTagAgeDays } from "./site-tag";
import { getSessionCount } from "./platform-metrics";

export interface AnalysisDataPackage {
  brand: { name: string; domain: string; industry: string | null };
  dateRange: { days: number; start: string; end: string };
  gsc: { clicks: number; impressions: number; avgPosition: number; ctr: number; prevClicks: number; prevImpressions: number };
  ga4: { sessions: number; conversions: number; bounceRate: number; newUsers: number; prevSessions: number } | null;
  topPages: Array<{ url: string; clicks: number; position: number; ctr: number }>;
  topQueries: Array<{ query: string; clicks: number; position: number; impressions: number }>;
  audit: { healthScore: number; errors: number; warnings: number; topIssues: string[] } | null;
  benchmarks: Record<string, { value: number; sampleSize: number }>;
  successRates: Array<{ type: string; total: number; improved: number; avgImpact: number }>;
  memoryContext: string;
  competitorSummary: string | null;
  industryBriefingSummary: string | null;
  industryEvents: Array<{ title: string; severity: string }>;
  backlinks: { total: number; new: number; lost: number } | null;
  conversions: { total: number; topChannel: string | null; forms: number; calls: number } | null;
  heatmap: { avgScroll: number | null; rageClicks: number } | null;
  lifecycle: { newPages: number; pages404: number } | null;
  aiVisibility: { total: number; mentioned: number; mentionRate: number; topGaps: string[] } | null;
  // First-party Site Tag rollup for the current window. Cookieless,
  // server-side endpoint — captures visitors GA4 loses to ad-blockers
  // / ITP / consent rejection. `installDays` is how long the Site Tag
  // has been collecting data (null = no install). `conversionRate` is
  // server-computed as conversions ÷ sessions so the report never
  // divides by zero. topConvertingPages is capped at 5 for prose fit.
  siteTag: {
    installDays: number | null;
    pageViews: number;
    sessions: number;
    formStarts: number;
    formSubmits: number;
    clickToCalls: number;
    smsClicks: number;
    bookingOpens: number;
    bookingSubmits: number;
    bookingConfirmed: number;
    outboundClicks: number;
    conversionsTotal: number;
    conversionRate: number;
    conversionsByType: Record<string, number>;
    topConvertingPages: Array<{ url: string; count: number }>;
  } | null;
}

export async function buildAnalysisDataPackage(brandId: string, days: number): Promise<AnalysisDataPackage> {
  const brand = await prisma.brand.findUnique({ where: { id: brandId }, select: { name: true, domain: true, industry: true } });
  if (!brand) throw new Error("Brand not found");

  const end = new Date();
  const start = new Date(end.getTime() - days * 86400000);
  const prevStart = new Date(start.getTime() - days * 86400000);

  const results = await Promise.allSettled([
    prisma.gscPage.aggregate({ where: { brandId, date: { gte: start } }, _sum: { clicks: true, impressions: true }, _avg: { position: true, ctr: true } }),
    prisma.gscPage.aggregate({ where: { brandId, date: { gte: prevStart, lt: start } }, _sum: { clicks: true, impressions: true } }),
    prisma.metricSnapshot.findFirst({ where: { brandId, date: { gte: start } }, orderBy: { date: "desc" } }),
    prisma.metricSnapshot.findFirst({ where: { brandId, date: { gte: prevStart, lt: start } }, orderBy: { date: "desc" } }),
    prisma.gscPage.findMany({ where: { brandId, date: { gte: start } }, orderBy: { clicks: "desc" }, take: 10, select: { pageUrl: true, clicks: true, position: true, ctr: true } }),
    prisma.gscQuery.findMany({ where: { brandId, date: { gte: start } }, orderBy: { clicks: "desc" }, take: 15, select: { query: true, clicks: true, position: true, impressions: true } }),
    prisma.technicalAudit.findFirst({ where: { brandId }, orderBy: { createdAt: "desc" } }),
    brand.industry ? getIndustryBenchmarks(brand.industry) : Promise.resolve({}),
    brand.industry ? getRecommendationSuccessRates(brand.industry) : Promise.resolve([]),
    getMemoryContext(brandId, 15),
    prisma.competitor.findMany({ where: { brandId, isActive: true }, include: { snapshots: { orderBy: { crawledAt: "desc" }, take: 1, select: { totalPages: true, newPages: true } } }, take: 3 }),
    prisma.industryBriefing.findFirst({ where: { brandId }, orderBy: { generatedAt: "desc" }, select: { summary: true, externalEvents: true } }),
    prisma.backlink.count({ where: { brandId, status: { in: ["active", "new"] } } }),
    prisma.backlink.count({ where: { brandId, status: "new", firstSeenAt: { gte: start } } }),
    prisma.backlink.count({ where: { brandId, status: "lost", lostAt: { gte: start } } }),
    // intentionally unfiltered: AI analysis context; broader data is intentional
    prisma.siteConversion.findMany({ where: { brandId, timestamp: { gte: start } }, select: { conversionType: true, lastTouchChannel: true } }),
    prisma.heatmapEvent.aggregate({ where: { brandId, eventType: "scroll" }, _avg: { maxScrollPercent: true } }),
    prisma.siteEvent.count({ where: { brandId, eventType: "rage_click", timestamp: { gte: start } } }),
    prisma.pageRecord.count({ where: { brandId, firstSeenAt: { gte: start } } }),
    prisma.pageRecord.count({ where: { brandId, currentStatus: 404 } }),
    // AI Visibility summary
    prisma.aiVisibilityCheck.findMany({ where: { brandId }, orderBy: { checkedAt: "desc" }, take: 50, select: { query: true, mentioned: true, position: true } }),
  ]);

  const v = <T,>(i: number, def: T): T => results[i].status === "fulfilled" ? (results[i] as PromiseFulfilledResult<T>).value : def;
  const gscCur = v(0, { _sum: { clicks: 0, impressions: 0 }, _avg: { position: 0, ctr: 0 } } as { _sum: { clicks: number | null; impressions: number | null }; _avg: { position: number | null; ctr: number | null } });
  const gscPrev = v(1, { _sum: { clicks: 0, impressions: 0 } } as { _sum: { clicks: number | null; impressions: number | null } });
  const ga4Cur = v(2, null) as { sessions: number; conversions: number; bounceRate: number; users: number } | null;
  const ga4Prev = v(3, null) as { sessions: number } | null;
  const topPages = v(4, [] as Array<{ pageUrl: string; clicks: number; position: number; ctr: number }>);
  const topQueries = v(5, [] as Array<{ query: string; clicks: number; position: number; impressions: number }>);
  const audit = v(6, null) as { crawlHealth: number; totalIssues?: number } | null;
  const benchmarks = v(7, {} as Record<string, { value: number; sampleSize: number }>);
  const successRates = v(8, [] as Array<{ type: string; total: number; improved: number; avgImpact: number }>);
  const memoryContext = v(9, "" as string);
  const competitors = v(10, [] as Array<{ domain: string; snapshots: Array<{ totalPages: number | null; newPages: unknown }> }>);
  const briefing = v(11, null) as { summary: string | null; externalEvents: unknown } | null;
  const blTotal = v(12, 0 as number);
  const blNew = v(13, 0 as number);
  const blLost = v(14, 0 as number);
  const convRows = v(15, [] as Array<{ conversionType: string; lastTouchChannel: string | null }>);
  const scrollAgg = v(16, { _avg: { maxScrollPercent: null } } as { _avg: { maxScrollPercent: number | null } });
  const rageClicks = v(17, 0 as number);
  const newPages = v(18, 0 as number);
  const pages404 = v(19, 0 as number);
  const aiVisChecks = v(20, [] as Array<{ query: string; mentioned: boolean; position: number | null }>);

  // Derive conversion summary
  const channelCounts = new Map<string, number>();
  let forms = 0, calls = 0;
  for (const c of convRows) {
    channelCounts.set(c.lastTouchChannel || "direct", (channelCounts.get(c.lastTouchChannel || "direct") || 0) + 1);
    if (c.conversionType.startsWith("phone")) calls++; else forms++;
  }
  const topChannel = [...channelCounts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;

  const compSummary = competitors.length > 0 ? competitors.map((c) => `${c.domain}: ${c.snapshots[0]?.totalPages ?? "?"} pages`).join("; ") : null;
  const events = Array.isArray(briefing?.externalEvents) ? (briefing.externalEvents as Array<{ title?: string; severity?: string }>).filter((e) => e.title).map((e) => ({ title: e.title!, severity: e.severity || "moderate" })) : [];

  // First-party Site Tag rollup. Scoped to the brand's TrackedSite so
  // only events ingested through /api/collect count. Each query
  // individually .catch'd so a single aggregate failure zeros that
  // slice instead of blanking the whole Site Tag block.
  let siteTag: AnalysisDataPackage["siteTag"] = null;
  try {
    const trackedSite = await prisma.trackedSite.findUnique({
      where: { brandId },
      select: { id: true },
    });
    if (trackedSite) {
      // session_start is intentionally omitted from the TrackedEvent
      // groupBy — the session count comes from TrackedSession.count
      // below (the authoritative unique-visitor count; every other
      // surface in the platform uses it).
      const ST_EVENT_TYPES = [
        "page_view",
        "form_start", "form_submit",
        "click_to_call", "sms_click",
        "booking_widget_opened", "booking_submit", "booking_confirmed",
        "outbound_click",
      ];
      const [stEventRows, stSessions, stConvByType, stConvPageRows, stInstalledAt] = await Promise.all([
        prisma.trackedEvent.groupBy({
          by: ["eventType"],
          where: {
            trackedSiteId: trackedSite.id,
            eventType: { in: ST_EVENT_TYPES },
            timestamp: { gte: start },
          },
          _count: true,
        }).catch(() => [] as Array<{ eventType: string; _count: number }>),
        getSessionCount(brandId, start, { trackedSiteId: trackedSite.id }),
        // intentionally unfiltered: AI analysis context; broader data is intentional
        prisma.siteConversion.groupBy({
          by: ["conversionType"],
          where: { brandId, timestamp: { gte: start } },
          _count: true,
        }).catch(() => [] as Array<{ conversionType: string; _count: number }>),
        // intentionally unfiltered: AI analysis context; broader data is intentional
        prisma.siteConversion.groupBy({
          by: ["pageUrl"],
          where: { brandId, timestamp: { gte: start } },
          _count: true,
        }).catch(() => [] as Array<{ pageUrl: string; _count: number }>),
        getSiteTagInstalledAt(brandId).catch(() => null),
      ]);

      const stCounts: Record<string, number> = {};
      for (const r of stEventRows) {
        stCounts[r.eventType] = typeof r._count === "number" ? r._count : 0;
      }
      const stConvByTypeMap: Record<string, number> = {};
      let stConvTotal = 0;
      for (const r of stConvByType) {
        const n = typeof r._count === "number" ? r._count : 0;
        stConvByTypeMap[r.conversionType] = n;
        stConvTotal += n;
      }
      const stConvRate = stSessions > 0 ? Math.round((stConvTotal / stSessions) * 1000) / 10 : 0;
      const stTopPages = stConvPageRows
        .map((r) => ({ url: r.pageUrl, count: typeof r._count === "number" ? r._count : 0 }))
        .filter((r) => r.count > 0)
        .sort((a, b) => b.count - a.count)
        .slice(0, 5);

      siteTag = {
        installDays: stInstalledAt ? siteTagAgeDays(stInstalledAt) : null,
        pageViews: stCounts["page_view"] ?? 0,
        sessions: stSessions,
        formStarts: stCounts["form_start"] ?? 0,
        formSubmits: stCounts["form_submit"] ?? 0,
        clickToCalls: stCounts["click_to_call"] ?? 0,
        smsClicks: stCounts["sms_click"] ?? 0,
        bookingOpens: stCounts["booking_widget_opened"] ?? 0,
        bookingSubmits: stCounts["booking_submit"] ?? 0,
        bookingConfirmed: stCounts["booking_confirmed"] ?? 0,
        outboundClicks: stCounts["outbound_click"] ?? 0,
        conversionsTotal: stConvTotal,
        conversionRate: stConvRate,
        conversionsByType: stConvByTypeMap,
        topConvertingPages: stTopPages,
      };
    }
  } catch { /* best effort — leave siteTag null on failure */ }

  return {
    brand, dateRange: { days, start: start.toISOString().split("T")[0], end: end.toISOString().split("T")[0] },
    gsc: { clicks: gscCur._sum.clicks ?? 0, impressions: gscCur._sum.impressions ?? 0, avgPosition: Math.round((gscCur._avg.position ?? 0) * 10) / 10, ctr: Math.round((gscCur._avg.ctr ?? 0) * 10000) / 100, prevClicks: gscPrev._sum.clicks ?? 0, prevImpressions: gscPrev._sum.impressions ?? 0 },
    ga4: ga4Cur ? { sessions: ga4Cur.sessions, conversions: ga4Cur.conversions, bounceRate: ga4Cur.bounceRate, newUsers: ga4Cur.users, prevSessions: ga4Prev?.sessions ?? 0 } : null,
    topPages: topPages.map((p) => ({ url: p.pageUrl, clicks: p.clicks, position: Math.round(p.position * 10) / 10, ctr: Math.round(p.ctr * 10000) / 100 })),
    topQueries: topQueries.map((q) => ({ query: q.query, clicks: q.clicks, position: Math.round(q.position * 10) / 10, impressions: q.impressions })),
    audit: audit ? { healthScore: audit.crawlHealth, errors: audit.totalIssues ?? 0, warnings: 0, topIssues: [] } : null,
    benchmarks, successRates, memoryContext,
    competitorSummary: compSummary, industryBriefingSummary: briefing?.summary ?? null, industryEvents: events,
    backlinks: { total: blTotal, new: blNew, lost: blLost },
    conversions: convRows.length > 0 ? { total: convRows.length, topChannel, forms, calls } : null,
    heatmap: { avgScroll: scrollAgg._avg.maxScrollPercent, rageClicks },
    lifecycle: { newPages, pages404 },
    aiVisibility: aiVisChecks.length > 0 ? {
      total: aiVisChecks.length,
      mentioned: aiVisChecks.filter((c) => c.mentioned).length,
      mentionRate: Math.round((aiVisChecks.filter((c) => c.mentioned).length / aiVisChecks.length) * 100),
      topGaps: aiVisChecks.filter((c) => !c.mentioned).slice(0, 3).map((c) => c.query),
    } : null,
    siteTag,
  };
}
