// /server/game/GameManager.js
const { v4: uuidv4 } = require('uuid');
const GameInstance = require('./instance/GameInstance');
const dataUtils = require('../data/dataUtils');
const GAME_CONFIG = require('../core/config');

class GameManager {
    constructor(io) {
        this.io = io;
        this.games = {};
        this.userIdentifierToGameId = {};
        this.pendingPvPGames = [];
        console.log("[GameManager] Initialized.");
    }

    _removePreviousPendingGames(currentSocketId, identifier, excludeGameId = null) {
        const oldPendingGameId = this.userIdentifierToGameId[identifier];
        if (oldPendingGameId && oldPendingGameId !== excludeGameId && this.games[oldPendingGameId]) {
            const gameToRemove = this.games[oldPendingGameId];
            if (gameToRemove.mode === 'pvp' && gameToRemove.playerCount === 1 && this.pendingPvPGames.includes(oldPendingGameId)) {
                const oldOwnerInfo = Object.values(gameToRemove.players).find(p => p.id === GAME_CONFIG.PLAYER_ID);
                if (oldOwnerInfo && (oldOwnerInfo.identifier === identifier)) {
                    console.log(`[GameManager] Пользователь ${identifier} (сокет: ${currentSocketId}) создал/присоединился к новой игре. Удаляем его предыдущую ожидающую игру: ${oldPendingGameId}`);
                    this._cleanupGame(oldPendingGameId, 'replaced_by_new_game');
                }
            }
        }
    }

    createGame(socket, mode = 'ai', chosenCharacterKey = 'elena', identifier) {
        this._removePreviousPendingGames(socket.id, identifier);
        if (this.userIdentifierToGameId[identifier] && this.games[this.userIdentifierToGameId[identifier]]) {
            socket.emit('gameError', { message: 'Вы уже находитесь в активной или ожидающей игре.' });
            this.handleRequestGameState(socket, identifier);
            return;
        }

        const gameId = uuidv4();
        const game = new GameInstance(gameId, this.io, mode, this);
        game.ownerIdentifier = identifier; // Устанавливаем владельца игры
        this.games[gameId] = game;
        const charKeyForInstance = (mode === 'pvp') ? chosenCharacterKey : 'elena';

        if (game.addPlayer(socket, charKeyForInstance, identifier)) {
            this.userIdentifierToGameId[identifier] = gameId;
            console.log(`[GameManager] Игра создана: ${gameId} (режим: ${mode}) игроком ${identifier} (выбран: ${charKeyForInstance})`);
            const assignedPlayerId = game.players[socket.id]?.id;

            if (!assignedPlayerId) {
                this._cleanupGame(gameId, 'player_add_failed_no_role');
                socket.emit('gameError', { message: 'Ошибка сервера при создании игры (роль).' });
                return;
            }
            socket.emit('gameCreated', { gameId: gameId, mode: mode, yourPlayerId: assignedPlayerId });

            if ((game.mode === 'ai' && game.playerCount === 1) || (game.mode === 'pvp' && game.playerCount === 2)) {
                const isInitialized = game.initializeGame();
                if (isInitialized) game.startGame();
                else this._cleanupGame(gameId, 'initialization_failed_on_create');

                if (game.mode === 'pvp' && game.playerCount === 2) { // Если PvP заполнилась
                    const idx = this.pendingPvPGames.indexOf(gameId);
                    if (idx > -1) this.pendingPvPGames.splice(idx, 1);
                    this.broadcastAvailablePvPGames();
                }
            } else if (mode === 'pvp' && game.playerCount === 1) {
                if (!this.pendingPvPGames.includes(gameId)) this.pendingPvPGames.push(gameId);
                game.initializeGame();
                socket.emit('waitingForOpponent');
                this.broadcastAvailablePvPGames();
            }
        } else {
            this._cleanupGame(gameId, 'player_add_failed_instance');
        }
    }

