import { prisma } from "@/lib/prisma";
import type { ConversionHeatMap } from "../types";

// ─── Region mapping ───────────────────────────────────────────────────────────

type RegionY = "header" | "hero" | "upper-content" | "mid-content" | "lower-content" | "footer";
type RegionX = "left" | "center" | "right";

function yZone(pct: number): RegionY {
  if (pct <= 12) return "header";
  if (pct <= 28) return "hero";
  if (pct <= 46) return "upper-content";
  if (pct <= 64) return "mid-content";
  if (pct <= 82) return "lower-content";
  return "footer";
}

function xZone(pct: number): RegionX {
  if (pct <= 33) return "left";
  if (pct <= 67) return "center";
  return "right";
}

const Y_LABELS: Record<RegionY, string> = {
  header: "Header / Nav",
  hero: "Hero Area",
  "upper-content": "Upper Content",
  "mid-content": "Mid Content",
  "lower-content": "Lower Content",
  footer: "Footer",
};

function regionLabel(y: RegionY, x: RegionX): string {
  return `${Y_LABELS[y]} (${x})`;
}

function regionKey(y: RegionY, x: RegionX): string {
  return `${y}|${x}`;
}

function parseRegionKey(key: string): { y: RegionY; x: RegionX; label: string } {
  const [y, x] = key.split("|") as [RegionY, RegionX];
  return { y, x, label: regionLabel(y, x) };
}

// ─── Page type inference ──────────────────────────────────────────────────────

function inferPageType(url: string): ConversionHeatMap["perPage"][number]["pageType"] {
  let path = url;
  try { path = new URL(url).pathname.toLowerCase(); } catch { path = url.toLowerCase(); }
  if (path === "/" || path === "") return "homepage";
  if (/contact|reach-us|get-in-touch|schedule|book|consult|quote|demo/.test(path)) return "contact";
  if (/product|item|shop|buy|purchase|order/.test(path)) return "product";
  if (/service|solution|offering|package|pricing|plan/.test(path)) return "service";
  return "other";
}

// ─── CTA element detection ────────────────────────────────────────────────────

function isCta(label: string, tag: string): boolean {
  const l = label.toLowerCase();
  return (
    tag === "button" ||
    /schedule|contact|call|book|get.*(started|quote|demo)|sign.?up|request|consult|free|try/i.test(l) ||
    /cta|btn|button/i.test(l)
  );
}

// ─── Click point type ─────────────────────────────────────────────────────────

interface ClickPoint {
  positionX: number | null;
  positionY: number | null;
  label: string | null;
  tag: string | null;
}

// ─── URL path for display ─────────────────────────────────────────────────────

function urlPath(url: string): string {
  try { return new URL(url).pathname || "/"; } catch { return url; }
}

// ─── Confidence ───────────────────────────────────────────────────────────────

function dataConfidence(totalClicks: number): ConversionHeatMap["dataConfidence"] {
  if (totalClicks >= 10_000) return "high";
  if (totalClicks >= 1_000) return "moderate";
  if (totalClicks >= 100) return "low";
  return "insufficient";
}

// ─── Main export ──────────────────────────────────────────────────────────────

const PATENT_RELEVANCE =
  "Cookieless click event capture by the meSEO Site Tag enables cross-referencing visitor engagement patterns with conversion outcomes — a correlation that traditional analytics tools cannot compute without persistent identity.";

