/**
 * GET /api/pages/tracker?brandId=xxx&status=all|200|3xx|404|5xx&sort=firstSeen&dir=desc&nav=all|in|out
 *
 * Returns the full PageRecord list for the Launch Tracker UI with
 * filters, sorting, summary counts, and redirect/status-change metadata.
 * Auth: user must be a member of the brand's organization.
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import type { Prisma } from "@prisma/client";
import {
  getCachedBrandSitemap,
  buildSitemapLookup,
} from "@/lib/services/brand-sitemap";
import { normalizePath } from "@/lib/services/url-normalize";

export const dynamic = "force-dynamic";

type StatusFilter = "all" | "live" | "redirect" | "notfound" | "servererror";
type NavFilter = "all" | "in" | "out";
type SortKey = "firstSeen" | "lastSeen" | "clicks" | "impressions" | "position" | "url";

export async function GET(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { searchParams } = new URL(req.url);
  const brandId = searchParams.get("brandId");
  if (!brandId) return NextResponse.json({ error: "brandId required" }, { status: 400 });

  const statusFilter = (searchParams.get("status") || "all") as StatusFilter;
  const navFilter = (searchParams.get("nav") || "all") as NavFilter;
  const sort = (searchParams.get("sort") || "lastSeen") as SortKey;
  const dir = searchParams.get("dir") === "asc" ? "asc" : "desc";
  const limit = Math.min(500, Math.max(10, Number(searchParams.get("limit")) || 200));

  // Permission check
  const brand = await prisma.brand.findUnique({
    where: { id: brandId },
    include: { organization: { include: { memberships: true } } },
  });
  if (!brand || !brand.organization.memberships.some((m) => m.userId === user.id)) {
    return NextResponse.json({ error: "Access denied" }, { status: 403 });
  }

  // Build where clause
  const where: Prisma.PageRecordWhereInput = { brandId };
  if (statusFilter === "live") where.currentStatus = 200;
  else if (statusFilter === "redirect") where.currentStatus = { in: [301, 302, 307, 308] };
  else if (statusFilter === "notfound") where.currentStatus = 404;
  else if (statusFilter === "servererror") where.currentStatus = { gte: 500, lt: 600 };

  if (navFilter === "in") where.isInNav = true;
  else if (navFilter === "out") where.isInNav = false;

  const orderBy: Prisma.PageRecordOrderByWithRelationInput =
    sort === "firstSeen" ? { firstSeenAt: dir }
    : sort === "lastSeen" ? { lastSeenAt: dir }
    : sort === "clicks" ? { totalClicks: dir }
    : sort === "impressions" ? { totalImpressions: dir }
    : sort === "position" ? { currentPosition: dir }
    : { url: dir };

  const pages = await prisma.pageRecord.findMany({
    where,
    orderBy,
    take: limit,
    include: {
      _count: { select: { redirectsToThis: true, statusHistory: true } },
    },
  });

  // ── Real launch-date join ─────────────────────────────────────────
  // Each PageRecord stores a `firstSeenAt` that equals the moment the
  // Site Tag fired its first event for the URL — which for a brand
  // with a freshly-installed tag means "tag install date", NOT the
  // page's actual publish date. Real launch dates come from two
  // independent signals:
  //
  //   • sitemap <lastmod>  — the publisher's own declaration of when
  //                          the page was last updated (≈ published).
  //   • gscPage.date (min) — the earliest day Google recorded this URL
  //                          in search results.
  //
  // We surface all three on every row and let the UI pick the
  // earliest-known as the primary "launch date".

  const [sitemapCache, gscFirstSeenRows] = await Promise.all([
    getCachedBrandSitemap(brandId),
    // One aggregation pulls earliest GSC date per URL for every page
    // this brand has ever ranked for — O(1) extra queries regardless
    // of page count.
    prisma.gscPage.groupBy({
      by: ["pageUrl"],
      where: { brandId },
      _min: { date: true },
    }),
  ]);

  const sitemapLookup = sitemapCache ? buildSitemapLookup(sitemapCache.entries) : new Map<string, Date | null>();

  // Normalised path → earliest GSC date. Paths are normalised on both
  // sides so www./trailing-slash differences don't break the join.
  const gscFirstSeenByPath = new Map<string, Date>();
  for (const row of gscFirstSeenRows) {
    if (!row._min.date) continue;
    const path = normalizePath(row.pageUrl);
    if (!path) continue;
    const existing = gscFirstSeenByPath.get(path);
    if (!existing || row._min.date < existing) {
      gscFirstSeenByPath.set(path, row._min.date);
    }
  }

  // Summary counts across the whole brand (not filtered)
  const [total, live, redirects, notFound, serverError, newThisMonth, inNav] = await Promise.all([
    prisma.pageRecord.count({ where: { brandId } }),
    prisma.pageRecord.count({ where: { brandId, currentStatus: 200 } }),
    prisma.pageRecord.count({ where: { brandId, currentStatus: { in: [301, 302, 307, 308] } } }),
    prisma.pageRecord.count({ where: { brandId, currentStatus: 404 } }),
    prisma.pageRecord.count({ where: { brandId, currentStatus: { gte: 500, lt: 600 } } }),
    prisma.pageRecord.count({
      where: {
        brandId,
        firstSeenAt: { gte: startOfMonth() },
      },
    }),
    prisma.pageRecord.count({ where: { brandId, isInNav: true } }),
  ]);

  // Recently detected (last 7 days)
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
  const recentlyDetected = await prisma.pageRecord.findMany({
    where: { brandId, firstSeenAt: { gte: sevenDaysAgo } },
    orderBy: { firstSeenAt: "desc" },
    take: 10,
    select: {
      id: true, url: true, title: true, firstSeenAt: true,
      currentStatus: true, isInNav: true, addedToNavAt: true,
    },
  });

  // Recent 404s (pages where a status change landed on 404 in the last 30 days)
  const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
  const recent404s = await prisma.pageStatusChange.findMany({
    where: {
      toStatus: 404,
      detectedAt: { gte: thirtyDaysAgo },
      page: { brandId },
    },
    orderBy: { detectedAt: "desc" },
    take: 15,
    include: {
      page: {
        select: {
          id: true, url: true, title: true, totalClicks: true,
          totalImpressions: true, internalLinks: true,
        },
      },
    },
  });

  return NextResponse.json({
    summary: {
      total,
      live,
      redirects,
      notFound,
      serverError,
      newThisMonth,
      inNav,
    },
    pages: pages.map((p) => {
      // Path used for joining against sitemap + GSC data. PageRecord
      // stores paths like "/about" so a double-normalisation is a no-op
      // but keeps the lookup consistent with the Map builders above.
      const joinPath = normalizePath(p.url);
      const sitemapLastmod = sitemapLookup.get(joinPath) ?? null;
      const gscFirstSeen = gscFirstSeenByPath.get(joinPath) ?? null;
      const tagFirstSeen = p.firstSeenAt;

      // True launch date = earliest credible signal. Sitemap lastmod
      // wins when present (closest to "published"); GSC first-seen is
      // next (close to indexing); tag first-seen is the fallback.
      const candidates: Date[] = [];
      if (sitemapLastmod) candidates.push(sitemapLastmod);
      if (gscFirstSeen) candidates.push(gscFirstSeen);
      candidates.push(tagFirstSeen);
      const launchDate = candidates.reduce((earliest, d) => (d < earliest ? d : earliest));
      const daysLive = Math.max(0, Math.floor((Date.now() - launchDate.getTime()) / (1000 * 60 * 60 * 24)));

      return {
        id: p.id,
        url: p.url,
        fullUrl: p.fullUrl,
        title: p.title,
        currentStatus: p.currentStatus,
        firstSeenAt: p.firstSeenAt, // kept for backwards compat (= tag first seen)
        lastSeenAt: p.lastSeenAt,
        isInNav: p.isInNav,
        addedToNavAt: p.addedToNavAt,
        totalClicks: p.totalClicks,
        totalImpressions: p.totalImpressions,
        currentCtr: p.currentCtr,
        currentPosition: p.currentPosition,
        totalVisits: p.totalVisits,
        redirectsInCount: p._count.redirectsToThis,
        statusChangeCount: p._count.statusHistory,
        daysLive,
        // ── Real launch-date fields ──────────────────────────────
        sitemapLastmod: sitemapLastmod ? sitemapLastmod.toISOString() : null,
        gscFirstSeenAt: gscFirstSeen ? gscFirstSeen.toISOString() : null,
        tagFirstSeenAt: tagFirstSeen.toISOString(),
        /** Earliest of the three — what the UI renders as the primary
         *  "launch date". Callers can render a tooltip with all 3. */
        launchDate: launchDate.toISOString(),
        /** Which source won: "sitemap" | "gsc" | "tag". */
        launchSource:
          sitemapLastmod && sitemapLastmod.getTime() === launchDate.getTime() ? "sitemap"
          : gscFirstSeen && gscFirstSeen.getTime() === launchDate.getTime() ? "gsc"
          : "tag",
      };
    }),
    sitemapCache: sitemapCache
      ? { crawledAt: sitemapCache.crawledAt, count: sitemapCache.count }
      : null,
    recentlyDetected,
    recent404s: recent404s.map((c) => ({
      pageId: c.page.id,
      url: c.page.url,
      title: c.page.title,
      detectedAt: c.detectedAt,
      fromStatus: c.fromStatus,
      lostClicks: c.page.totalClicks,
      lostImpressions: c.page.totalImpressions,
      inboundLinkCount: Array.isArray(c.page.internalLinks)
        ? (c.page.internalLinks as unknown[]).length
        : 0,
    })),
  });
}

function startOfMonth() {
  const d = new Date();
  return new Date(d.getFullYear(), d.getMonth(), 1);
}