    joinGame(socket, gameId, identifier) { // identifier - это userId присоединяющегося
        const game = this.games[gameId];
        if (!game) { socket.emit('gameError', { message: 'Игра с таким ID не найдена.' }); return; }
        if (game.mode !== 'pvp') { socket.emit('gameError', { message: 'К этой игре нельзя присоединиться как к PvP.' }); return; }
        if (game.playerCount >= 2) { socket.emit('gameError', { message: 'Эта PvP игра уже заполнена.' }); return; }

        // === ИЗМЕНЕНИЕ: Запрет присоединения к своей же игре ===
        if (game.ownerIdentifier === identifier) {
            socket.emit('gameError', { message: 'Вы не можете присоединиться к игре, которую сами создали и ожидаете.' });
            // Можно отправить состояние этой игры, если она действительно ожидает
            this.handleRequestGameState(socket, identifier);
            return;
        }
        // === КОНЕЦ ИЗМЕНЕНИЯ ===

        if (this.userIdentifierToGameId[identifier] && this.userIdentifierToGameId[identifier] !== gameId) {
            socket.emit('gameError', { message: 'Вы уже находитесь в другой активной игре.' });
            this.handleRequestGameState(socket, identifier);
            return;
        }
        // Проверка на случай, если игрок пытается присоединиться к игре, где он уже есть (хотя ownerIdentifier проверка выше это частично покрывает для создателя)
        const existingPlayerInThisGame = Object.values(game.players).find(p => p.identifier === identifier);
        if (existingPlayerInThisGame) {
            socket.emit('gameError', { message: 'Вы уже находитесь в этой игре.' });
            this.handleRequestGameState(socket, identifier); // Отправляем состояние игры
            return;
        }


        this._removePreviousPendingGames(socket.id, identifier, gameId);

        if (game.addPlayer(socket, null, identifier)) {
            this.userIdentifierToGameId[identifier] = gameId;
            console.log(`[GameManager] Игрок ${identifier} присоединился к PvP игре ${gameId}`);

            if (game.mode === 'pvp' && game.playerCount === 2) {
                const isInitialized = game.initializeGame();
                if (isInitialized) game.startGame();
                else this._cleanupGame(gameId, 'initialization_failed_on_join');

                const idx = this.pendingPvPGames.indexOf(gameId);
                if (idx > -1) this.pendingPvPGames.splice(idx, 1);
                this.broadcastAvailablePvPGames();
            }
        }
    }

    findAndJoinRandomPvPGame(socket, chosenCharacterKeyForCreation = 'elena', identifier) {
        this._removePreviousPendingGames(socket.id, identifier);
        if (this.userIdentifierToGameId[identifier] && this.games[this.userIdentifierToGameId[identifier]]) {
            socket.emit('gameError', { message: 'Вы уже находитесь в активной или ожидающей игре.' });
            this.handleRequestGameState(socket, identifier);
            return;
        }

        let gameIdToJoin = null;
        const preferredOpponentKey = chosenCharacterKeyForCreation === 'elena' ? 'almagest' : 'elena';

        // Ищем игру, созданную НЕ текущим пользователем
        for (const id of [...this.pendingPvPGames]) {
            const pendingGame = this.games[id];
            // === ИЗМЕНЕНИЕ: Убеждаемся, что не присоединяемся к игре, которую сами создали и ожидаем ===
            if (pendingGame && pendingGame.mode === 'pvp' && pendingGame.playerCount === 1 && pendingGame.ownerIdentifier !== identifier) {
                // === КОНЕЦ ИЗМЕНЕНИЯ ===
                const firstPlayerInfo = Object.values(pendingGame.players).find(p => p.id === GAME_CONFIG.PLAYER_ID);
                if (firstPlayerInfo && firstPlayerInfo.chosenCharacterKey === preferredOpponentKey) {
                    gameIdToJoin = id; break;
                }
                if (!gameIdToJoin) gameIdToJoin = id; // Берем первую подходящую, если нет с нужным персонажем
            }
        }

        if (gameIdToJoin) {
            this.joinGame(socket, gameIdToJoin, identifier);
        } else {
            this.createGame(socket, 'pvp', chosenCharacterKeyForCreation, identifier);
            // Сообщение о создании новой игры отправляется из createGame/initializeGame/startGame
        }
    }

    handlePlayerAction(identifier, actionData) {
        const gameId = this.userIdentifierToGameId[identifier];
        const game = this.games[gameId];
        if (game) {
            const playerInfo = Object.values(game.players).find(p => p.identifier === identifier);
            const currentSocketId = playerInfo?.socket?.id;
            if (playerInfo && currentSocketId) {
                const actualSocket = this.io.sockets.sockets.get(currentSocketId);
                if (actualSocket?.connected) game.processPlayerAction(currentSocketId, actionData);
                else console.warn(`[GameManager] Игрок ${identifier}: действие, но сокет ${currentSocketId} отключен.`);
            } else {
                console.warn(`[GameManager] Игрок ${identifier}: действие для игры ${gameId}, но не найден в game.players.`);
                delete this.userIdentifierToGameId[identifier];
                const s = this.io.sockets.sockets.get(identifier) || playerInfo?.socket;
                if (s) s.emit('gameNotFound', { message: 'Ваша игровая сессия потеряна (ошибка игрока).' });
            }
        } else {
            console.warn(`[GameManager] Игрок ${identifier}: действие, но игра ${gameId} не найдена.`);
            delete this.userIdentifierToGameId[identifier];
            const s = this.io.sockets.sockets.get(identifier);
            if (s) s.emit('gameNotFound', { message: 'Ваша игровая сессия не найдена.' });
        }
    }

