import { WebSocketServer, WebSocket } from "ws";
import { Server } from "http";
import { IncomingMessage } from "http";

interface NotificationMessage {
  type: "shift_assigned" | "shift_swapped" | "delegation_created" | "delegation_approved" | "article_published" | "task_progress";
  data: any;
  timestamp: Date;
  recipientId?: number;
  broadcastToRole?: "admin" | "moderator" | "support_lead";
}

interface AdminConnection {
  ws: WebSocket;
  adminId: number;
  role: string;
  connectedAt: Date;
}

class AdminNotificationService {
  private wss: WebSocketServer | null = null;
  private connections: Map<number, AdminConnection[]> = new Map();
  private messageQueue: NotificationMessage[] = [];
  private maxQueueSize = 1000;

  /**
   * Initialize WebSocket server
   */
  initializeWebSocket(server: Server) {
    this.wss = new WebSocketServer({ server, path: "/api/admin-ws" });

    this.wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
      const adminId = this.extractAdminIdFromRequest(req);
      const role = this.extractRoleFromRequest(req);

      if (!adminId) {
        ws.close(1008, "Unauthorized");
        return;
      }

      this.registerConnection(adminId, role, ws);

      ws.on("message", (data: string) => {
        try {
          const message = JSON.parse(data);
          this.handleClientMessage(adminId, message);
        } catch (error) {
          console.error("WebSocket message parse error:", error);
        }
      });

      ws.on("close", () => {
        this.unregisterConnection(adminId, ws);
      });

      ws.on("error", (error) => {
        console.error("WebSocket error:", error);
      });

      // Send queued messages for this admin
      this.flushQueuedMessages(adminId);
    });

    console.log("[WebSocket] Admin notification service initialized");
  }

  /**
   * Register a new admin connection
   */
  private registerConnection(adminId: number, role: string, ws: WebSocket) {
    if (!this.connections.has(adminId)) {
      this.connections.set(adminId, []);
    }

    this.connections.get(adminId)!.push({
      ws,
      adminId,
      role,
      connectedAt: new Date(),
    });

    console.log(`[WebSocket] Admin ${adminId} connected (role: ${role})`);
  }

  /**
   * Unregister admin connection
   */
  private unregisterConnection(adminId: number, ws: WebSocket) {
    const connections = this.connections.get(adminId);
    if (connections) {
      const index = connections.findIndex((c) => c.ws === ws);
      if (index !== -1) {
        connections.splice(index, 1);
      }

      if (connections.length === 0) {
        this.connections.delete(adminId);
        console.log(`[WebSocket] Admin ${adminId} disconnected`);
      }
    }
  }

  /**
   * Handle messages from clients (e.g., acknowledgments, subscriptions)
   */
  private handleClientMessage(adminId: number, message: any) {
    if (message.type === "ping") {
      const connections = this.connections.get(adminId);
      if (connections && connections.length > 0) {
        connections[0].ws.send(JSON.stringify({ type: "pong" }));
      }
    }
  }

  /**
   * Send notification to specific admin
   */
  notifyAdmin(adminId: number, notification: NotificationMessage) {
    const connections = this.connections.get(adminId);

    if (connections && connections.length > 0) {
      const payload = JSON.stringify(notification);
      connections.forEach((conn) => {
        if (conn.ws.readyState === WebSocket.OPEN) {
          conn.ws.send(payload);
        }
      });
    } else {
      // Queue message if admin is offline
      this.queueMessage(notification);
    }
  }

  /**
   * Broadcast notification to all admins with specific role
   */
  broadcastToRole(role: string, notification: NotificationMessage) {
    const payload = JSON.stringify(notification);

    this.connections.forEach((connections) => {
      connections.forEach((conn) => {
        if (conn.role === role && conn.ws.readyState === WebSocket.OPEN) {
          conn.ws.send(payload);
        }
      });
    });
  }

  /**
   * Broadcast notification to all connected admins
   */
  broadcastToAll(notification: NotificationMessage) {
    const payload = JSON.stringify(notification);

    this.connections.forEach((connections) => {
      connections.forEach((conn) => {
        if (conn.ws.readyState === WebSocket.OPEN) {
          conn.ws.send(payload);
        }
      });
    });
  }

  /**
   * Queue message for offline admin
   */
  private queueMessage(notification: NotificationMessage) {
    if (this.messageQueue.length >= this.maxQueueSize) {
      this.messageQueue.shift(); // Remove oldest message
    }
    this.messageQueue.push(notification);
  }

  /**
   * Flush queued messages to admin
   */
  private flushQueuedMessages(adminId: number) {
    const connections = this.connections.get(adminId);
    if (!connections) return;

    const messagesToSend = this.messageQueue.filter(
      (m) => !m.recipientId || m.recipientId === adminId
    );

    messagesToSend.forEach((message) => {
      const payload = JSON.stringify(message);
      connections.forEach((conn) => {
        if (conn.ws.readyState === WebSocket.OPEN) {
          conn.ws.send(payload);
        }
      });
    });

    // Clear sent messages from queue
    this.messageQueue = this.messageQueue.filter(
      (m) => m.recipientId && m.recipientId !== adminId
    );
  }

  /**
   * Get active admin connections count
   */
  getActiveConnections(): number {
    let count = 0;
    this.connections.forEach((connections) => {
      count += connections.length;
    });
    return count;
  }

  /**
   * Extract admin ID from WebSocket request
   */
  private extractAdminIdFromRequest(req: IncomingMessage): number | null {
    const url = new URL(req.url || "", `http://${req.headers.host}`);
    const adminId = url.searchParams.get("adminId");
    return adminId ? parseInt(adminId, 10) : null;
  }

  /**
   * Extract role from WebSocket request
   */
  private extractRoleFromRequest(req: IncomingMessage): string {
    const url = new URL(req.url || "", `http://${req.headers.host}`);
    return url.searchParams.get("role") || "admin";
  }
}

// Export singleton instance
export const adminNotificationService = new AdminNotificationService();

/**
 * Notification helpers
 */
export const notifyShiftAssigned = (adminId: number, shiftData: any) => {
  adminNotificationService.notifyAdmin(adminId, {
    type: "shift_assigned",
    data: shiftData,
    timestamp: new Date(),
    recipientId: adminId,
  });
};

export const notifyShiftSwapped = (adminIds: number[], swapData: any) => {
  adminIds.forEach((adminId) => {
    adminNotificationService.notifyAdmin(adminId, {
      type: "shift_swapped",
      data: swapData,
      timestamp: new Date(),
      recipientId: adminId,
    });
  });
};

export const notifyDelegationCreated = (adminId: number, delegationData: any) => {
  adminNotificationService.notifyAdmin(adminId, {
    type: "delegation_created",
    data: delegationData,
    timestamp: new Date(),
    recipientId: adminId,
  });
};

export const notifyDelegationApproved = (adminId: number, delegationData: any) => {
  adminNotificationService.notifyAdmin(adminId, {
    type: "delegation_approved",
    data: delegationData,
    timestamp: new Date(),
    recipientId: adminId,
  });
};

export const notifyArticlePublished = (articleData: any) => {
  adminNotificationService.broadcastToRole("admin", {
    type: "article_published",
    data: articleData,
    timestamp: new Date(),
    broadcastToRole: "admin",
  });
};

export const notifyTaskProgress = (adminId: number, progressData: any) => {
  adminNotificationService.notifyAdmin(adminId, {
    type: "task_progress",
    data: progressData,
    timestamp: new Date(),
    recipientId: adminId,
  });
};
