import { Server } from "socket.io";
import jwt from "jsonwebtoken";

import { env } from "./env.js";
import { getCurrentDeviceStates } from "../services/trackingService.js";

const socketDebugLog = (...args) => {
  if (!env.trackingDebug) {
    return;
  }

  console.log("[tracking:socket]", ...args);
};

const LOCAL_HOST_PATTERNS = [
  /^localhost$/i,
  /^127(?:\.\d{1,3}){3}$/,
  /^::1$/,
  /^10(?:\.\d{1,3}){3}$/,
  /^192\.168(?:\.\d{1,3}){2}$/,
  /^172\.(1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2}$/,
];

const isAllowedSocketOrigin = (origin, requestHost) => {
  if (!origin) {
    return true;
  }

  if (env.clientUrls.includes(origin)) {
    return true;
  }

  try {
    const parsedOrigin = new URL(origin);

    if (requestHost && parsedOrigin.host === requestHost) {
      return true;
    }

    return (
      parsedOrigin.port === String(env.port) &&
      LOCAL_HOST_PATTERNS.some((pattern) => pattern.test(parsedOrigin.hostname))
    );
  } catch {
    return false;
  }
};

export const attachSocketServer = (httpServer) => {
  const io = new Server(httpServer, {
    allowRequest: (req, callback) => {
      const origin = req.headers.origin;
      const requestHost = req.headers.host;

      if (isAllowedSocketOrigin(origin, requestHost)) {
        callback(null, true);
        return;
      }

      callback("Origin not allowed by Socket.IO.", false);
    },
    cors: {
      origin: true,
      credentials: true,
      methods: ["GET", "POST", "PATCH", "DELETE"],
    },
  });

  io.on("connection", (socket) => {
    console.log(`Socket connected: ${socket.id}`);
    socketDebugLog("connection", {
      socketId: socket.id,
      transport: socket.conn.transport.name,
      address: socket.handshake.address,
      origin: socket.handshake.headers.origin || null,
    });

    socket.on("company:subscribe", (companyId) => {
      if (!companyId) {
        return;
      }

      socket.join(`company:${companyId}`);
      socketDebugLog("company:subscribe", {
        socketId: socket.id,
        companyId: Number(companyId),
      });
    });

    socket.on("company:unsubscribe", (companyId) => {
      if (!companyId) {
        return;
      }

      socket.leave(`company:${companyId}`);
      socketDebugLog("company:unsubscribe", {
        socketId: socket.id,
        companyId: Number(companyId),
      });
    });

    socket.on("admin:subscribe", () => {
      socket.join("admins");
    });

    socket.on("admin:unsubscribe", () => {
      socket.leave("admins");
    });

    socket.on("user:subscribe", (userId) => {
      if (!userId) {
        return;
      }

      socket.join(`user:${userId}`);
    });

    socket.on("user:unsubscribe", (userId) => {
      if (!userId) {
        return;
      }

      socket.leave(`user:${userId}`);
    });

    socket.on("join_room", ({ user_id: userId, role, company_id: companyId, depot_id: depotId } = {}) => {
      if (userId) {
        socket.join(`user:${userId}`);
      }

      if (role) {
        socket.join(`role:${role}`);
      }

      if (companyId) {
        socket.join(`company:${companyId}`);
      }

      if (depotId) {
        socket.join(`depot:${depotId}`);
      }

      socketDebugLog("join_room", {
        socketId: socket.id,
        userId: userId ? Number(userId) : null,
        role: role || null,
        companyId: companyId ? Number(companyId) : null,
        depotId: depotId ? Number(depotId) : null,
        rooms: Array.from(socket.rooms),
      });
    });

    socket.on("join_company", async ({ company_id: companyId, token }) => {
      if (!companyId || !token) {
        socketDebugLog("join_company:missing_payload", {
          socketId: socket.id,
          companyId,
          hasToken: Boolean(token),
        });
        return;
      }

      try {
        const decoded = jwt.verify(token, env.jwtSecret);
        const requestedCompanyId = Number(companyId);
        const allowedCompanies = Array.isArray(decoded.companyIds)
          ? decoded.companyIds.map(Number)
          : [Number(decoded.companyId)].filter(Boolean);

        if (!allowedCompanies.includes(requestedCompanyId) && Number(decoded.companyId) !== requestedCompanyId) {
          socketDebugLog("join_company:denied", {
            socketId: socket.id,
            requestedCompanyId,
            decodedCompanyId: Number(decoded.companyId),
            allowedCompanies,
          });
          socket.emit("tracking:error", { message: "Accès refusé à cette société." });
          return;
        }

        socket.join(`company:${requestedCompanyId}`);
        const devices = await getCurrentDeviceStates(requestedCompanyId);
        socketDebugLog("join_company:ok", {
          socketId: socket.id,
          requestedCompanyId,
          devicesCount: devices.length,
          rooms: Array.from(socket.rooms),
        });
        socket.emit("devices:initial_state", devices);
      } catch (error) {
        socketDebugLog("join_company:error", {
          socketId: socket.id,
          companyId: Number(companyId),
          message: error?.message || "unknown",
        });
        socket.emit("tracking:error", { message: "Jeton invalide." });
      }
    });

    socket.on("disconnect", () => {
      console.log(`Socket disconnected: ${socket.id}`);
      socketDebugLog("disconnect", {
        socketId: socket.id,
      });
    });
  });

  return io;
};
