/**
 * GET /api/site-tag/analytics/video-chat?brandId=X&range=7d|30d|90d
 *
 * Powers the Video / Chat tab on /site-tag-analytics. Reads
 * from TrackedEvent (not SiteEvent) — t.js emits video_play,
 * video_progress, video_complete, video_engagement via sendEvent()
 * which routes to /api/collect, so the rows live in TrackedEvent
 * keyed by trackedSiteId. Chat events (chat_opened,
 * chat_widget_detected, chat_message_sent) live in the same table.
 *
 * Response shape:
 *   {
 *     stats: {
 *       videoPlays, videoCompletes, completionRate,
 *       avgWatchTimeMs, totalWatchTimeMs,
 *       chatOpens, chatWidgetsDetected, chatMessages,
 *     },
 *     topVideos: [{ videoId, title, provider, plays, completes,
 *                   completionRate, avgWatchTimeMs }],
 *     chat:  { opens, widgetsDetected, messages, topPage,
 *              events: [{ date, page, platform, action }] },
 *     videos: [ legacy empty array kept for cached clients ],
 *   }
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";

export const dynamic = "force-dynamic";

const CACHE_HEADERS = {
  "Cache-Control": "private, max-age=30, stale-while-revalidate=120",
};

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");
  const range = searchParams.get("range") || "30d";
  if (!brandId) return NextResponse.json({ error: "brandId required" }, { status: 400 });

  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 });
  }

  const days = range === "1d" ? 1 : range === "7d" ? 7 : range === "28d" ? 28 : range === "90d" ? 90 : 30;
  const since = new Date(Date.now() - days * 86_400_000);

  // TrackedEvent rows carry trackedSiteId, not brandId — resolve once.
  const trackedSite = await prisma.trackedSite
    .findUnique({ where: { brandId }, select: { id: true } })
    .catch(() => null);

  if (!trackedSite) {
    return NextResponse.json(emptyPayload(), { headers: CACHE_HEADERS });
  }

  try {
    const [
      videoPlays,
      videoCompletes,
      chatOpens,
      chatWidgetsDetected,
      chatMessages,
      videoPlayRows,
      videoCompleteRows,
      videoEngagementRows,
      chatEventRows,
    ] = await Promise.all([
      prisma.trackedEvent
        .count({ where: { trackedSiteId: trackedSite.id, eventType: "video_play", timestamp: { gte: since } } })
        .catch(() => 0),
      prisma.trackedEvent
        .count({ where: { trackedSiteId: trackedSite.id, eventType: "video_complete", timestamp: { gte: since } } })
        .catch(() => 0),
      prisma.trackedEvent
        .count({ where: { trackedSiteId: trackedSite.id, eventType: "chat_opened", timestamp: { gte: since } } })
        .catch(() => 0),
      prisma.trackedEvent
        .count({ where: { trackedSiteId: trackedSite.id, eventType: "chat_widget_detected", timestamp: { gte: since } } })
        .catch(() => 0),
      prisma.trackedEvent
        .count({ where: { trackedSiteId: trackedSite.id, eventType: "chat_message_sent", timestamp: { gte: since } } })
        .catch(() => 0),
      // Raw rows for in-memory per-video aggregation. Prisma groupBy
      // on Json doesn't bucket sensibly (whole-blob equality), so we
      // pull recent rows and bucket client-side. 1000 is plenty for
      // a 90-day window on a mid-volume site.
      prisma.trackedEvent
        .findMany({
          where: { trackedSiteId: trackedSite.id, eventType: "video_play", timestamp: { gte: since } },
          select: { pageUrl: true, metadata: true },
          orderBy: { timestamp: "desc" },
          take: 1000,
        })
        .catch(() => [] as Array<{ pageUrl: string; metadata: unknown }>),
      prisma.trackedEvent
        .findMany({
          where: { trackedSiteId: trackedSite.id, eventType: "video_complete", timestamp: { gte: since } },
          select: { pageUrl: true, metadata: true },
          orderBy: { timestamp: "desc" },
          take: 1000,
        })
        .catch(() => [] as Array<{ pageUrl: string; metadata: unknown }>),
      prisma.trackedEvent
        .findMany({
          where: { trackedSiteId: trackedSite.id, eventType: "video_engagement", timestamp: { gte: since } },
          select: { pageUrl: true, metadata: true },
          orderBy: { timestamp: "desc" },
          take: 1000,
        })
        .catch(() => [] as Array<{ pageUrl: string; metadata: unknown }>),
      prisma.trackedEvent
        .findMany({
          where: {
            trackedSiteId: trackedSite.id,
            eventType: { in: ["chat_opened", "chat_widget_detected", "chat_message_sent"] },
            timestamp: { gte: since },
          },
          select: { pageUrl: true, eventType: true, metadata: true, timestamp: true },
          orderBy: { timestamp: "desc" },
          take: 200,
        })
        .catch(() => [] as Array<{ pageUrl: string; eventType: string; metadata: unknown; timestamp: Date }>),
    ]);

    // ── Per-video aggregation ──────────────────────────────────
    // Key by video_id (fall back to title, then pageUrl + sequence
    // index so headless players without ids still surface).
    type VideoBucket = {
      videoId: string;
      title: string | null;
      provider: string | null;
      plays: number;
      completes: number;
      watchTimeMs: number;
      engagementSamples: number;
    };
    const byVideo: Record<string, VideoBucket> = {};
    function videoKey(meta: Record<string, unknown>, pageUrl: string): string {
      const id = typeof meta.video_id === "string" && meta.video_id ? meta.video_id : null;
      if (id) return id;
      const title = typeof meta.title === "string" && meta.title ? meta.title : null;
      if (title) return "title:" + title.slice(0, 100);
      return "page:" + pageUrl;
    }
    function ensureBucket(key: string, meta: Record<string, unknown>): VideoBucket {
      if (!byVideo[key]) {
        byVideo[key] = {
          videoId: typeof meta.video_id === "string" ? meta.video_id : key.replace(/^(title|page):/, ""),
          title: typeof meta.title === "string" ? meta.title : null,
          provider: typeof meta.provider === "string" ? meta.provider : null,
          plays: 0,
          completes: 0,
          watchTimeMs: 0,
          engagementSamples: 0,
        };
      } else {
        // Later-seen metadata may include fields earlier rows lacked
        // (e.g. engagement events carry title when play events
        // didn't). Fill forward where possible.
        if (!byVideo[key].title && typeof meta.title === "string") byVideo[key].title = meta.title;
        if (!byVideo[key].provider && typeof meta.provider === "string") byVideo[key].provider = meta.provider;
      }
      return byVideo[key];
    }

    for (const r of videoPlayRows) {
      const meta = (r.metadata || {}) as Record<string, unknown>;
      ensureBucket(videoKey(meta, r.pageUrl), meta).plays++;
    }
    for (const r of videoCompleteRows) {
      const meta = (r.metadata || {}) as Record<string, unknown>;
      ensureBucket(videoKey(meta, r.pageUrl), meta).completes++;
    }
    let totalWatchTimeMs = 0;
    let engagementSampleCount = 0;
    for (const r of videoEngagementRows) {
      const meta = (r.metadata || {}) as Record<string, unknown>;
      const b = ensureBucket(videoKey(meta, r.pageUrl), meta);
      const wtm = typeof meta.watch_time_ms === "number" && Number.isFinite(meta.watch_time_ms) ? meta.watch_time_ms : 0;
      b.watchTimeMs += Math.max(0, wtm);
      b.engagementSamples++;
      totalWatchTimeMs += wtm;
      engagementSampleCount++;
    }

    const topVideos = Object.values(byVideo)
      .map((v) => ({
        videoId: v.videoId,
        title: v.title,
        provider: v.provider,
        plays: v.plays,
        completes: v.completes,
        completionRate: v.plays > 0 ? Math.min(100, Math.round((v.completes / v.plays) * 100)) : 0,
        avgWatchTimeMs: v.engagementSamples > 0 ? Math.round(v.watchTimeMs / v.engagementSamples) : 0,
      }))
      .sort((a, b) => b.plays - a.plays)
      .slice(0, 20);

    // ── Chat aggregation (recent-events table + top-page stat) ──
    const chatPageCounts: Record<string, number> = {};
    const chatList: Array<{ date: string; page: string; platform: string; action: string }> = [];
    for (const e of chatEventRows) {
      const meta = (e.metadata || {}) as Record<string, unknown>;
      const platform =
        (typeof meta.platform === "string" && meta.platform) ||
        (typeof meta.chatPlatform === "string" && meta.chatPlatform) ||
        (typeof meta.provider === "string" && meta.provider) ||
        "unknown";
      const action = e.eventType.replace(/^chat_/, "");
      chatPageCounts[e.pageUrl] = (chatPageCounts[e.pageUrl] || 0) + 1;
      chatList.push({
        date: e.timestamp.toISOString(),
        page: e.pageUrl,
        platform: String(platform).slice(0, 60),
        action,
      });
    }
    const topChatPage = Object.entries(chatPageCounts).sort(([, a], [, b]) => b - a)[0]?.[0] || null;

    return NextResponse.json(
      {
        stats: {
          videoPlays,
          videoCompletes,
          completionRate: videoPlays > 0 ? Math.min(100, Math.round((videoCompletes / videoPlays) * 100)) : 0,
          totalWatchTimeMs,
          avgWatchTimeMs: engagementSampleCount > 0 ? Math.round(totalWatchTimeMs / engagementSampleCount) : 0,
          chatOpens,
          chatWidgetsDetected,
          chatMessages,
        },
        topVideos,
        chat: {
          opens: chatOpens,
          widgetsDetected: chatWidgetsDetected,
          messages: chatMessages,
          topPage: topChatPage,
          events: chatList.slice(0, 50),
        },
        // Legacy field kept so older cached clients don't crash.
        // Superseded by `topVideos` above.
        videos: [],
      },
      { headers: CACHE_HEADERS },
    );
  } catch (err) {
    console.error("[site-tag/analytics:video-chat] failed", err);
    return NextResponse.json(emptyPayload());
  }
}

function emptyPayload() {
  return {
    stats: {
      videoPlays: 0,
      videoCompletes: 0,
      completionRate: 0,
      totalWatchTimeMs: 0,
      avgWatchTimeMs: 0,
      chatOpens: 0,
      chatWidgetsDetected: 0,
      chatMessages: 0,
    },
    topVideos: [] as Array<never>,
    chat: { opens: 0, widgetsDetected: 0, messages: 0, topPage: null, events: [] as Array<never> },
    videos: [] as Array<never>,
  };
}