    handleDisconnect(socketId, identifier) {
        const gameId = this.userIdentifierToGameId[identifier];
        const game = this.games[gameId];

        if (game) {
            // Ищем игрока по ИДЕНТИФИКАТОРУ, так как сокет мог уже обновиться при переподключении
            const playerInfo = Object.values(game.players).find(p => p.identifier === identifier);

            if (playerInfo) {
                // Проверяем, действительно ли отключается АКТУАЛЬНЫЙ сокет этого игрока
                if (playerInfo.socket.id === socketId) {
                    console.log(`[GameManager] Актуальный сокет ${socketId} игрока ${identifier} отключился из игры ${gameId}.`);
                    const dPlayerRole = playerInfo.id;
                    const dCharKey = playerInfo.chosenCharacterKey;

                    game.removePlayer(socketId); // Удаляем именно этот сокет из игры

                    if (game.playerCount === 0) {
                        this._cleanupGame(gameId, 'all_players_disconnected');
                    } else if (game.mode === 'pvp' && game.playerCount === 1 && game.gameState && !game.gameState.isGameOver) {
                        game.endGameDueToDisconnect(socketId, dPlayerRole, dCharKey);
                    } else if (game.mode === 'ai' && game.playerCount === 0 && game.gameState && !game.gameState.isGameOver) {
                        game.endGameDueToDisconnect(socketId, dPlayerRole, dCharKey); // Завершаем AI игру, если игрок ушел
                    }
                    // Если игра уже была isGameOver, _cleanupGame был вызван ранее.
                    // userIdentifierToGameId[identifier] для отключившегося игрока УДАЛЯЕТСЯ здесь,
                    // чтобы он мог начать новую игру или переподключиться.
                    delete this.userIdentifierToGameId[identifier];
                } else {
                    // Отключился старый сокет (socketId), но у игрока (identifier) уже новый активный сокет.
                    // Ничего не делаем с игрой, так как игрок по-прежнему в ней с новым сокетом.
                    // Просто логируем, что старый сокет отвалился.
                    console.log(`[GameManager] Отключился старый сокет ${socketId} для игрока ${identifier}, который уже переподключился с сокетом ${playerInfo.socket.id} в игре ${gameId}.`);
                    // Связь userIdentifierToGameId[identifier] остается, так как он все еще в игре.
                }
            } else {
                // Игрока с таким identifier нет в этой игре.
                // Это может случиться, если игра была очищена до того, как пришло событие disconnect.
                // console.log(`[GameManager] Отключившийся сокет ${socketId} (identifier: ${identifier}) не найден в активных игроках игры ${gameId} (возможно, игра уже очищена).`);
                delete this.userIdentifierToGameId[identifier]; // На всякий случай.
            }
        } else {
            // console.log(`[GameManager] Отключился сокет ${socketId} (identifier: ${identifier}). Активная игра не найдена по идентификатору.`);
            delete this.userIdentifierToGameId[identifier];
        }
    }

    _cleanupGame(gameId, reason = 'unknown') {
        const game = this.games[gameId];
        if (!game) return false;
        console.log(`[GameManager] Очистка игры ${gameId} (Причина: ${reason}).`);

        if (typeof game.turnTimer?.clear === 'function') game.turnTimer.clear();

        Object.values(game.players).forEach(pInfo => {
            if (pInfo?.identifier && this.userIdentifierToGameId[pInfo.identifier] === gameId) {
                delete this.userIdentifierToGameId[pInfo.identifier];
            }
        });
        if(game.ownerIdentifier && this.userIdentifierToGameId[game.ownerIdentifier] === gameId){
            delete this.userIdentifierToGameId[game.ownerIdentifier];
        }

        const pendingIdx = this.pendingPvPGames.indexOf(gameId);
        if (pendingIdx > -1) this.pendingPvPGames.splice(pendingIdx, 1);

        delete this.games[gameId];
        this.broadcastAvailablePvPGames();
        return true;
    }