export async function aggregateConversionHeatMap(
  brandId: string,
  windowDays = 30,
): Promise<ConversionHeatMap | null> {
  const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1000);

  // Fetch click heatmap events and conversion sessions in parallel
  const [clickRows, scrollRows, conversions] = await Promise.all([
    prisma.heatmapEvent.findMany({
      where: { brandId, eventType: "clicks", timestamp: { gte: since } },
      select: { pageUrl: true, sessionId: true, events: true },
      take: 5_000,
    }),
    prisma.heatmapEvent.findMany({
      where: { brandId, eventType: "scroll", timestamp: { gte: since } },
      select: { pageUrl: 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 (clickRows.length === 0) {
    return {
      hasFirstPartyData: true,
      hasClickData: false,
      windowDays,
      dataConfidence: "insufficient",
      perPage: [],
      aggregateInsights: [],
      patentRelevance: PATENT_RELEVANCE,
    };
  }

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

  // Count pageviews per page from scroll events (proxy for page views)
  const pageviewCounts = new Map<string, number>();
  for (const row of scrollRows) {
    const key = row.pageUrl;
    pageviewCounts.set(key, (pageviewCounts.get(key) ?? 0) + 1);
  }

  // ── Parse click events per page ───────────────────────────────────────────

  type PageAccum = {
    sessions: Set<string>;
    regionClicks: Map<string, { count: number; convertedSessions: Set<string> }>;
    elementClicks: Map<string, { label: string; tag: string; count: number; convertedSessions: Set<string> }>;
    totalClicks: number;
  };

  const pageMap = new Map<string, PageAccum>();

  let grandTotalClicks = 0;

  for (const row of clickRows) {
    const points = Array.isArray(row.events) ? (row.events as unknown as ClickPoint[]) : [];
    if (points.length === 0) continue;

    const accum = pageMap.get(row.pageUrl) ?? {
      sessions: new Set<string>(),
      regionClicks: new Map(),
      elementClicks: new Map(),
      totalClicks: 0,
    };

    if (row.sessionId) accum.sessions.add(row.sessionId);

    for (const pt of points) {
      const px = typeof pt.positionX === "number" ? pt.positionX : null;
      const py = typeof pt.positionY === "number" ? pt.positionY : null;
      if (px == null || py == null) continue;

      const y = yZone(py);
      const x = xZone(px);
      const rKey = regionKey(y, x);

      const regionEntry = accum.regionClicks.get(rKey) ?? { count: 0, convertedSessions: new Set<string>() };
      regionEntry.count++;
      if (row.sessionId && convertedSessionIds.has(row.sessionId)) {
        regionEntry.convertedSessions.add(row.sessionId);
      }
      accum.regionClicks.set(rKey, regionEntry);

      // Element-level aggregation by label (normalized)
      const label = pt.label ?? (pt.tag ? `<${pt.tag}>` : "unknown");
      const tag = pt.tag ?? "div";
      const eKey = label.toLowerCase().slice(0, 60);
      const elemEntry = accum.elementClicks.get(eKey) ?? { label, tag, count: 0, convertedSessions: new Set<string>() };
      elemEntry.count++;
      if (row.sessionId && convertedSessionIds.has(row.sessionId)) {
        elemEntry.convertedSessions.add(row.sessionId);
      }
      accum.elementClicks.set(eKey, elemEntry);

      accum.totalClicks++;
      grandTotalClicks++;
    }

    pageMap.set(row.pageUrl, accum);
  }

  // ── Build per-page results (top 10 by click volume) ──────────────────────

  const sortedPages = Array.from(pageMap.entries())
    .sort((a, b) => b[1].totalClicks - a[1].totalClicks)
    .slice(0, 10);

  const perPage: ConversionHeatMap["perPage"] = sortedPages.map(([pageUrl, accum]) => {
    const totalClicks = accum.totalClicks;
    const totalSessions = accum.sessions.size;
    const clicksPerSession = totalSessions > 0 ? Math.round((totalClicks / totalSessions) * 10) / 10 : 0;
    const pageviews = pageviewCounts.get(pageUrl) ?? totalSessions;

    // Region density
    const clickDensityRegions = Array.from(accum.regionClicks.entries())
      .map(([key, { count, convertedSessions }]) => {
        const { label } = parseRegionKey(key);
        return {
          regionLabel: label,
          clickCount: count,
          sharePercent: Math.round((count / totalClicks) * 100),
          hasConversionPath: convertedSessions.size > 0,
        };
      })
      .sort((a, b) => b.clickCount - a.clickCount);

    // Top clicked elements
    const topClickedElements = Array.from(accum.elementClicks.entries())
      .sort((a, b) => b[1].count - a[1].count)
      .slice(0, 10)
      .map(([, { label, tag, count, convertedSessions }]) => {
        const convContrib = convertedSessions.size;
        return {
          selector: tag,
          elementLabel: label,
          clickCount: count,
          conversionContribution: convContrib,
          conversionContributionPercent: totalSessions > 0 ? Math.round((convContrib / totalSessions) * 100) : 0,
        };
      });

    // Dead zones: high pageview regions with low click share
    const avgClicksPerRegion = totalClicks / Math.max(accum.regionClicks.size, 1);
    const deadZones = clickDensityRegions
      .filter((r) => r.clickCount < avgClicksPerRegion * 0.3 && r.regionLabel.includes("Content"))
      .slice(0, 3)
      .map((r) => ({
        regionLabel: r.regionLabel,
        pageviewCount: pageviews,
        clickCount: r.clickCount,
        missedClickRatio: r.clickCount > 0 ? Math.round((pageviews / r.clickCount) * 10) / 10 : pageviews,
        recommendation: `Add a CTA or interactive element in the ${r.regionLabel} to capture the scroll engagement without clicks.`,
      }));

    // Hot zones: high-click regions excluding header/nav
    const hotZones = clickDensityRegions
      .filter((r) => !r.regionLabel.startsWith("Header") && r.clickCount > avgClicksPerRegion * 1.5)
      .slice(0, 3)
      .map((r) => {
        const topElem = topClickedElements[0];
        const hasCta = topElem ? isCta(topElem.elementLabel, topElem.selector) : false;
        return {
          regionLabel: r.regionLabel,
          clickCount: r.clickCount,
          isCurrentlyCta: hasCta,
          recommendation: hasCta
            ? `Existing CTA in ${r.regionLabel} is performing well — consider A/B testing copy variants.`
            : `High engagement in ${r.regionLabel} without a CTA — add a conversion element here.`,
        };
      });

    // CTA placements
    const ctaElements = topClickedElements.filter((e) => isCta(e.elementLabel, e.selector));
    const currentCtaPlacements = ctaElements.map((e) => {
      const regionForElem = clickDensityRegions[0]?.regionLabel ?? "Unknown";
      const effectivenessScore = Math.min(
        100,
        Math.round(
          (e.clickCount / Math.max(totalClicks, 1)) * 50 +
            (e.conversionContribution / Math.max(totalSessions, 1)) * 50,
        ),
      );
      return {
        selector: e.selector,
        elementLabel: e.elementLabel,
        regionLabel: regionForElem,
        clickCount: e.clickCount,
        conversionContribution: e.conversionContribution,
        effectivenessScore,
      };
    });

    // Recommended CTA placements: hot zones without CTAs
    const recommendedCtaPlacements = hotZones
      .filter((z) => !z.isCurrentlyCta)
      .map((z) => ({
        regionLabel: z.regionLabel,
        rationale: `${z.clickCount} clicks observed in this region with no current CTA capturing that intent.`,
        expectedLift: "estimated 15–30% CTR uplift based on observed click density in this area",
      }));

    return {
      pageUrl,
      pageType: inferPageType(pageUrl),
      totalClicks,
      totalSessions,
      clicksPerSession,
      clickDensityRegions,
      topClickedElements,
      deadZones,
      hotZones,
      currentCtaPlacements,
      recommendedCtaPlacements,
    };
  });

  // ── Aggregate insights ────────────────────────────────────────────────────

  const aggregateInsights: ConversionHeatMap["aggregateInsights"] = [];

  // Insight 1: pages with high dead-zone ratio
  const deadZonePages = perPage.filter((p) => p.deadZones.length > 0);
  if (deadZonePages.length > 0) {
    const affectedPages = deadZonePages.map((p) => urlPath(p.pageUrl));
    aggregateInsights.push({
      insight: `${deadZonePages.length} page${deadZonePages.length > 1 ? "s" : ""} have content regions with view-to-click ratios above 10:1 — visitors scroll through without clicking.`,
      affectedPages,
      actionRecommendation: "Add secondary CTAs or interactive elements (expandable FAQs, comparison tables, testimonial carousels) in these scroll-heavy dead zones.",
    });
  }

  // Insight 2: hero area engagement
  const heroEngagement = perPage.filter((p) =>
    p.clickDensityRegions.some((r) => r.regionLabel.startsWith("Hero") && r.sharePercent > 30),
  );
  if (heroEngagement.length > 0) {
    aggregateInsights.push({
      insight: `Hero area captures 30%+ of all clicks on ${heroEngagement.length} page${heroEngagement.length > 1 ? "s" : ""}, confirming above-the-fold primacy.`,
      affectedPages: heroEngagement.map((p) => urlPath(p.pageUrl)),
      actionRecommendation: "Protect hero CTA placement and test button copy variants — this area has the highest leverage for conversion lift.",
    });
  }

  // Insight 3: footer engagement
  const footerHot = perPage.filter((p) =>
    p.clickDensityRegions.some((r) => r.regionLabel.startsWith("Footer") && r.sharePercent > 15),
  );
  if (footerHot.length > 0) {
    aggregateInsights.push({
      insight: `Footer area drives 15%+ of clicks on ${footerHot.length} page${footerHot.length > 1 ? "s" : ""}  — visitors who reach the bottom are highly engaged.`,
      affectedPages: footerHot.map((p) => urlPath(p.pageUrl)),
      actionRecommendation: "Add a prominent conversion CTA in the footer section to capture bottom-of-page intent before exit.",
    });
  }

  // Insight 4: high-conversion-path regions
  const conversionRegionPages = perPage.filter((p) =>
    p.clickDensityRegions.some((r) => r.hasConversionPath),
  );
  if (conversionRegionPages.length > 0) {
    const topPage = conversionRegionPages.sort((a, b) => b.totalClicks - a.totalClicks)[0];
    const topRegion = topPage.clickDensityRegions.find((r) => r.hasConversionPath);
    if (topRegion) {
      aggregateInsights.push({
        insight: `Clicks in the ${topRegion.regionLabel} of ${urlPath(topPage.pageUrl)} directly preceded conversions in ${Math.round((topRegion.sharePercent * topPage.totalClicks) / 100)} sessions.`,
        affectedPages: [urlPath(topPage.pageUrl)],
        actionRecommendation: "Prioritize this region for CTA prominence, fast load time, and mobile tap-target sizing — it is your highest-value conversion zone.",
      });
    }
  }

  return {
    hasFirstPartyData: true,
    hasClickData: true,
    windowDays,
    dataConfidence: dataConfidence(grandTotalClicks),
    perPage,
    aggregateInsights,
    patentRelevance: PATENT_RELEVANCE,
  };
}
