/**
 * Phase 1 — Metric Ground-Truth Audit
 * ───────────────────────────────────────
 *
 * Run against a live DB to capture the *actual* counts per metric
 * table for a single brand. Output is written to stdout AND to
 * scripts/audit-results.md so the ground truth lives alongside the
 * code-side audit.
 *
 * Usage:
 *   DATABASE_URL=postgres://... npx tsx scripts/audit-phase1.ts
 *   # or pick a different brand
 *   BRAND_NAME="Modern Heart" npx tsx scripts/audit-phase1.ts
 *
 * This script is READ-ONLY — it only runs aggregate() / groupBy()
 * / count() queries. Safe to run in production.
 *
 * Scoping note: some metric tables are keyed by brandId directly
 * (SiteConversion, SiteEvent, RealUserMetric, AiInteraction,
 * AiUsageLog, PlatformEvent, MetricSnapshot, GscPage, TechnicalAudit)
 * while TrackedEvent and TrackedSession are scoped via TrackedSite.
 * The script resolves the brand's TrackedSite once and uses
 * trackedSiteId for those two tables.
 */

import { PrismaClient } from "@prisma/client";
import { writeFileSync, mkdirSync } from "fs";
import { dirname } from "path";

const prisma = new PrismaClient();

// Accumulator for the markdown report. Every log() call appends to
// both stdout and this buffer; the buffer is flushed to
// scripts/audit-results.md at the end of the run.
const report: string[] = [];
function log(line: string) {
  log(line);
  report.push(line);
}

// JSON.stringify replacer that handles BigInt + Date cleanly.
function replacer(_key: string, value: unknown): unknown {
  if (typeof value === "bigint") return value.toString();
  if (value instanceof Date) return value.toISOString();
  return value;
}

function j(v: unknown): string {
  return JSON.stringify(v, replacer);
}

