import { Socket } from "socket.io";
import UserController from "../../../controllers/UserController";

type SecureSocket = Socket & {
  data: {
    user?: UserController;
    [key: string]: unknown;
  };
};

export const attachSocketEventPermissions = (
  socket: Socket,
  eventPermissions: Record<string, string[]>,
): boolean => {
  const user = (socket.data?.user as UserController | undefined) ?? undefined;
  if (!user) return false;

  socket.use(async (packet, packetNext) => {
    const eventName = packet[0];

    if (typeof eventName !== "string") return packetNext();

    const eventRequiredPermissions = eventPermissions[eventName] ?? [];
    if (eventRequiredPermissions.length === 0) return packetNext();

    const eventChecks = await Promise.all(
      eventRequiredPermissions.map((permission) =>
        user.hasPermission(permission),
      ),
    );

    if (eventChecks.includes(false)) {
      return packetNext(new Error("Forbidden: insufficient permissions"));
    }

    return packetNext();
  });

  return true;
};

export const socketAuthMiddleware = (permParams?: {
  permissions?: string[];
  eventPermissions?: Record<string, string[]>;
}) => {
  return async (socket: SecureSocket, next: (err?: Error) => void) => {
    const user = socket.data.user;
    if (!user) return next(new Error("Unauthorized"));

    const requiredPermissions = permParams?.permissions ?? [];

    if (requiredPermissions.length > 0) {
      const permissionChecks = await Promise.all(
        requiredPermissions.map((permission) => user.hasPermission(permission)),
      );

      if (permissionChecks.includes(false)) {
        return next(new Error("Forbidden: insufficient permissions"));
      }
    }

    const eventPermissions = permParams?.eventPermissions;

    if (eventPermissions) {
      const attached = attachSocketEventPermissions(socket, eventPermissions);
      if (!attached) return next(new Error("Unauthorized"));
    }

    return next();
  };
};
