import { describe, it, expect, beforeEach } from "bun:test";
import { RoomManager } from "./RoomManager";
import { gameRegistry } from "@shared/games/registry";
import { chessPlugin } from "@shared/games/chess/plugin";

// Register chess plugin for tests
if (!gameRegistry.get("chess")) {
    gameRegistry.register(chessPlugin);
}

describe("RoomManager", () => {
    let manager: RoomManager;

    beforeEach(() => {
        manager = new RoomManager();
    });

    describe("createRoom", () => {
        it("should create a room and return its id", () => {
            const result = manager.createRoom("chess", "player1");
            expect(result.ok).toBe(true);
            if (result.ok) {
                expect(result.roomId).toBeDefined();
                expect(typeof result.roomId).toBe("string");
            }
        });

        it("should reject unknown game type", () => {
            const result = manager.createRoom("unknown-game", "player1");
            expect(result.ok).toBe(false);
        });

        it("should add creator as first player", () => {
            const result = manager.createRoom("chess", "player1");
            if (result.ok) {
                const room = manager.getRoom(result.roomId);
                expect(room?.players).toContain("player1");
                expect(room?.host).toBe("player1");
                expect(room?.status).toBe("waiting");
            }
        });
    });

    describe("joinRoom", () => {
        it("should add a player to a waiting room", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            const join = manager.joinRoom(create.roomId, "player2", "player");
            expect(join.ok).toBe(true);
            if (join.ok) {
                expect(join.joinedAs).toBe("player");
            }
        });

        it("should auto-start when room reaches maxPlayers", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.joinRoom(create.roomId, "player2", "player");
            const room = manager.getRoom(create.roomId);
            expect(room?.status).toBe("playing");
            expect(room?.state).toBeDefined();
        });

        it("should allow joining as spectator when game is playing", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.joinRoom(create.roomId, "player2", "player");
            const spec = manager.joinRoom(create.roomId, "spectator1", "spectator");
            expect(spec.ok).toBe(true);
        });

        it("should downgrade to spectator when joining full room as player", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.joinRoom(create.roomId, "player2", "player");
            const result = manager.joinRoom(create.roomId, "player3", "player");
            expect(result.ok).toBe(true);
            if (result.ok) {
                expect(result.joinedAs).toBe("spectator");
            }
        });

        it("should reject joining nonexistent room", () => {
            const result = manager.joinRoom("fake-id", "player1", "player");
            expect(result.ok).toBe(false);
        });
    });

    describe("handleAction", () => {
        it("should apply a valid game action", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.joinRoom(create.roomId, "player2", "player");
            const result = manager.handleAction(create.roomId, "player1", { type: "move", from: "e2", to: "e4" });
            expect(result.ok).toBe(true);
        });

        it("should reject action from spectator", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.joinRoom(create.roomId, "player2", "player");
            manager.joinRoom(create.roomId, "spectator1", "spectator");
            const result = manager.handleAction(create.roomId, "spectator1", { type: "move", from: "e2", to: "e4" });
            expect(result.ok).toBe(false);
        });
    });

    describe("leaveRoom", () => {
        it("should remove a player from the room", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.leaveRoom(create.roomId, "player1");
            // Room is deleted when last player leaves a waiting room
            const room = manager.getRoom(create.roomId);
            expect(room).toBeUndefined();
        });

        it("should remove a spectator from the room", () => {
            const create = manager.createRoom("chess", "player1");
            if (!create.ok) throw new Error("Failed to create room");
            manager.joinRoom(create.roomId, "player2", "player");
            manager.joinRoom(create.roomId, "spec1", "spectator");
            manager.leaveRoom(create.roomId, "spec1");
            const room = manager.getRoom(create.roomId);
            expect(room?.spectators.has("spec1")).toBe(false);
        });
    });

    describe("listRooms", () => {
        it("should return summaries of all rooms", () => {
            manager.createRoom("chess", "player1");
            manager.createRoom("chess", "player2");
            const rooms = manager.listRooms();
            expect(rooms.length).toBe(2);
            expect(rooms[0].gameSlug).toBe("chess");
            expect(rooms[0].status).toBe("waiting");
        });

        it("should filter by game type", () => {
            manager.createRoom("chess", "player1");
            const rooms = manager.listRooms("chess");
            expect(rooms.length).toBe(1);
            const empty = manager.listRooms("blackjack");
            expect(empty.length).toBe(0);
        });
    });
});
