/**
 * AI Referral Backfill
 * ────────────────────
 * Scans existing TrackedSession records for referrer URLs matching
 * AI platform domains and creates AiAttribution records for them.
 * Idempotent — skips sessions that already have an AiAttribution.
 */

import { prisma } from "@/lib/prisma";
import { inferQueryFromLandingPage } from "@/lib/services/ai-attribution";

const AI_DOMAIN_MAP: Array<{
  domains: string[];
  platform: string;
  channel: "llmo" | "geo" | "aeo";
}> = [
  { domains: ["chat.openai.com", "chatgpt.com"],                    platform: "chatgpt",            channel: "llmo" },
  { domains: ["perplexity.ai"],                                      platform: "perplexity",         channel: "llmo" },
  { domains: ["claude.ai"],                                          platform: "claude",             channel: "llmo" },
  { domains: ["gemini.google.com", "bard.google.com"],               platform: "gemini",             channel: "llmo" },
  { domains: ["copilot.microsoft.com"],                              platform: "copilot",            channel: "llmo" },
  { domains: ["you.com"],                                            platform: "you_com",            channel: "llmo" },
  { domains: ["phind.com"],                                          platform: "phind",              channel: "llmo" },
  { domains: ["meta.ai"],                                            platform: "meta_ai",            channel: "llmo" },
  { domains: ["poe.com"],                                            platform: "poe",                channel: "llmo" },
  { domains: ["bing.com/chat", "www.bing.com/chat"],                 platform: "copilot",            channel: "llmo" },
];

function matchAiReferrer(referrer: string): { platform: string; channel: "llmo" | "geo" | "aeo" } | null {
  try {
    const url = new URL(referrer);
    const host = url.hostname.replace(/^www\./, "");
    const hostPath = `${host}${url.pathname}`;

    for (const entry of AI_DOMAIN_MAP) {
      for (const domain of entry.domains) {
        if (host === domain || host === `www.${domain}` || hostPath.startsWith(domain)) {
          return { platform: entry.platform, channel: entry.channel };
        }
      }
    }
  } catch {
    const lower = referrer.toLowerCase();
    for (const entry of AI_DOMAIN_MAP) {
      for (const domain of entry.domains) {
        if (lower.includes(domain)) {
          return { platform: entry.platform, channel: entry.channel };
        }
      }
    }
  }
  return null;
}

export interface BackfillResult {
  brandsProcessed: number;
  totalSessionsScanned: number;
  aiReferralsFound: number;
  googleAiOverviewFound: number;
  otherAiPlatformFound: number;
  attributionsCreated: number;
  byBrand: Record<string, {
    scanned: number; found: number; created: number;
    googleAiOverview: number; otherPlatforms: number;
  }>;
}

// Extract text fragment from the event metadata or page URL. Checks
// common locations: metadata.landingTextFragment (session_start),
// metadata.textFragment (page_view), and the raw pageUrl hash.
function extractTextFragment(event: {
  metadata: unknown;
  pageUrl: string | null;
}): string | null {
  const meta = (event.metadata ?? {}) as Record<string, unknown>;
  if (typeof meta.landingTextFragment === "string" && meta.landingTextFragment) return meta.landingTextFragment;
  if (typeof meta.textFragment === "string" && meta.textFragment) return meta.textFragment;
  if (event.pageUrl) {
    const idx = event.pageUrl.indexOf("#:~:text=");
    if (idx > -1) return event.pageUrl.slice(idx + "#:~:text=".length).split("&")[0];
  }
  return null;
}

function isGoogleReferrer(ref: string): boolean {
  return /(^|\.)google\.[a-z.]+/.test(ref.toLowerCase());
}

