import type { Server as HttpServer } from "node:http";
import { instrument } from "@socket.io/admin-ui";
import { Server } from "socket.io";

import { createLogger } from "../logger/logger.controller.js";
import { auth } from "../utils/auth.utils.js";
import { allowedOrigins } from "../utils/cors.utils.js";
import { registerEmitter } from "./roomEmitter.socket.js";
import { registerHasRoomMembers } from "./socket.manager.js";
import { isRoomKey, roomConfigs } from "./socket.roomConfig.js";

const log = createLogger({ module: "socket.io", subModule: "setup" });

export const setupSocketIORoutes = (baseUrl: string, server: HttpServer) => {
	const io = new Server(server, {
		path: `${baseUrl}/api/socket.io`,
		cors: {
			origin: allowedOrigins,
			credentials: true,
		},
	});

	registerHasRoomMembers((roomId) => {
		return (io.sockets.adapter.rooms.get(roomId)?.size ?? 0) > 0;
	});

	registerEmitter((roomId, payloads) => {
		io.to(roomId).emit("room-update", {
			roomId,
			payloads,
		});
	});

	io.use(async (socket, next) => {
		try {
			//const cookieHeader = socket.handshake.headers.cookie;
			const headers = new Headers();

			for (const [key, value] of Object.entries(socket.request.headers)) {
				if (typeof value === "string") {
					headers.set(key, value);
				} else if (Array.isArray(value)) {
					headers.set(key, value.join(", "));
				}
			}

			const session = await auth.api.getSession({
				headers,
			});

			if (!session) {
				return next(); // allow connection, but no auth
			}

			if (session) {
				socket.user = session.user;
				socket.session = session as any;
			}

			next();
		} catch (err) {
			next();
		}
	});

	io.on("connection", (s) => {
		log.info({}, `User connected: ${s.id}`);

		s.emit("welcome", {
			serverTime: Date.now(),
			availableRooms: ["logs", "labels"],
			version: "1.0.0",
		});

		s.on("join-room", async ({ room, params }) => {
			if (!isRoomKey(room)) return;

			const config = roomConfigs[room];

			if (!config) {
				return s.emit("room-error", {
					roomId: room,
					message: `Unknown room: ${room}`,
				});
			}

			const actualRoom = config.buildRoom
				? config.buildRoom(params)
				: (room as any);

			const allowed = config.canJoin
				? await config.canJoin({
						socket: s,
						user: s.user,
						room,
						actualRoom,
						params,
					})
				: true;

			if (!allowed) {
				return s.emit("room-error", {
					roomId: room,
					message: `Not authorized to be in room: ${room}`,
				});
			}

			await s.join(actualRoom);

			s.emit("room-joined", {
				room,
				roomId: actualRoom,
				params,
			});

			if (config.seed) {
				const payloads = await config.seed({
					room,
					actualRoom,
					params,
					user: s.user,
				});

				s.emit("room-update", {
					room,
					roomId: actualRoom,
					type: "snapshot",
					payloads,
				});
			}

			log.info(
				{ room, actualRoom, params },
				`User joined ${actualRoom}: ${s.id}`,
			);
		});
		// s.on("leave-room", (room) => {
		// 	s.leave(room);
		// 	log.info({}, `${s.id} left room: ${JSON.stringify(room)}`);
		// });
		s.on("leave-room", async ({ room, params }) => {
			if (!isRoomKey(room)) return;

			const config = roomConfigs[room];

			if (!config) return;

			const actualRoom = config.buildRoom
				? config.buildRoom(params)
				: (room as any);

			await s.leave(actualRoom);

			s.emit("room-left", {
				room,
				roomId: actualRoom,
				params,
			});

			log.info(
				{ room, actualRoom, params },
				`${s.id} left room: ${actualRoom}`,
			);
		});
	});

	io.on("disconnect", (s) => {
		log.info({}, "User disconnected:", s.id);
	});

	// admin stuff

	const admin = io.of("/admin");
	admin.on("connection", (s) => {
		log.info({}, `User connected: ${s.id}`);
	});

	admin.on("disconnect", (s) => {
		log.info({}, "User disconnected:", s.id);
	});

	instrument(io, {
		auth: false,
		//namespaceName: "/admin",
	});
};
