/**
 * Dashboard Real-time Events API (Server-Sent Events)
 *
 * Provides real-time updates to the dashboard via SSE:
 * - New messages from conversations
 * - New bookings
 * - Analytics updates
 * - Workflow status changes
 * - Notifications
 *
 * Client subscribes via EventSource and receives typed events.
 */

import { NextRequest } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/db";
import { logger } from "@/lib/logger";
import { convexQuery } from "@/lib/convex-client";
// Event types that can be sent to clients
type DashboardEventType =
  | "connection"
  | "new_message"
  | "conversation_updated"
  | "new_booking"
  | "booking_updated"
  | "analytics_updated"
  | "new_lead"
  | "workflow_status"
  | "notification"
  | "heartbeat";

interface DashboardEvent {
  type: DashboardEventType;
  data: unknown;
  timestamp: string;
}

// In-memory store for active SSE connections
// In production, use Redis Pub/Sub for multi-instance support
const activeConnections = new Map<
  string,
  Set<ReadableStreamDefaultController<Uint8Array>>
>();

// Global event emitter for broadcasting events
export function emitDashboardEvent(userId: string, event: DashboardEvent) {
  const controllers = activeConnections.get(userId);
  if (!controllers || controllers.size === 0) return;

  const eventString = formatSSEMessage(event);
  const encoder = new TextEncoder();
  const data = encoder.encode(eventString);

  controllers.forEach((controller) => {
    try {
      controller.enqueue(data);
    } catch {
      // Controller may be closed, will be cleaned up
    }
  });
}

// Broadcast to all users (for system-wide events)
export function broadcastDashboardEvent(event: DashboardEvent) {
  activeConnections.forEach((controllers, userId) => {
    emitDashboardEvent(userId, event);
  });
}

// Format message for SSE
function formatSSEMessage(event: DashboardEvent): string {
  const data = JSON.stringify({
    ...event,
    timestamp: event.timestamp || new Date().toISOString(),
  });
  return `event: ${event.type}\ndata: ${data}\n\n`;
}

export async function GET(request: NextRequest) {
  try {
    // Authenticate user
    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;

    // Verify user exists and is active
    const user = await prisma.users.findUnique({
      where: { id: userId },
      select: { id: true, isActive: true },
    });

    if (!user || !user.isActive) {
      return new Response(
        JSON.stringify({ error: "User not found or inactive" }),
        {
          status: 403,
          headers: { "Content-Type": "application/json" },
        },
      );
    }

    // Create SSE stream
    const stream = new ReadableStream({
      start(controller) {
        // Add controller to active connections
        if (!activeConnections.has(userId)) {
          activeConnections.set(userId, new Set());
        }
        activeConnections.get(userId)!.add(controller);

        logger.info("Dashboard SSE connection opened", { userId });

        // Send initial connection event
        const connectionEvent: DashboardEvent = {
          type: "connection",
          data: { status: "connected", userId },
          timestamp: new Date().toISOString(),
        };
        controller.enqueue(
          new TextEncoder().encode(formatSSEMessage(connectionEvent)),
        );

        // Send initial data
        sendInitialData(userId, controller);

        // Set up heartbeat to keep connection alive
        const heartbeatInterval = setInterval(() => {
          try {
            const heartbeat: DashboardEvent = {
              type: "heartbeat",
              data: { time: Date.now() },
              timestamp: new Date().toISOString(),
            };
            controller.enqueue(
              new TextEncoder().encode(formatSSEMessage(heartbeat)),
            );
          } catch {
            clearInterval(heartbeatInterval);
          }
        }, 30000); // Every 30 seconds

        // Clean up on close
        request.signal.addEventListener("abort", () => {
          clearInterval(heartbeatInterval);
          activeConnections.get(userId)?.delete(controller);
          if (activeConnections.get(userId)?.size === 0) {
            activeConnections.delete(userId);
          }
          logger.info("Dashboard SSE connection closed", { userId });
        });
      },
      cancel() {
        // Clean up when stream is cancelled
        activeConnections.get(userId)?.forEach((c) => {
          try {
            c.close();
          } catch {
            // Already closed
          }
        });
        activeConnections.delete(userId);
      },
    });

    return new Response(stream, {
      headers: {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        Connection: "keep-alive",
        "X-Accel-Buffering": "no", // Disable Nginx buffering
      },
    });
  } catch (error) {
    logger.error("Dashboard SSE error", { error });
    return new Response(JSON.stringify({ error: "Internal server error" }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
}

/**
 * Send initial data to newly connected client
 */
async function sendInitialData(
  userId: string,
  controller: ReadableStreamDefaultController<Uint8Array>,
) {
  try {
    // Get unread notification count
    // Strangler fig: When DS_NOTIFICATIONS=convex, fetch unread count from Convex
    let unreadCount: number;
    try {
      const { convexQuery } = await import("@/lib/convex-client");
      const convexCount = await convexQuery(
        "notifications/queries:getUnreadCount",
        { userId },
      );
      unreadCount = typeof convexCount === "number" ? convexCount : 0;
    } catch {
      // Fall back to Prisma if Convex is unavailable
      unreadCount = await prisma.notifications.count({
        where: { userId, isRead: false },
      });
    }

    // Get today's analytics summary
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const todayAnalytics = await prisma.workflowAnalytics.findMany({
      where: {
        userId,
        date: { gte: today },
      },
    });

    const analyticsSummary = todayAnalytics.reduce(
      (acc, curr) => ({
        messages: acc.messages + curr.messagesReceived + curr.messagesSent,
        bookings: acc.bookings + curr.appointmentsBooked,
        leads: acc.leads + curr.leadsGenerated,
      }),
      { messages: 0, bookings: 0, leads: 0 },
    );

    // Get active conversations count
    const activeConversations = await prisma.conversationLog.count({
      where: { userId, status: "open" },
    });

    // Get workflow health status
    const workflows = await prisma.userWorkflow.findMany({
      where: { userId, status: "active" },
      select: { workflowType: true, healthStatus: true },
    });

    // Send initial summary
    const initialEvent: DashboardEvent = {
      type: "analytics_updated",
      data: {
        unreadNotifications: unreadCount,
        todaySummary: analyticsSummary,
        activeConversations,
        workflowHealth: workflows.reduce(
          (acc, w) => ({ ...acc, [w.workflowType]: w.healthStatus }),
          {},
        ),
      },
      timestamp: new Date().toISOString(),
    };

    controller.enqueue(
      new TextEncoder().encode(formatSSEMessage(initialEvent)),
    );
  } catch (error) {
    logger.error("Failed to send initial SSE data", { error, userId });
  }
}

// Utility function to emit events from webhook handlers
export async function notifyDashboard(
  userId: string,
  eventType: DashboardEventType,
  data: unknown,
) {
  const event: DashboardEvent = {
    type: eventType,
    data,
    timestamp: new Date().toISOString(),
  };
  emitDashboardEvent(userId, event);
}

// Only allow GET method (SSE is GET-based)
export async function POST() {
  return new Response(JSON.stringify({ error: "Method not allowed" }), {
    status: 405,
    headers: { "Content-Type": "application/json" },
  });
}
