/**
 * GET /api/admin/sessions?brandId=X&page=1&limit=20&filter=...
 *
 * Admin-facing session list for a brand. Designed to answer
 * "which sessions converted, and what did their journey look like?"
 * without forcing the operator into a raw-SQL panel.
 *
 * Query params:
 *   brandId  — required. Scopes to that brand's TrackedSite.
 *   page     — 1-indexed, default 1.
 *   limit    — default 25, max 100.
 *   filter   — all | conversions | phone | form | booking.
 *                all         → every session in the window.
 *                conversions → sessions with ≥1 SiteConversion.
 *                phone       → sessions with a phone_call conversion.
 *                form        → sessions with a form_submitted /
 *                              form_submit / form_submission.
 *                booking     → sessions with an appointment_booked /
 *                              booking_confirmed.
 *   range    — 1d | 7d | 30d | 90d, default 30d.
 *   sort     — recent | interactions | duration, default recent.
 *
 * Returns:
 *   {
 *     sessions: [{
 *       sessionId, internalId, startedAt, lastSeenAt, durationMs,
 *       pageCount, eventCount, conversionCount,
 *       topConversionType, source, medium, country, device,
 *     }],
 *     total, page, limit, hasMore, scope,
 *   }
 *
 * Lives under /api/admin/* so the admin-host middleware lets it
 * through. Superadmin-gated.
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { requireSuperadmin, withAdminTiming } from "@/lib/admin-auth";
import { canonicalConversionType, type CanonicalConversionType } from "@/lib/conversion-taxonomy";

export const dynamic = "force-dynamic";
export const maxDuration = 30;

type Filter = "all" | "conversions" | "phone" | "form" | "booking";
type Sort   = "recent" | "interactions" | "duration";
type Range  = "1d" | "7d" | "30d" | "90d";

function rangeStart(r: Range): Date {
  const days = r === "1d" ? 1 : r === "7d" ? 7 : r === "90d" ? 90 : 30;
  return new Date(Date.now() - days * 86_400_000);
}

// Match the filter label → set of canonical conversion types.
const FILTER_CANONICAL: Record<Exclude<Filter, "all" | "conversions">, CanonicalConversionType[]> = {
  phone:   ["phone_call"],
  form:    ["form_submitted"],
  booking: ["appointment_booked"],
};

export async function GET(req: Request) {
  const denied = await requireSuperadmin();
  if (denied) return denied;

  const { searchParams } = new URL(req.url);
  const brandId = searchParams.get("brandId");
  if (!brandId) {
    return NextResponse.json({ error: "brandId required" }, { status: 400 });
  }
  const filter = (searchParams.get("filter") ?? "all") as Filter;
  const range  = (searchParams.get("range")  ?? "30d") as Range;
  const sort   = (searchParams.get("sort")   ?? "recent") as Sort;
  const page   = Math.max(1, Number(searchParams.get("page")  ?? 1));
  const limit  = Math.max(1, Math.min(100, Number(searchParams.get("limit") ?? 25)));

  return NextResponse.json(await withAdminTiming("admin.sessions.list", async () => {
    const site = await prisma.trackedSite.findUnique({
      where: { brandId }, select: { id: true },
    });
    if (!site) {
      return { sessions: [], total: 0, page, limit, hasMore: false, scope: { brandId }, error: "No TrackedSite for this brand." };
    }

    const from = rangeStart(range);

    // ── Resolve sessionIds matching the filter ──
    // SiteConversion.sessionId is the RAW client string (TrackedSession.sessionId),
    // while TrackedEvent.sessionId is the TrackedSession.id cuid. The
    // list query joins via TrackedSession.sessionId (the string) when
    // a conversion filter is active.
    let sessionIdWhitelist: string[] | null = null;
    if (filter !== "all") {
      const conversionFilter: { conversionType?: { in: string[] } } = {};
      if (filter === "conversions") {
        // Any canonical conversion — no type filter, just existence.
      } else {
        // Union of the canonical name + every raw source name that
        // resolves to it. canonicalConversionType.sources already
        // enumerates these in the taxonomy table; flatten via
        // CONVERSION_TYPES.
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const _typesForFilter = FILTER_CANONICAL[filter as keyof typeof FILTER_CANONICAL] ?? [];
        // Expand each canonical name to the raw-source strings that
        // resolve to it, plus the canonical name itself.
        const { CONVERSION_TYPES } = await import("@/lib/conversion-taxonomy");
        const union = new Set<string>();
        for (const canon of _typesForFilter) {
          union.add(canon);
          for (const src of CONVERSION_TYPES[canon].sources) {
            // Strip the "gtag:" / "amelia:" / etc. prefix since the
            // SiteConversion.conversionType column stores the bare
            // type, not the source-prefixed string.
            union.add(src.includes(":") ? src.split(":", 2)[1] : src);
          }
        }
        conversionFilter.conversionType = { in: Array.from(union) };
      }

      // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
      const convSessions = await prisma.siteConversion.findMany({
        where: {
          brandId,
          timestamp: { gte: from },
          ...conversionFilter,
        },
        select: { sessionId: true },
        distinct: ["sessionId"],
      });
      sessionIdWhitelist = convSessions.map((s) => s.sessionId);
      if (sessionIdWhitelist.length === 0) {
        return { sessions: [], total: 0, page, limit, hasMore: false, scope: { brandId } };
      }
    }

    // ── Session base query ──
    const sessionWhere = {
      trackedSiteId: site.id,
      startedAt: { gte: from },
      ...(sessionIdWhitelist ? { sessionId: { in: sessionIdWhitelist } } : {}),
    };

    const orderBy = sort === "interactions"
      ? { pageCount: "desc" as const }
      : sort === "duration"
        ? { lastSeenAt: "desc" as const }
        : { startedAt: "desc" as const };

    const [total, sessions] = await Promise.all([
      prisma.trackedSession.count({ where: sessionWhere }),
      prisma.trackedSession.findMany({
        where: sessionWhere,
        orderBy,
        skip: (page - 1) * limit,
        take: limit,
        select: {
          id: true, sessionId: true, startedAt: true, lastSeenAt: true,
          pageCount: true, referrer: true, utmSource: true, utmMedium: true,
          utmCampaign: true, country: true, device: true,
          browser: true, os: true, city: true, region: true,
        },
      }),
    ]);

    // ── Per-session event + conversion counts ──
    // Two groupBy queries keyed off session ids. TrackedEvent.sessionId
    // is the cuid (TrackedSession.id); SiteConversion.sessionId is the
    // string (TrackedSession.sessionId).
    const internalIds = sessions.map((s) => s.id);
    const rawIds      = sessions.map((s) => s.sessionId);

    const [eventCounts, conversionRows] = await Promise.all([
      prisma.trackedEvent.groupBy({
        by: ["sessionId"],
        where: { sessionId: { in: internalIds } },
        _count: true,
      }),
      // intentionally unfiltered: admin-only endpoint; needs raw or all-time counts
      prisma.siteConversion.findMany({
        where: { sessionId: { in: rawIds }, brandId },
        select: { sessionId: true, conversionType: true },
      }),
    ]);
    const eventCountBySession = new Map(eventCounts.map((e) => [e.sessionId ?? "", e._count]));
    const conversionsBySession = new Map<string, { count: number; topType: string | null }>();
    for (const c of conversionRows) {
      const cur = conversionsBySession.get(c.sessionId) ?? { count: 0, topType: null };
      cur.count++;
      if (!cur.topType) cur.topType = canonicalConversionType(c.conversionType) ?? c.conversionType;
      conversionsBySession.set(c.sessionId, cur);
    }

    const rows = sessions.map((s) => {
      const durationMs = Math.max(0, s.lastSeenAt.getTime() - s.startedAt.getTime());
      const conv = conversionsBySession.get(s.sessionId);
      return {
        sessionId: s.sessionId,
        internalId: s.id,
        startedAt: s.startedAt.toISOString(),
        lastSeenAt: s.lastSeenAt.toISOString(),
        durationMs,
        pageCount: s.pageCount,
        eventCount: eventCountBySession.get(s.id) ?? 0,
        conversionCount: conv?.count ?? 0,
        topConversionType: conv?.topType ?? null,
        source: s.utmSource ?? (s.referrer ? "referral" : "direct"),
        medium: s.utmMedium ?? null,
        campaign: s.utmCampaign ?? null,
        referrer: s.referrer ?? null,
        country: s.country ?? null,
        device: s.device ?? null,
        browser: s.browser ?? null,
        os: s.os ?? null,
        city: s.city ?? null,
        region: s.region ?? null,
      };
    });

    return {
      scope: { brandId, filter, range, sort },
      sessions: rows,
      total,
      page,
      limit,
      hasMore: page * limit < total,
    };
  }));
}