    getAvailablePvPGamesListForClient() {
        return this.pendingPvPGames.map(gameId => {
            const game = this.games[gameId];
            if (game && game.mode === 'pvp' && game.playerCount === 1 && game.gameState && !game.gameState.isGameOver) {
                const p1Info = Object.values(game.players).find(p => p.id === GAME_CONFIG.PLAYER_ID);
                let p1Username = 'Игрок';
                let p1CharName = '';
                let ownerId = game.ownerIdentifier; // === ИЗМЕНЕНИЕ: Получаем ownerId ===

                if (p1Info) {
                    p1Username = p1Info.socket?.userData?.username || `User#${String(p1Info.identifier).substring(0,4)}`;
                    const charData = dataUtils.getCharacterBaseStats(p1Info.chosenCharacterKey);
                    p1CharName = charData?.name || p1Info.chosenCharacterKey;
                }
                return {
                    id: gameId,
                    status: `Ожидает (Создал: ${p1Username} за ${p1CharName})`,
                    ownerIdentifier: ownerId // === ИЗМЕНЕНИЕ: Отправляем ownerIdentifier клиенту ===
                };
            }
            return null;
        }).filter(info => info !== null);
    }

    broadcastAvailablePvPGames() {
        this.io.emit('availablePvPGamesList', this.getAvailablePvPGamesListForClient());
    }

    handleRequestGameState(socket, identifier) {
        const gameId = this.userIdentifierToGameId[identifier];
        const game = gameId ? this.games[gameId] : null;

        if (game) {
            const playerInfoInGameInstance = Object.values(game.players).find(p => p.identifier === identifier);
            if (playerInfoInGameInstance) {
                if (game.gameState?.isGameOver) {
                    delete this.userIdentifierToGameId[identifier];
                    socket.emit('gameNotFound', { message: 'Ваша предыдущая игра уже завершена.' });
                    return;
                }
                console.log(`[GameManager] Восстановление игры ${gameId} для ${identifier}. Новый сокет ${socket.id}.`);
                const oldSocketId = playerInfoInGameInstance.socket?.id; // Добавил ?. на случай если сокета нет
                if (oldSocketId && oldSocketId !== socket.id && game.players[oldSocketId]) {
                    delete game.players[oldSocketId];
                    if(game.playerSockets[playerInfoInGameInstance.id]?.id === oldSocketId) {
                        delete game.playerSockets[playerInfoInGameInstance.id];
                    }
                }
                playerInfoInGameInstance.socket = socket;
                game.players[socket.id] = playerInfoInGameInstance;
                game.playerSockets[playerInfoInGameInstance.id] = socket;
                socket.join(game.id);

                const pCharKey = playerInfoInGameInstance.chosenCharacterKey;
                const pData = dataUtils.getCharacterData(pCharKey);
                const opponentRole = playerInfoInGameInstance.id === GAME_CONFIG.PLAYER_ID ? GAME_CONFIG.OPPONENT_ID : GAME_CONFIG.PLAYER_ID;
                const oCharKey = game.gameState?.[opponentRole]?.characterKey || (playerInfoInGameInstance.id === GAME_CONFIG.PLAYER_ID ? game.opponentCharacterKey : game.playerCharacterKey);
                const oData = oCharKey ? dataUtils.getCharacterData(oCharKey) : null; // oData может быть null, если оппонента нет

                if (pData && (oData || !game.opponentCharacterKey) && game.gameState) {
                    socket.emit('gameStarted', {
                        gameId: game.id, yourPlayerId: playerInfoInGameInstance.id, initialGameState: game.gameState,
                        playerBaseStats: pData.baseStats,
                        opponentBaseStats: oData?.baseStats || dataUtils.getCharacterBaseStats(null) || {name: 'Ожидание...', maxHp:1}, // Заглушка если оппонента нет
                        playerAbilities: pData.abilities,
                        opponentAbilities: oData?.abilities || [],
                        log: game.consumeLogBuffer(), clientConfig: { ...GAME_CONFIG }
                    });
                    if(game.mode === 'pvp' && game.playerCount === 1 && game.ownerIdentifier === identifier) socket.emit('waitingForOpponent');
                    if (!game.gameState.isGameOver && game.turnTimer?.start) {
                        game.turnTimer.start(game.gameState.isPlayerTurn, (game.mode === 'ai' && !game.gameState.isPlayerTurn));
                    }
                } else {
                    this._handleGameRecoveryError(socket, gameId, identifier, 'data_load_fail_reconnect');
                }
            } else {
                this._handleGameRecoveryError(socket, gameId, identifier, 'player_not_in_instance_reconnect');
            }
        } else {
            socket.emit('gameNotFound', { message: 'Активная игровая сессия не найдена.' });
        }
    }

    _handleGameRecoveryError(socket, gameId, identifier, reasonCode) {
        console.error(`[GameManager] Ошибка восстановления игры ${gameId} для ${identifier} (причина: ${reasonCode}).`);
        socket.emit('gameError', { message: 'Ошибка сервера при восстановлении состояния игры.' });
        this._cleanupGame(gameId, `recovery_error_${reasonCode}`);
        socket.emit('gameNotFound', { message: 'Ваша игровая сессия была завершена из-за ошибки.' });
    }
}

module.exports = GameManager;