const express = require('express');
const socketio = require('socket.io');
const {
    v4: uuidv4
} = require('uuid');
const fs = require('fs');

const app = express();
const PORT = process.env.PORT || 3000;

// Game state storage
const rooms = Array(10).fill().map((_, i) => ({
    id: `room${i}`,
    players: [],
    gameState: Array(6).fill().map(() => Array(7).fill(null)),
    currentPlayer: 0,
    moves: []
}));

const hallOfFame = JSON.parse(fs.readFileSync('halloffame.json', 'utf8') || '{}');

app.use(express.static('public'));
app.use(express.urlencoded({
    extended: true
}));

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/public/index.html');
});

app.get('/room:num', (req, res) => {
    res.sendFile(__dirname + '/public/room.html');
});

const server = app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

const io = socketio(server);

io.on('connection', (socket) => {
    let currentRoom = null;
    let playerName = null;

    socket.on('askRooms', () => {
        io.emit('updateRooms', rooms);
        io.emit('updateHOF', hallOfFame);
    });

    let stopGame = (room, roomId) => {
        console.log("Not enough players in room " + roomId + ", game stops.");
        room["gameState"] = Array(6).fill().map(() => Array(7).fill(null));
        room["currentPlayer"] = 0;
    };

    let startGame = (room, roomId) => {
        console.log("Two players on room " + roomId + ", game starting.");
        io.to(roomId).emit('startGame', {
            board: room.gameState,
            currentPlayer: room.currentPlayer
        });
    };

    let leaveRooms = () => {
        if (currentRoom) {
            let oldPlayercount = currentRoom.players.length;
            currentRoom.players = currentRoom.players.filter(p => p.id !== socket.id);
            let newPlayercount = currentRoom.players.length;

            if (oldPlayercount == 2 && newPlayercount < 2) {
                stopGame(currentRoom, currentRoom.id);
            }
            console.log("Player " + playerName + " leaved room " + currentRoom.id + ".");
            currentRoom = null;
            io.emit('updateRooms', rooms);
        }
    }

    socket.on('joinRoom', ({
        name,
        roomId
    }) => {
        leaveRooms();
        const room = rooms.find(r => r.id === roomId);
        if (!room || room.players.length >= 2) {
            socket.emit('error', 'Room is full or doesn\'t exist');
            return;
        }

        playerName = name;
        currentRoom = room;
        console.log("Player " + name + " joined room " + roomId + ".");
        room.players.push({
            id: socket.id,
            name
        });
        socket.join(roomId);
        io.emit('updateRooms', rooms);

        if (room.players.length === 2) {
            startGame(room, roomId);
        }
    });

    socket.on('move', (col) => {
        if (!currentRoom) return;

        const playerIndex = currentRoom.players.findIndex(p => p.id === socket.id);
        if (playerIndex !== currentRoom.currentPlayer) {
            console.log("Not " + playerName + "'s turn to play");
            return;
        };


        const row = findAvailableRow(currentRoom.gameState, col);
        if (row === -1) return;

        console.log("On room " + currentRoom.id + ", " + playerName + " played on column " + col + " at height" + row);

        currentRoom.gameState[row][col] = playerIndex;
        currentRoom.moves.push({
            player: playerIndex,
            col
        });

        const winner = checkWin(currentRoom.gameState, row, col);
        if (winner !== null) {
            console.log("In room "+currentRoom.id+", player "+playerName+" won"); 
            hallOfFame[playerName] = (hallOfFame[playerName] || 0) + 1;
            fs.writeFileSync('halloffame.json', JSON.stringify(hallOfFame));
            stopGame(currentRoom, currentRoom.id);
            socket.join(currentRoom.id);
            io.to(currentRoom.id).emit('stopGame', {
                board: currentRoom.gameState,
                winner: winner
            });
        }
        else {
            currentRoom.currentPlayer = 1 - currentRoom.currentPlayer;
            socket.join(currentRoom.id);
            io.to(currentRoom.id).emit('updateGame', {
                board: currentRoom.gameState,
                currentPlayer: currentRoom.currentPlayer
            });
        }
    });

    socket.on('chatMessage', (message) => {
        if (!currentRoom) {
            return;
        }
        io.to(currentRoom.id).emit('message', {
            name: playerName,
            message
        });
    });

    socket.on('disconnect', () => {
        leaveRooms();
    });
});

function findAvailableRow(board, col) {
    for (let row = 5; row >= 0; row--) {
        if (board[row][col] == null) return row;
    }
    return -1;
}


function checkWin(board, row, col) {
    const player = board[row][col];
    if (player === null) return null;

    const ROWS = board.length;
    const COLS = board[0].length;
    const WINNING_LENGTH = 4;

    const directions = [
        [0, 1],   // horizontal →
        [1, 0],   // vertical ↓
        [1, 1],   // diagonale descendante ↘
        [1, -1]   // diagonale montante ↗
    ];

    for (const [dx, dy] of directions) {
        let count = 1;

        
        for (let step = 1; step < WINNING_LENGTH; step++) {
            const newRow = row + step * dx;
            const newCol = col + step * dy;
            if (
                newRow >= 0 && newRow < ROWS &&
                newCol >= 0 && newCol < COLS &&
                board[newRow][newCol] === player
            ) {
                count++;
            } else {
                break;
            }
        }

        // Direction opposée 
        for (let step = 1; step < WINNING_LENGTH; step++) {
            const newRow = row - step * dx;
            const newCol = col - step * dy;
            if (
                newRow >= 0 && newRow < ROWS &&
                newCol >= 0 && newCol < COLS &&
                board[newRow][newCol] === player
            ) {
                count++;
            } else {
                break;
            }
        }

        if (count >= WINNING_LENGTH) {
            return player;
        }
    }

    return null;
}