async function main() {
  const brandNeedle = process.env.BRAND_NAME ?? "Modern Heart";

  const brand = await prisma.brand.findFirst({
    where: { name: { contains: brandNeedle, mode: "insensitive" } },
    select: { id: true, name: true, domain: true, createdAt: true },
  });
  if (!brand) {
    console.error(`No brand matching "${brandNeedle}" found.`);
    process.exit(1);
  }

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

  log("=== Brand ===");
  log(j({ brand }));
  log("=== TrackedSite ===");
  log(j({ site }));

  // ─── Direct-brandId tables ──────────────────────────────────
  //
  // Each aggregate runs independently so a single failing query
  // doesn't blackhole the rest of the audit. Empty / errored rows
  // render as ERROR in the output.
  const brandTables: Array<{ name: string; run: () => Promise<unknown> }> = [
    {
      name: "SiteConversion",
      run: () => prisma.siteConversion.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      }),
    },
    {
      name: "SiteEvent",
      run: () => prisma.siteEvent.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      }),
    },
    {
      name: "RealUserMetric",
      run: () => prisma.realUserMetric.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      }),
    },
    {
      name: "AiInteraction",
      run: () => prisma.aiInteraction.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      }),
    },
    {
      name: "AiUsageLog",
      run: () => prisma.aiUsageLog.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      }),
    },
    {
      name: "PlatformEvent",
      run: () => prisma.platformEvent.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      }),
    },
    {
      name: "MetricSnapshot",
      run: () => prisma.metricSnapshot.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { date: true },
        _max: { date: true },
      }),
    },
    {
      name: "GscPage",
      run: () => prisma.gscPage.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { date: true },
        _max: { date: true },
      }),
    },
    {
      name: "TechnicalAudit",
      run: () => prisma.technicalAudit.aggregate({
        where: { brandId: brand.id },
        _count: true,
        _min: { createdAt: true },
        _max: { createdAt: true },
      }),
    },
  ];

  log("\n=== Brand-scoped tables ===");
  for (const t of brandTables) {
    try {
      const result = await t.run();
      log(`${t.name}: ${j(result)}`);
    } catch (err) {
      log(`${t.name}: ERROR ${err instanceof Error ? err.message : String(err)}`);
    }
  }

  // ─── TrackedSite-scoped tables ──────────────────────────────
  log("\n=== TrackedSite-scoped tables ===");
  if (!site) {
    log("TrackedEvent / TrackedSession: SKIPPED (no TrackedSite for brand)");
  } else {
    try {
      const te = await prisma.trackedEvent.aggregate({
        where: { trackedSiteId: site.id },
        _count: true,
        _min: { timestamp: true },
        _max: { timestamp: true },
      });
      log(`TrackedEvent: ${j(te)}`);
    } catch (err) {
      log(`TrackedEvent: ERROR ${err instanceof Error ? err.message : String(err)}`);
    }
    try {
      const ts = await prisma.trackedSession.aggregate({
        where: { trackedSiteId: site.id },
        _count: true,
        _min: { startedAt: true },
        _max: { startedAt: true },
      });
      log(`TrackedSession: ${j(ts)}`);
    } catch (err) {
      log(`TrackedSession: ERROR ${err instanceof Error ? err.message : String(err)}`);
    }
  }

  // ─── Type breakdowns (the numbers the UI actually renders) ──
  log("\n=== Distinct event-type counts ===");

  if (site) {
    try {
      const trackedTypes = await prisma.trackedEvent.groupBy({
        by: ["eventType"],
        where: { trackedSiteId: site.id },
        _count: true,
      });
      trackedTypes.sort((a, b) => (b._count as unknown as number) - (a._count as unknown as number));
      // Top 30 only — longer lists hide the signal.
      log(`TrackedEvent types (top 30): ${j(trackedTypes.slice(0, 30))}`);
      log(`TrackedEvent distinct type count: ${trackedTypes.length}`);
    } catch (err) {
      log(`TrackedEvent types: ERROR ${err instanceof Error ? err.message : String(err)}`);
    }
  }

  try {
    const siteConvTypes = await prisma.siteConversion.groupBy({
      by: ["conversionType"],
      where: { brandId: brand.id },
      _count: true,
    });
    siteConvTypes.sort((a, b) => (b._count as unknown as number) - (a._count as unknown as number));
    // All SiteConversion types — there are rarely more than ~20
    // distinct values but capped at 30 for safety.
    log(`SiteConversion types (top 30): ${j(siteConvTypes.slice(0, 30))}`);
    log(`SiteConversion distinct type count: ${siteConvTypes.length}`);
  } catch (err) {
    log(`SiteConversion types: ERROR ${err instanceof Error ? err.message : String(err)}`);
  }

  try {
    const siteEventTypes = await prisma.siteEvent.groupBy({
      by: ["eventType"],
      where: { brandId: brand.id },
      _count: true,
    });
    siteEventTypes.sort((a, b) => (b._count as unknown as number) - (a._count as unknown as number));
    log(`SiteEvent types (top 20): ${j(siteEventTypes.slice(0, 20))}`);
    log(`SiteEvent distinct type count: ${siteEventTypes.length}`);
  } catch (err) {
    log(`SiteEvent types: ERROR ${err instanceof Error ? err.message : String(err)}`);
  }

  // ─── 30-day windowed ground truth ──────────────────────────
  // The UI's default range is 30 days. Logging both all-time
  // and 30-day counts side by side so "why does the dashboard
  // show 528 conversions when the DB has 3000+?" is obvious.
  log("\n=== 30-day windowed counts ===");
  const since30d = new Date(Date.now() - 30 * 86_400_000);

  try {
    const conv30 = await prisma.siteConversion.count({
      where: { brandId: brand.id, timestamp: { gte: since30d } },
    });
    log(`SiteConversion (30d): ${conv30}`);
  } catch (err) {
    log(`SiteConversion (30d): ERROR ${err instanceof Error ? err.message : String(err)}`);
  }

  if (site) {
    try {
      const te30 = await prisma.trackedEvent.count({
        where: { trackedSiteId: site.id, timestamp: { gte: since30d } },
      });
      log(`TrackedEvent (30d): ${te30}`);
      const sessStart30 = await prisma.trackedEvent.count({
        where: { trackedSiteId: site.id, eventType: "session_start", timestamp: { gte: since30d } },
      });
      log(`TrackedEvent session_start (30d): ${sessStart30}`);
      const pv30 = await prisma.trackedEvent.count({
        where: { trackedSiteId: site.id, eventType: "page_view", timestamp: { gte: since30d } },
      });
      log(`TrackedEvent page_view (30d): ${pv30}`);
      const ts30 = await prisma.trackedSession.count({
        where: { trackedSiteId: site.id, startedAt: { gte: since30d } },
      });
      log(`TrackedSession (30d): ${ts30}`);
    } catch (err) {
      log(`TrackedEvent/TrackedSession (30d): ERROR ${err instanceof Error ? err.message : String(err)}`);
    }
  }

  try {
    const gsc30 = await prisma.gscPage.aggregate({
      where: { brandId: brand.id, date: { gte: since30d } },
      _sum: { clicks: true, impressions: true },
      _avg: { position: true, ctr: true },
    });
    log(`GscPage (30d) aggregate: ${j(gsc30)}`);
  } catch (err) {
    log(`GscPage (30d): ERROR ${err instanceof Error ? err.message : String(err)}`);
  }

  try {
    const ai30 = await prisma.aiUsageLog.aggregate({
      where: { brandId: brand.id, timestamp: { gte: since30d } },
      _count: true,
      _sum: { estimatedCost: true, inputTokens: true, outputTokens: true },
    });
    log(`AiUsageLog (30d): ${j(ai30)}`);
  } catch (err) {
    log(`AiUsageLog (30d): ERROR ${err instanceof Error ? err.message : String(err)}`);
  }

  log("\n=== Audit complete ===");

  // Flush the full run to scripts/audit-results.md so the result is
  // checked in alongside the code-side audit. Wrapping in ``` keeps
  // the markdown file renderable even for large JSON payloads.
  const outPath = "scripts/audit-results.md";
  const now = new Date().toISOString();
  const body = [
    "# Phase 1 — Metric Ground-Truth Audit",
    "",
    `**Generated:** ${now}`,
    `**Brand:** ${brand.name} (${brand.id})`,
    `**Domain:** ${brand.domain}`,
    "",
    "This file is produced by `scripts/audit-phase1.ts` and captures",
    "the raw per-table counts + event-type breakdowns for a single",
    "brand. These are the **ground-truth numbers** the UI surfaces",
    "must agree with.",
    "",
    "Re-run with `npx tsx scripts/audit-phase1.ts` — the file is",
    "overwritten in place.",
    "",
    "## Full output",
    "",
    "```text",
    ...report,
    "```",
    "",
  ].join("\n");
  mkdirSync(dirname(outPath), { recursive: true });
  writeFileSync(outPath, body, "utf8");
  console.log(`\n[audit-phase1] wrote ${outPath} (${report.length} lines)`);
}

main()
  .catch((err) => {
    console.error("[audit-phase1] FATAL", err);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());