export async function runAiReferralBackfill(brandIdFilter?: string): Promise<BackfillResult> {
  const where: Record<string, unknown> = {
    status: { not: "deleted" },
    trackedSite: { isNot: null },
  };
  if (brandIdFilter) where.id = brandIdFilter;

  const brands = await prisma.brand.findMany({
    where,
    select: {
      id: true,
      name: true,
      trackedSite: { select: { id: true } },
    },
  });

  const result: BackfillResult = {
    brandsProcessed: 0,
    totalSessionsScanned: 0,
    aiReferralsFound: 0,
    googleAiOverviewFound: 0,
    otherAiPlatformFound: 0,
    attributionsCreated: 0,
    byBrand: {},
  };

  for (const brand of brands) {
    if (!brand.trackedSite) continue;
    result.brandsProcessed++;

    const brandStats = { scanned: 0, found: 0, created: 0, googleAiOverview: 0, otherPlatforms: 0 };
    let cursor: string | undefined;

    while (true) {
      const sessions = await prisma.trackedSession.findMany({
        where: {
          trackedSiteId: brand.trackedSite.id,
          referrer: { not: null },
        },
        select: {
          id: true,
          sessionId: true,
          referrer: true,
          entryPage: true,
          startedAt: true,
          pageCount: true,
        },
        orderBy: { id: "asc" },
        take: 500,
        ...(cursor ? { skip: 1, cursor: { id: cursor } } : {}),
      });

      if (sessions.length === 0) break;
      cursor = sessions[sessions.length - 1].id;
      brandStats.scanned += sessions.length;

      for (const session of sessions) {
        if (!session.referrer) continue;

        let match = matchAiReferrer(session.referrer);
        let queryContext: string | null = null;

        // Google AI Overview fallback: referrer is google.com (not an
        // explicit AI platform), but one of the session's events carries
        // a text fragment from the landing URL. That's the strongest
        // signal available for AI Overview clicks.
        if (!match && isGoogleReferrer(session.referrer)) {
          const fragEvent = await prisma.trackedEvent.findFirst({
            where: { sessionId: session.id },
            select: { metadata: true, pageUrl: true },
            orderBy: { timestamp: "asc" },
          }).catch(() => null);
          const fragment = fragEvent ? extractTextFragment(fragEvent) : null;
          if (fragment) {
            match = { platform: "google_ai_overview", channel: "geo" };
            try {
              queryContext = decodeURIComponent(fragment.replace(/\+/g, " ")).slice(0, 500);
            } catch {
              queryContext = fragment.slice(0, 500);
            }
          }
        }

        if (!match) continue;

        brandStats.found++;
        if (match.platform === "google_ai_overview") brandStats.googleAiOverview++;
        else brandStats.otherPlatforms++;

        const existing = await prisma.aiAttribution.findFirst({
          where: { brandId: brand.id, sessionId: session.sessionId },
          select: { id: true },
        });
        if (existing) continue;

        const conversion = await prisma.siteConversion.findFirst({
          where: {
            brandId: brand.id,
            sessionId: session.sessionId,
          },
          select: {
            conversionType: true,
            pageUrl: true,
            id: true,
            timestamp: true,
          },
        }).catch(() => null);

        await prisma.aiAttribution.create({
          data: {
            brandId: brand.id,
            sessionId: session.sessionId,
            channel: match.channel,
            aiPlatform: match.platform,
            referrerUrl: session.referrer.slice(0, 2048),
            queryContext: queryContext?.trim() ? queryContext : inferQueryFromLandingPage(session.entryPage),
            landingPage: session.entryPage.slice(0, 2048),
            pagesViewed: session.pageCount,
            timestamp: session.startedAt,
            converted: !!conversion,
            conversionType: conversion?.conversionType ?? null,
            conversionPage: conversion?.pageUrl ?? null,
            conversionId: conversion?.id ?? null,
            conversionTimestamp: conversion?.timestamp ?? null,
          },
        }).catch(() => null);

        brandStats.created++;
      }
    }

    result.totalSessionsScanned += brandStats.scanned;
    result.aiReferralsFound += brandStats.found;
    result.googleAiOverviewFound += brandStats.googleAiOverview;
    result.otherAiPlatformFound += brandStats.otherPlatforms;
    result.attributionsCreated += brandStats.created;
    result.byBrand[brand.name] = brandStats;
  }

  return result;
}

/**
 * Patch existing AiAttribution records that have null queryContext
 * by inferring a query from the landing page URL.
 */
export async function backfillMissingQueryContext(): Promise<number> {
  const records = await prisma.aiAttribution.findMany({
    where: { queryContext: null },
    select: { id: true, landingPage: true },
    take: 5000,
  });

  let patched = 0;
  for (const rec of records) {
    const inferred = inferQueryFromLandingPage(rec.landingPage);
    await prisma.aiAttribution.update({
      where: { id: rec.id },
      data: { queryContext: inferred },
    }).catch(() => null);
    patched++;
  }

  return patched;
}
