/**
 * Paid Media Context for AI Strategist
 * ─────────────────────────────────────
 * Builds a compact text block for injection into the Strategist's
 * data context. Surfaces paid-vs-organic insights so the AI can
 * recommend budget reallocation:
 *   "You're paying $12/click for 'cardiologist Houston' but you
 *    rank #3 organically — pause the ad and save $4,200/month"
 */

import { prisma } from "@/lib/prisma";

export async function buildPaidMediaContext(
  brandId: string,
  days = 28,
): Promise<string | null> {
  try {
    const since = new Date(Date.now() - days * 86_400_000);

    const site = await prisma.trackedSite.findUnique({
      where: { brandId },
      select: { id: true },
    });

    const [campaignAgg, clickEvents, paths, channelSessions] = await Promise.all([
      prisma.adCampaignData.aggregate({
        where: { brandId, date: { gte: since } },
        _sum: { spend: true, clicks: true, impressions: true, platformConversions: true },
        _count: true,
      }).catch(() => ({ _sum: { spend: null, clicks: null, impressions: null, platformConversions: null }, _count: 0 })),
      prisma.adClickEvent.count({
        where: { brandId, timestamp: { gte: since } },
      }).catch(() => 0),
      prisma.attributionPath.findMany({
        where: { brandId, createdAt: { gte: since } },
        select: {
          firstTouchChannel: true,
          convertingChannel: true,
          conversionValue: true,
          touchpointCount: true,
          daysBetweenFirstAndConversion: true,
        },
        take: 5000,
      }).catch(() => []),
      site
        ? prisma.trackedSession.groupBy({
            by: ["channelGroup"],
            where: { trackedSiteId: site.id, startedAt: { gte: since }, channelGroup: { not: null } },
            _count: true,
          }).catch(() => [] as Array<{ channelGroup: string | null; _count: number }>)
        : Promise.resolve([] as Array<{ channelGroup: string | null; _count: number }>),
    ]);

    const hasSpendData = (campaignAgg._sum.spend ?? 0) > 0;
    const hasClickIds = clickEvents > 0;
    const hasPaths = paths.length > 0;
    if (!hasSpendData && !hasClickIds && !hasPaths && channelSessions.length === 0) return null;

    const lines: string[] = [];
    lines.push(`Paid Media Intelligence — ${days}-day window`);

    if (hasSpendData) {
      const spend = campaignAgg._sum.spend ?? 0;
      const clicks = campaignAgg._sum.clicks ?? 0;
      const imps = campaignAgg._sum.impressions ?? 0;
      const platConv = campaignAgg._sum.platformConversions ?? 0;
      const cpc = clicks > 0 ? (spend / clicks).toFixed(2) : "—";
      lines.push("");
      lines.push("Ad Spend Summary:");
      lines.push(`  Spend: $${Math.round(spend).toLocaleString()} · Clicks: ${clicks.toLocaleString()} · Impressions: ${imps.toLocaleString()}`);
      lines.push(`  CPC: $${cpc} · Platform-claimed conversions: ${platConv}`);
    }

    if (channelSessions.length > 0) {
      lines.push("");
      lines.push("Sessions by Channel Group:");
      const sorted = channelSessions
        .filter((r) => r.channelGroup)
        .map((r) => ({ channel: r.channelGroup!, sessions: typeof r._count === "number" ? r._count : 0 }))
        .sort((a, b) => b.sessions - a.sessions);
      for (const r of sorted.slice(0, 8)) {
        lines.push(`  ${r.channel}: ${r.sessions.toLocaleString()} sessions`);
      }
      const paidSessions = sorted.filter((r) => r.channel.startsWith("Paid")).reduce((s, r) => s + r.sessions, 0);
      const organicSessions = sorted.filter((r) => r.channel.includes("Organic")).reduce((s, r) => s + r.sessions, 0);
      if (paidSessions > 0 || organicSessions > 0) {
        lines.push(`  → Paid: ${paidSessions.toLocaleString()} vs Organic: ${organicSessions.toLocaleString()}`);
      }
    }

    if (hasPaths) {
      const paidPaths = paths.filter((p) => p.firstTouchChannel?.startsWith("Paid"));
      const organicPaths = paths.filter((p) => p.firstTouchChannel?.includes("Organic"));
      const avgTouchpoints = paths.length > 0
        ? (paths.reduce((s, p) => s + p.touchpointCount, 0) / paths.length).toFixed(1)
        : "0";
      const avgDays = paths.length > 0
        ? (paths.reduce((s, p) => s + p.daysBetweenFirstAndConversion, 0) / paths.length).toFixed(1)
        : "0";
      lines.push("");
      lines.push("Attribution Paths:");
      lines.push(`  Total: ${paths.length} · Avg touchpoints: ${avgTouchpoints} · Avg days to convert: ${avgDays}`);
      lines.push(`  Paid first-touch: ${paidPaths.length} · Organic first-touch: ${organicPaths.length}`);

      const channelConv: Record<string, number> = {};
      for (const p of paths) {
        const ch = p.convertingChannel ?? "Direct";
        channelConv[ch] = (channelConv[ch] ?? 0) + 1;
      }
      const topChannels = Object.entries(channelConv).sort(([, a], [, b]) => b - a).slice(0, 5);
      if (topChannels.length > 0) {
        lines.push("  Converting channels: " + topChannels.map(([ch, n]) => `${ch}=${n}`).join(", "));
      }
    }

    if (hasClickIds && !hasSpendData) {
      lines.push("");
      lines.push(`Ad click events detected: ${clickEvents} (gclid/fbclid/msclkid/etc in landing URLs). No ad platform connected — spend data unavailable. Recommend connecting Google Ads / Meta for full ROAS analysis.`);
    }

    lines.push("");
    lines.push("Analysis guidance:");
    lines.push("- When organic ranking is high (position 1-5) for a keyword the brand is ALSO paying for via ads, recommend pausing the ad to save spend — organic captures the same traffic for free.");
    lines.push("- When paid first-touch drives conversions but organic first-touch has higher volume, recommend increasing ad budget on keywords where organic doesn't rank yet (position > 10).");
    lines.push("- When avg days-to-convert is high (>7), recommend retargeting campaigns to accelerate the decision.");
    lines.push("- When platform-claimed conversions exceed Site-Tag-verified conversions, flag the over-attribution and recommend the brand investigate the discrepancy.");

    return lines.join("\n");
  } catch {
    return null;
  }
}
