import { NextRequest } from "next/server";
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";

/**
 * GET /api/devices/events — Server-Sent Events for real-time device updates.
 *
 * Polls the DB every 5 seconds for device state changes and emits SSE events:
 *   - device_online   — a device transitioned to "online"
 *   - device_offline   — a device transitioned to "offline"
 *   - device_updated  — device data changed (models, VRAM, heartbeat fields)
 *   - heartbeat       — periodic keepalive with full device list snapshot
 *
 * Auth: requires a valid Auth.js session (cookie-based).
 * Supports Last-Event-ID header for reconnection.
 */

const POLL_INTERVAL_MS = 5_000;

interface DeviceSnapshot {
  id: string;
  name: string;
  tunnel_url: string;
  models: string[];
  hardware: string;
  status: string;
  last_seen: string;
  latency_ms: number | null;
  vram_usage_mb: number | null;
  active_model: string | null;
  requests_served: number;
  uptime_seconds: number;
  savings_usd: number;
  marketplace_enabled: boolean;
  price_cents_per_hour: number | null;
}

function deviceToSnapshot(d: {
  id: string;
  name: string;
  tunnelUrl: string;
  models: string;
  hardware: string;
  status: string;
  lastSeen: Date;
  latencyMs: number | null;
  vramUsageMb: number | null;
  activeModel: string | null;
  requestsServed: number;
  uptimeSeconds: number;
  savingsUsd: number;
  marketplaceEnabled: boolean;
  marketplacePriceCents: number | null;
}): DeviceSnapshot {
  return {
    id: d.id,
    name: d.name,
    tunnel_url: d.tunnelUrl,
    models: JSON.parse(d.models || "[]") as string[],
    hardware: d.hardware,
    status: d.status,
    last_seen: d.lastSeen.toISOString(),
    latency_ms: d.latencyMs,
    vram_usage_mb: d.vramUsageMb,
    active_model: d.activeModel,
    requests_served: d.requestsServed,
    uptime_seconds: d.uptimeSeconds,
    savings_usd: d.savingsUsd,
    marketplace_enabled: d.marketplaceEnabled,
    price_cents_per_hour: d.marketplacePriceCents,
  };
}

function formatSSE(event: string, data: unknown, id: string): string {
  return `id: ${id}\nevent: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
}

export async function GET(req: NextRequest) {
  const session = await auth();
  if (!session?.user?.id) {
    return new Response(JSON.stringify({ error: "Unauthorized" }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  const userId = session.user.id;

  // Parse Last-Event-ID for reconnection support
  const lastEventId = req.headers.get("Last-Event-ID");
  let eventCounter = lastEventId ? parseInt(lastEventId, 10) || 0 : 0;

  const stream = new ReadableStream({
    async start(controller) {
      const encoder = new TextEncoder();

      // Track previous device state for diff detection
      let previousDevices = new Map<string, DeviceSnapshot>();

      // Load initial state
      try {
        const devices = await db.device.findMany({
          where: { userId },
          orderBy: { lastSeen: "desc" },
        });
        const snapshots = devices.map(deviceToSnapshot);
        for (const snap of snapshots) {
          previousDevices.set(snap.id, snap);
        }

        // Send initial snapshot as heartbeat
        eventCounter++;
        controller.enqueue(
          encoder.encode(
            formatSSE("heartbeat", { devices: snapshots }, String(eventCounter))
          )
        );
      } catch {
        controller.enqueue(
          encoder.encode(
            formatSSE("error", { message: "Failed to load initial devices" }, "0")
          )
        );
        controller.close();
        return;
      }

      // Poll loop
      const interval = setInterval(async () => {
        try {
          const devices = await db.device.findMany({
            where: { userId },
            orderBy: { lastSeen: "desc" },
          });
          const currentSnapshots = devices.map(deviceToSnapshot);
          const currentMap = new Map<string, DeviceSnapshot>();
          for (const snap of currentSnapshots) {
            currentMap.set(snap.id, snap);
          }

          // Detect changes
          for (const [id, current] of currentMap) {
            const previous = previousDevices.get(id);

            if (!previous) {
              // New device appeared
              eventCounter++;
              controller.enqueue(
                encoder.encode(
                  formatSSE("device_online", { device: current }, String(eventCounter))
                )
              );
              continue;
            }

            // Status transition: offline -> online
            if (previous.status !== "online" && current.status === "online") {
              eventCounter++;
              controller.enqueue(
                encoder.encode(
                  formatSSE("device_online", { device: current }, String(eventCounter))
                )
              );
              continue;
            }

            // Status transition: online -> offline
            if (previous.status !== "offline" && current.status === "offline") {
              eventCounter++;
              controller.enqueue(
                encoder.encode(
                  formatSSE("device_offline", { device: current }, String(eventCounter))
                )
              );
              continue;
            }

            // Check for data changes (models, VRAM, active model, requests, etc.)
            if (
              previous.last_seen !== current.last_seen ||
              previous.vram_usage_mb !== current.vram_usage_mb ||
              previous.active_model !== current.active_model ||
              previous.requests_served !== current.requests_served ||
              previous.uptime_seconds !== current.uptime_seconds ||
              JSON.stringify(previous.models) !== JSON.stringify(current.models)
            ) {
              eventCounter++;
              controller.enqueue(
                encoder.encode(
                  formatSSE("device_updated", { device: current }, String(eventCounter))
                )
              );
            }
          }

          // Detect removed devices (went offline / deleted)
          for (const [id, previous] of previousDevices) {
            if (!currentMap.has(id)) {
              eventCounter++;
              controller.enqueue(
                encoder.encode(
                  formatSSE(
                    "device_offline",
                    { device: { ...previous, status: "offline" } },
                    String(eventCounter)
                  )
                )
              );
            }
          }

          // Send periodic heartbeat with full snapshot every poll
          eventCounter++;
          controller.enqueue(
            encoder.encode(
              formatSSE("heartbeat", { devices: currentSnapshots }, String(eventCounter))
            )
          );

          // Update previous state
          previousDevices = currentMap;
        } catch {
          // DB errors are non-fatal — skip this tick
        }
      }, POLL_INTERVAL_MS);

      // Cleanup on abort (client disconnects)
      req.signal.addEventListener("abort", () => {
        clearInterval(interval);
        controller.close();
      });
    },
  });

  return new Response(stream, {
    headers: {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache, no-transform",
      Connection: "keep-alive",
      "X-Accel-Buffering": "no", // Disable Nginx/Traefik buffering
    },
  });
}
