/**
 * POST /api/site-tag/page-seen
 *
 * Called by the meSEO Site Tag on every page load (and every SPA
 * navigation) to record a page visit. Upserts a PageRecord for the URL,
 * updates lastSeenAt, bumps totalVisits, and records a PageStatusChange
 * if the HTTP status differs from the last known status.
 *
 * Auth: siteId (public) + domain whitelist via resolveSiteFromTag.
 */

import { NextResponse } from "next/server";
import {
  getCorsHeaders,
  resolveSiteFromTag,
  toPathKey,
  buildFullUrl,
  upsertPageSeen,
} from "@/lib/services/page-lifecycle";
import { prisma } from "@/lib/prisma";

export const dynamic = "force-dynamic";

export async function OPTIONS(req: Request) {
  return new NextResponse(null, { status: 204, headers: getCorsHeaders(req) });
}

interface PageSeenBody {
  siteId?: string;
  url?: string;
  fullUrl?: string;
  title?: string;
  statusCode?: number;
  referrer?: string;
  loadTime?: number;
  metaDescription?: string;
  h1?: string;
  canonicalUrl?: string;
  wordCount?: number;
}

export async function POST(req: Request) {
  const cors = getCorsHeaders(req);
  let body: PageSeenBody;
  try {
    body = (await req.json()) as PageSeenBody;
  } catch {
    return NextResponse.json({ ok: false, error: "Invalid JSON" }, { status: 400, headers: cors });
  }

  const anyUrl = body.fullUrl || body.url || "";
  const resolved = await resolveSiteFromTag(body.siteId, anyUrl);
  if (!resolved.ok) {
    return NextResponse.json(
      { ok: false, error: resolved.error },
      { status: resolved.status, headers: cors },
    );
  }

  const path = toPathKey(body.url || body.fullUrl || "/");
  const fullUrl = buildFullUrl(null, path, body.fullUrl);

  // Clamp user-supplied strings to sane lengths to prevent abuse
  const title = body.title ? String(body.title).slice(0, 500) : null;
  const metaDescription = body.metaDescription ? String(body.metaDescription).slice(0, 1000) : null;
  const h1 = body.h1 ? String(body.h1).slice(0, 500) : null;
  const canonicalUrl = body.canonicalUrl ? String(body.canonicalUrl).slice(0, 2048) : null;
  const wordCount = typeof body.wordCount === "number" && Number.isFinite(body.wordCount)
    ? Math.min(200_000, Math.max(0, Math.floor(body.wordCount)))
    : null;

  const result = await upsertPageSeen({
    brandId: resolved.site.brandId,
    url: path,
    fullUrl,
    title,
    statusCode: typeof body.statusCode === "number" ? body.statusCode : 200,
    metaDescription,
    h1,
    canonicalUrl,
    wordCount,
  });

  // If the referrer is an internal page, record a visitor-observed
  // navigation edge. This doubles as a simple internal-link hint: if
  // visitors actually clicked from /a to /b, /a links to /b.
  if (body.referrer) {
    try {
      const refUrl = new URL(body.referrer);
      const refHost = refUrl.hostname.toLowerCase();
      const allowed = resolved.site.allowedDomains.map((d) => d.toLowerCase());
      const internal = allowed.some((d) => refHost === d || refHost.endsWith(`.${d}`));
      if (internal && refUrl.pathname !== path) {
        // Fire-and-forget: touch the referrer page's lastSeenAt without
        // bumping totalVisits (this was a navigation OUT of it, not TO it).
        const refPath = toPathKey(refUrl.pathname);
        prisma.pageRecord.updateMany({
          where: { brandId: resolved.site.brandId, url: refPath },
          data: { lastSeenAt: new Date() },
        }).catch(() => { /* non-fatal */ });
      }
    } catch {
      // Invalid referrer URL — ignore
    }
  }

  return NextResponse.json(
    {
      ok: true,
      pageId: result.pageId,
      isNewPage: result.isNew,
      statusChanged: result.statusChanged,
      ...(result.statusChanged ? { fromStatus: result.fromStatus, toStatus: result.toStatus } : {}),
    },
    { headers: cors },
  );
}
