

import API from "../../api.js";
import TableBasePage from "../base_table.js";
import DOM from "../../dom.js";
import Events from "../../lib/events.js";
import OverlayConfirm from "../../components/common/temporary/overlay_confirm.js";

export default class PageMtgGame extends TableBasePage {
    static hash = hashPageMtgGame;
    static attrIdRowObject = attrGameId;
    callSaveTableContent = API.saveGame;

    constructor(router) {
        super(router);
    }

    initialize() {
        this.sharedInitialize();
        this.hookupTcgGame();
    }
    hookupFilters() {
        // this.sharedHookupFilters();
    }
    callFilterTableContent() {
        window.location.reload();
    }
    loadRowTable(rowJson) {
        return;
    }
    getJsonRow(row) {
        return;
    }
    initialiseRowNew(tbody, row) {
        
    }
    postInitialiseRowNewCallback(tbody) {
        
    }

    hookupTableMain() {
        super.hookupTableMain();
    }
    hookupTcgGame() {
        this.initGamePage();
        let pageHeading = document.querySelector('.container.company-name .tcg-title.company-name');
        pageHeading.innerText = `MTG Game #${gameId}`;
    }

    initGamePage() {
        // Load existing game state from API or show setup
        PageMtgGame.updatePlayerSetup();
        if (typeof gameId !== 'undefined' && gameId) {
            this.loadGameFromServer();
        }
        /*
        else {
            PageMtgGame.updatePlayerSetup();
        }
        */
        
        PageMtgGame.hookupResetButton();
        PageMtgGame.hookupPlayerCountInput();
        this.hookupStartGameButton();
        /*
        this.hookupCommanderDeathIncrementButtons();
        this.hookupEliminateCommanderButtons();
        this.hookupPlayerLifeIncrementButtons();
        this.hookupCommanderDamageIncrementButtons();
        */
        PageMtgGame.hookupOverlayGameOutdated();
        PageMtgGame.hookupGameUpdateCheck();
    }
    static hookupResetButton() {
        const resetGameButton = document.querySelector('header.game-header .header-right .btn-tcg.btn-tcg-secondary');
        if (resetGameButton) {
            resetGameButton.addEventListener('click', PageMtgGame.resetGame);
        }
    }
    static hookupPlayerCountInput() {
        const playerCountInput = document.getElementById('playerCount');
        if (playerCountInput) {
            playerCountInput.addEventListener('change', PageMtgGame.updatePlayerSetup);
        }
    }
    hookupStartGameButton() {
        const startGameButton = document.querySelector('.setup-section .setup-actions .btn-tcg');
        if (startGameButton) {
            startGameButton.addEventListener('click', () => { this.startGame(); });
        }
    }
    /*
    hookupCommanderDeathIncrementButtons() {
        const commanderDeathIncremementButtons = document.querySelectorAll('#players-grid .player-card .commander-deaths .death-btn');
        if (commanderDeathIncremementButtons) {
            commanderDeathIncremementButtons.forEach((button) => {
                button.addEventListener('click', PageMtgGame.changeCommanderDeaths);
            });
        }
    }
    hookupEliminateCommanderButtons() {
        const eliminateCommanderButtons = document.querySelector('#players-grid .player-card .eliminate-btn');
        if (eliminateCommanderButtons) {
            eliminateCommanderButtons.forEach((button) => {
                button.addEventListener('click', PageMtgGame.toggleEliminate);
            });
        }
    }
    hookupPlayerLifeIncrementButtons() {
        const playerLifeIncrementButtons = document.querySelector('#players-grid .player-card .eliminate-btn');
        if (playerLifeIncrementButtons) {
            playerLifeIncrementButtons.forEach((button) => {
                button.addEventListener('click', PageMtgGame.changeLife);
            });
        }
    }
    hookupCommanderDamageIncrementButtons() {
        const commanderDamageIncrementButtons = document.querySelector('#players-grid .player-card .eliminate-btn');
        if (commanderDamageIncrementButtons) {
            commanderDamageIncrementButtons.forEach((button) => {
                button.addEventListener('click', PageMtgGame.changeCommanderDamage);
            });
        }
    }
    */
    static hookupGameUpdateCheck() {
        const intervalId = setInterval(async () => {
            const isGameDataOutdated = await PageMtgGame.checkLastGameUpdate();
            if (isGameDataOutdated) {
                clearInterval(intervalId);
            }
        }, 30000);
    }
    static async checkLastGameUpdate() {
        try {
            // Fetch players, rounds, and damage records from API
            const isGameDataOutdatedResponse = await API.getIsGameDataOutdated(game);
            const isGameDataOutdated = isGameDataOutdatedResponse[flagData];
            console.log({ isGameDataOutdatedResponse, isGameDataOutdated });

            if (isGameDataOutdated) {
                // const gameDataOutdatedLabel = document.querySelector(gameDataOutdatedLabelId);
                // gameDataOutdatedLabel.classList.remove(flagIsCollapsed);
                const pageTitleElement = document.querySelector('.topnav .tcg-title');
                pageTitleElement.classList.add(flagDirty);
                PageMtgGame.showOverlayGameOutdated();
            }

            return isGameDataOutdated;
        } catch (error) {
            console.error('Error checking for game update from server:', error);
        }
    }

    static hookupOverlayGameOutdated() {
        Events.initialiseEventHandler(idOverlayGameOutdated + ' button.' + flagCancel, flagInitialised, (buttonCancel) => {
            buttonCancel.addEventListener('click', () => {
                let overlay = document.querySelector(idOverlayGameOutdated);
                overlay.style.visibility = 'hidden';
            });
        });
        Events.initialiseEventHandler(idOverlayGameOutdated + ' button.' + flagSubmit, flagInitialised, (buttonConfirm) => {
            buttonConfirm.addEventListener('click', () => {
                let overlay = document.querySelector(idOverlayGameOutdated);
                overlay.style.visibility = 'hidden';
                window.location.reload();
            });
        });
    }
    static showOverlayGameOutdated() {
        let overlay = document.querySelector(idOverlayGameOutdated);
        overlay.classList.remove(flagIsCollapsed);
        overlay.style.visibility = 'visible';
    }

    async loadGameFromServer() {
        console.log("loading game from server");
        try {
            // Fetch players, rounds, and damage records from API
            const [playersResponse, roundsResponse, damageResponse] = await Promise.all([
                  API.getGamePlayers(gameId)
                , API.getGameRounds(gameId)
                , API.getGameDamageRecords(gameId)
            ]);
            console.log({ playersResponse, damageResponse });

            let setupSection = document.getElementById('setupSection');
            let gameSection = document.getElementById('gameSection');
            setupSection.classList.remove('hidden');
            gameSection.classList.add('hidden');

            if (playersResponse.status !== 'success') {
                console.error('Failed to load players:', playersResponse.message);
                return;
            }

            const savedPlayers = playersResponse.data || [];
            const savedRounds = roundsResponse.status === 'success' ? (roundsResponse.data || []) : [];
            const savedDamageRecords = damageResponse.status === 'success' ? (damageResponse.data || []) : [];
            
            players = savedPlayers;
            rounds = savedRounds;
            damageRecords = savedDamageRecords;

            if (savedPlayers.length === 0) {
                // No players yet, show setup section
                return;
            }

            // Hide setup, show game
            setupSection.classList.add('hidden');
            gameSection.classList.remove('hidden');

            console.log({ savedPlayers, damageRecords });

            // Render players to DOM
            const latestRoundId = PageMtgGame.getLatestRoundId();
            const latestRound = rounds.filter(round => round[attrRoundId] == latestRoundId)[0];
            const roundDisplayOrderLabel = PageMtgGame.getRoundDisplayOrderLabel();
            DOM.setElementValuesCurrentAndPrevious(roundDisplayOrderLabel, latestRound[flagDisplayOrder]);
            
            this.renderPlayers();

        } catch (error) {
            console.error('Error loading game from server:', error);
        }
    }
    static getRoundDisplayOrderLabel() {
        return document.querySelector([
              '#gameSection'
            , ' > .'
            , flagRow
            , '.'
            , flagRound
            , ' > .'
            , flagRow
            , '.'
            , flagRound
            , ' > .'
            , flagRound
            , '.'
            , flagDisplayOrder
            , ' > span.'
            , flagRound
            , '.'
            , flagDisplayOrder
        ].join(''));
    }

    renderPlayers() {
        const grid = document.getElementById('playersGrid');
        grid.innerHTML = '';
        
        // let activeRoundId = PageMtgGame.getActiveRoundId();
        const roundDisplayOrderLabel = PageMtgGame.getRoundDisplayOrderLabel();
        const currentRoundDisplayOrder = parseInt(DOM.getElementValueCurrent(roundDisplayOrderLabel));
        let activeRound = rounds.filter(round => round[flagDisplayOrder] == currentRoundDisplayOrder)[0];
        if (activeRound == null) {
            activeRound = PageMtgGame.makeDefaultGameRound(currentRoundDisplayOrder);
            rounds.push(activeRound);
        }
        DOM.setElementValueCurrent(roundDisplayOrderLabel, activeRound[flagDisplayOrder]);
        
        const previousRoundIds = rounds.filter(round => round[flagDisplayOrder] <= currentRoundDisplayOrder)
            .map(round => round[attrRoundId]);
        players.forEach((player, index) => {
            // Build display name: prefer user_name + deck_name, fallback to player name
            const playerId = player[attrPlayerId];
            let displayName = PageMtgGame.makePlayerDisplayName(playerId, index);
            let damagePlayerPairs = [...players, { [attrPlayerId]: null }];
            let maxCommanderDamageReceived = 0;
            damagePlayerPairs.forEach(damagePlayerPair => {
                const sourceId = damagePlayerPair[attrPlayerId];
                const filteredPlayerDamages = damageRecords.filter(damage => (
                        damage[attrRoundId] == activeRound[attrRoundId]
                    //    previousRoundIds.includes(damage[attrRoundId])
                    &&  damage[attrPlayerId] == playerId
                    &&  damage[attrReceivedFromCommanderPlayerId] == sourceId
                )); //[playerId] || {};
                if (filteredPlayerDamages.length == 0) {
                    damageRecords.push(PageMtgGame.makeDefaultGameRoundPlayerDamage(playerId, sourceId));
                }
                maxCommanderDamageReceived = Math.max(
                      maxCommanderDamageReceived
                    , damageRecords.filter(damage => (
                            damage[attrPlayerId] == playerId
                        &&  damage[attrReceivedFromCommanderPlayerId] == sourceId
                        &&  damage[attrReceivedFromCommanderPlayerId] != null
                        &&  previousRoundIds.includes(damage[attrRoundId])
                    ))
                        .map(damage => damage[flagLifeLoss])
                        .reduce((a, b) => a + b, 0)
                );
            });

            const totalDamage = damageRecords.filter(damage => (
                    damage[attrPlayerId] == playerId
                &&  previousRoundIds.includes(damage[attrRoundId])
            ))
                .map(damage => damage[flagLifeLoss] - damage[flagLifeGain])
                .reduce((a, b) => a + b, 0);
            let life = game[flagStartingLife] - totalDamage;

            let isEliminatedByForce = damageRecords.filter(damage => (
                    damage[attrPlayerId] == playerId
                &&  previousRoundIds.includes(damage[attrRoundId])
            ))
                .map(damage => damage[flagIsEliminated])
                .some(Boolean);
            console.log("renderPlayers");
            console.log({isEliminatedByForce, player, life, maxCommanderDamageReceived});
            const isEliminated = (
                    isEliminatedByForce
                ||  !player[flagActive]
                ||  life < 1
                ||  maxCommanderDamageReceived >= 21
            );

            const totalCommanderDeaths = damageRecords.filter(damage => (
                    damage[attrPlayerId] == playerId
                &&  damage[attrReceivedFromCommanderPlayerId] == null
                &&  damage[attrRoundId] == activeRound[attrRoundId]
            ))
                .map(damage => damage[flagCommanderDeaths])
                .reduce((a, b) => a + b, 0);
            const card = document.createElement('div');
            card.className = `player-card ${isEliminated ? 'eliminated' : ''}`;
            card.style.animationDelay = `${index * 0.1}s`;
            card.dataset.playerId = playerId;
            card.dataset.userName = player.user_name || '';
            card.dataset.deckName = player.deck_name || '';

            card.innerHTML = `
                <div class="player-header">
                    <div class="player-info">
                        <div class="player-name">${displayName}</div>
                        <div class="commander-deaths">
                            <span>Commander Deaths:</span>
                            <div class="death-counter">
                                <button class="death-btn death-minus" data-player-id="${playerId}">&minus;</button>
                                <span class="death-display" data-player-id="${playerId}" ${attrValuePrevious}="${totalCommanderDeaths}">${totalCommanderDeaths}</span>
                                <button class="death-btn death-plus" data-player-id="${playerId}">+</button>
                            </div>
                        </div>
                    </div>
                    <button class="eliminate-btn" data-player-id="${playerId}" ${attrValuePrevious}="${isEliminated}">
                        ${isEliminated ? 'Revive' : 'Eliminate'}
                    </button>
                </div>

                <div class="life-total">
                    <input type="hidden" class="life-value" data-player-id="${playerId}" value="${life}">
                    <div class="life-display" data-player-id="${playerId}" ${attrValuePrevious}="${life}">${life}</div>
                    <label>Gain</label>
                    <div class="life-gain-controls">
                        <button class="life-gain-btn" data-player-id="${playerId}" data-amount="-5">-5</button>
                        <button class="life-gain-btn" data-player-id="${playerId}" data-amount="-1">-1</button>
                        <button class="life-gain-btn" data-player-id="${playerId}" data-amount="1">+1</button>
                        <button class="life-gain-btn" data-player-id="${playerId}" data-amount="5">+5</button>
                    </div>
                    <label>Loss</label>
                    <div class="life-loss-controls">
                        <button class="life-loss-btn" data-player-id="${playerId}" data-amount="-5">-5</button>
                        <button class="life-loss-btn" data-player-id="${playerId}" data-amount="-1">-1</button>
                        <button class="life-loss-btn" data-player-id="${playerId}" data-amount="1">+1</button>
                        <button class="life-loss-btn" data-player-id="${playerId}" data-amount="5">+5</button>
                    </div>
                </div>

                <div class="commander-damage-section">
                    <div class="section-title">Commander Damage Taken</div>
                    <div class="damage-grid" data-player-id="${playerId}">
                        ${PageMtgGame.renderCommanderDamageRows(
                              playerId // playerId
                            , player[attrDeckId] // deckId
                        )}
                    </div>
                </div>
            `;

            grid.appendChild(card);
        });

        this.reorderPlayerCards();

        PageMtgGame.renderCommanderDamageLog();

        // Hookup all event handlers
        this.hookupGameRoundEvents();
        this.hookupPlayerCardEvents();
    }
    static renderCommanderDamageLog() {
        const currentRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();

        const damageTableBody = document.querySelector('.' + flagDamageLog + '.' + flagContainer + ' table tbody');
        damageTableBody.innerHTML = '';
        
        const previousRoundIds = rounds.filter(round => round[flagDisplayOrder] <= currentRoundDisplayOrder)
            .map(round => round[attrRoundId]);
        let newTableBodyHtml = '';
        damageRecords.forEach((damage) => {
            if (
                    damage[flagActive]
                &&  (
                        damage[flagCommanderDeaths] > 0
                    ||  damage[flagLifeGain] != 0
                    ||  damage[flagLifeLoss] != 0
                    ||  damage[flagIsEliminated]
                )
                // &&  rounds.filter(r => r[attrRoundId] == damage[attrRoundId])[0][flagDisplayOrder] <= currentRoundDisplayOrder
                &&  previousRoundIds.includes(damage[attrRoundId])
            ) {
                let round = rounds.filter(r => r[attrRoundId] == damage[attrRoundId])[0];
                let player = players.filter(p => p[attrPlayerId] == damage[attrPlayerId])[0];
                let receivedFromPlayer = (damage[attrReceivedFromCommanderPlayerId] == null) ? { [flagName]: ''} : players.filter(p => p[attrPlayerId] == damage[attrReceivedFromCommanderPlayerId])[0];
                newTableBodyHtml += `
                    <tr ${attrDamageId}="${damage[attrDamageId]}">
                        <td class="${attrRoundId}">${round[flagDisplayOrder]}</td>
                        <td class="${attrPlayerId}">${player[flagName]}</td>
                        <td class="${attrReceivedFromCommanderPlayerId}">${receivedFromPlayer[flagName]}</td>
                        <td class="${flagLifeGain}">${damage[flagLifeGain]}</td>
                        <td class="${flagLifeLoss}">${damage[flagLifeLoss]}</td>
                        <td class="${flagCommanderDeaths}">${damage[flagCommanderDeaths]}</td>
                        <td class="${flagIsEliminated}">${damage[flagIsEliminated]}</td>
                    </tr>
                `;
            }
        });
        damageTableBody.innerHTML = newTableBodyHtml;
    }
    static makeDefaultGameRoundPlayerDamage(playerId, receivedFromCommanderPlayerId) {
        let roundId = PageMtgGame.getActiveRoundId();
        return {
              [attrDamageId]: -1 - damageRecords.length
            , [attrRoundId]: roundId
            , [attrPlayerId]: playerId
            , [attrReceivedFromCommanderPlayerId]: receivedFromCommanderPlayerId
            , [flagLifeGain]: 0
            , [flagLifeLoss]: 0
            , [flagCommanderDeaths]: 0
            , [flagIsEliminated]: false
            , [flagActive]: true
        };
    }
    static getLatestRoundId() {
        let roundId = -1;
        if (rounds.length > 0) {
            const highestRoundDisplayOrder = rounds.map(round => { return round[flagDisplayOrder]; })
                .reduce((acc, cur) => Math.max(acc, cur), 0);
            const filteredRounds = rounds.filter(round => round[flagDisplayOrder] == highestRoundDisplayOrder);
            if (filteredRounds.length > 0) {
                roundId = filteredRounds[0][attrRoundId];
            }
            console.log({ "method": "getLatestRoundId", highestRoundDisplayOrder, filteredRounds, roundId });
        }
        return roundId;
    }
    static getActiveRoundDisplayOrder() {
        const roundDisplayOrderLabel = PageMtgGame.getRoundDisplayOrderLabel();
        return parseInt(DOM.getElementValueCurrent(roundDisplayOrderLabel));
    }
    static getActiveRoundId() {
        const currentRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
        let roundId = 0;
        if (rounds.length > 0) {
            let filteredRounds = rounds.filter(round => round[flagDisplayOrder] == currentRoundDisplayOrder);
            if (filteredRounds.length > 0) roundId = filteredRounds[0][attrRoundId];
            console.log({ "method": "getActiveRoundId", filteredRounds, roundId });
        }
        return roundId;
    }
    static makePlayerDisplayName(playerId, index) {
        if (playerId == null) {
            return `Player ${index + 1}`;
        }
        const player = players.filter(player => player[attrPlayerId] == playerId)[0];
        const deckId = player[attrDeckId];
        const deck = (deckId == null) ? null : decks.filter(deck => deck[attrDeckId] == deckId)[0];
        const user = (playerId == null) ? null : users[player[attrUserId]];
        return player[flagName] || `${(user == null) ? 'Error' : user[flagName]} - ${(deck == null) ? 'Error' : deck[flagName]}`;
    }
    static renderCommanderDamageRows(playerId) {
        const activeRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
        const previousRoundIds = rounds.filter(round => round[flagDisplayOrder] <= activeRoundDisplayOrder)
            .map(round => round[attrRoundId]);
        return players
            .filter(otherPlayer => otherPlayer[attrPlayerId] !== playerId)
            .map(otherPlayer => {
                const sourceId = otherPlayer[attrPlayerId];
                let otherPlayerDisplayName = PageMtgGame.makePlayerDisplayName(sourceId);
                const totalDamage = damageRecords.filter(damage => (
                        damage[attrPlayerId] == playerId
                    &&  damage[attrReceivedFromCommanderPlayerId] == sourceId
                    // &&  damage[attrRoundId] == roundId
                    &&  previousRoundIds.includes(damage[attrRoundId])
                ))
                    .map(damage => damage[flagLifeLoss])
                    .reduce((acc, curr) => acc + curr, 0);
                const isLethal = (totalDamage >= 21);

                return `
                    <div class="damage-row" data-player-id="${playerId}" data-source-id="${sourceId}">
                        <span class="damage-source">from ${otherPlayerDisplayName}</span>
                        <div class="damage-controls">
                            <button class="damage-btn damage-minus" data-player-id="${playerId}" data-source-id="${sourceId}">&minus;</button>
                            <input type="hidden" class="damage-value" data-player-id="${playerId}" data-source-id="${sourceId}" value="${totalDamage}">
                            <span class="damage-display ${isLethal ? 'lethal' : ''}" data-player-id="${playerId}" data-source-id="${sourceId}" ${attrValuePrevious}="${totalDamage}">${totalDamage}</span>
                            <button class="damage-btn damage-plus" data-player-id="${playerId}" data-source-id="${sourceId}">+</button>
                        </div>
                    </div>
                `;
            })
            .join('');
    }

    hookupGameRoundEvents() {
        let incrementRoundButtonSelector = '#gameSection .' + flagRow + '.' + flagRound + ' button.' + flagRoundDisplayOrderButton;
        Events.hookupEventHandler("click", incrementRoundButtonSelector, (event, button) => {
            const amount = button.classList.contains(flagRoundDisplayOrderPlus) ? 1 : -1;
            const roundDisplayOrderButtonContainer = button.parentElement;
            const roundDisplayOrderLabel = roundDisplayOrderButtonContainer.querySelector('span.' + flagRound + '.' + flagDisplayOrder);
            const currentRoundDisplayOrder = parseInt(DOM.getElementValueCurrent(roundDisplayOrderLabel));
            const newDisplayOrder = currentRoundDisplayOrder + amount;
            DOM.setElementValueCurrent(roundDisplayOrderLabel, newDisplayOrder);
            DOM.isElementDirty(roundDisplayOrderLabel);
            this.updateAndToggleShowButtonsSaveCancel();
            this.renderPlayers();
        });
    }
    static makeDefaultGameRound(displayOrder) {
        const newDisplayOrder = (displayOrder != null) ? displayOrder : 1 + Math.max(rounds.map(round => round[flagDisplayOrder]));
        return {
              [attrRoundId]: -newDisplayOrder
            , [attrGameId]: gameId
            , [flagNotes]: null
            , [flagDisplayOrder]: newDisplayOrder
            , [flagActive]: true
        };
    }
    hookupPlayerCardEvents() { 
        // Life gain buttons
        let lifeGainButtonSelector = '.life-gain-btn';
        Events.hookupEventHandler("click", lifeGainButtonSelector, (event, button) => {
            const playerId = parseInt(button.dataset.playerId);
            const amount = parseInt(button.dataset.amount);
            const activeRoundId = PageMtgGame.getActiveRoundId();
            const damageIndex = damageRecords.findIndex(damage => (
                    damage[attrRoundId] == activeRoundId
                &&  damage[attrPlayerId] == playerId
                &&  damage[attrReceivedFromCommanderPlayerId] == null
            ));
            this.changeLife(
                playerId // playerId
                , amount // amount
                , true // isLifeGainNotLoss
                , true // updateDamage
                , damageIndex // damageIndex
            );
        });

        // Life loss buttons
        let lifeLossButtonSelector = '.life-loss-btn';
        Events.hookupEventHandler("click", lifeLossButtonSelector, (event, button) => {
            const playerId = parseInt(button.dataset.playerId);
            const amount = parseInt(button.dataset.amount);
            const activeRoundId = PageMtgGame.getActiveRoundId();
            const damageIndex = damageRecords.findIndex(damage => (
                    damage[attrRoundId] == activeRoundId
                &&  damage[attrPlayerId] == playerId
                &&  damage[attrReceivedFromCommanderPlayerId] == null
            ));
            this.changeLife(
                playerId // playerId
                , amount // amount
                , false // isLifeGainNotLoss
                , true // updateDamage
                , damageIndex // damageIndex
            );
        });

        // Commander death buttons
        let commanderDeathButtonSelector = '.death-btn';
        Events.hookupEventHandler("click", commanderDeathButtonSelector, (event, button) => {
            const playerId = parseInt(button.dataset.playerId);
            const isMinusButton = button.classList.contains('death-minus');
            const amount = (isMinusButton) ? -1 : 1;
            this.changeCommanderDeaths(playerId, amount);
        });

        // Commander damage buttons
        let commmanderDamageButtonSelector = '.damage-btn';
        Events.hookupEventHandler("click", commmanderDamageButtonSelector, (event, button) => {
            const playerId = parseInt(button.dataset.playerId);
            const sourceId = parseInt(button.dataset.sourceId);
            const isMinusButton = button.classList.contains('damage-minus');
            const amount = (isMinusButton) ? -1 : 1;
            this.changeCommanderDamage(playerId, sourceId, amount);
        });

        // Eliminate buttons
        let eliminatePlayerButtonSelector = '.eliminate-btn';
        Events.hookupEventHandler("click", eliminatePlayerButtonSelector, (event, button) => {
            const playerId = parseInt(button.dataset.playerId);
            this.toggleEliminate(playerId);
        });
    }

    changeLife(playerId, amount, isLifeGainNotLoss = false, updateDamage = false, damageIndex = null) {
        const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
        // if (!card || card.classList.contains('eliminated')) return;

        const lifeInput = card.querySelector(`.life-value[data-player-id="${playerId}"]`);
        const lifeDisplay = card.querySelector(`.life-display[data-player-id="${playerId}"]`);

        const currentLife = parseInt(lifeInput.value) || 0;
        const newLife = currentLife + amount * ((isLifeGainNotLoss) ? 1 : -1);

        DOM.setElementAttributeValueCurrent(lifeDisplay, newLife);
        DOM.isElementDirty(lifeDisplay);
        lifeInput.value = newLife;
        lifeDisplay.textContent = newLife;

        if (updateDamage) {
            let fieldFlag = (isLifeGainNotLoss) ? flagLifeGain : flagLifeLoss;
            damageRecords[damageIndex][fieldFlag] += amount;
        }

        PageMtgGame.renderCommanderDamageLog();

        // PageMtgGame.debouncedSave();
        this.updateAndToggleShowButtonsSaveCancel();
    }

    changeCommanderDamage(playerId, sourceId, amount) {
        const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
        // if (!card || card.classList.contains('eliminated')) return;

        const damageInput = card.querySelector(`.damage-value[data-player-id="${playerId}"][data-source-id="${sourceId}"]`);
        const damageDisplay = card.querySelector(`.damage-display[data-player-id="${playerId}"][data-source-id="${sourceId}"]`);

        const currentDamage = parseInt(damageInput.value) || 0;
        const newDamage = Math.max(0, currentDamage + amount);
        amount = newDamage - currentDamage;
        DOM.setElementAttributeValueCurrent(damageDisplay, newDamage);
        DOM.isElementDirty(damageDisplay);
        damageInput.value = newDamage;
        damageDisplay.textContent = newDamage;

        // Update lethal class
        if (newDamage >= 21) {
            damageDisplay.classList.add('lethal');
        } else {
            damageDisplay.classList.remove('lethal');
        }

        const activeRoundId = PageMtgGame.getActiveRoundId();
        const damageIndex = damageRecords.findIndex(damageRecord => (
                damageRecord[attrRoundId] == activeRoundId
            &&  damageRecord[attrPlayerId] == playerId
            &&  damageRecord[attrReceivedFromCommanderPlayerId] == sourceId
        ));
        damageRecords[damageIndex][flagLifeLoss] += amount;
        
        let isLifeGainNotLoss = false;
        this.changeLife(
              playerId // playerId
            , amount // amount
            , isLifeGainNotLoss // isLifeGainNotLoss
            , false // updateDamage
            , damageIndex // damageIndex
        );
        // PageMtgGame.debouncedSave();
    }

    changeCommanderDeaths(playerId, amount) {
        const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
        // if (!card || card.classList.contains('eliminated')) return;

        const deathDisplay = card.querySelector(`.death-display[data-player-id="${playerId}"]`);
        const currentDeaths = parseInt(deathDisplay.textContent) || 0;
        const newDeaths = Math.max(0, currentDeaths + amount);

        deathDisplay.textContent = newDeaths;
        DOM.setElementAttributeValueCurrent(deathDisplay, newDeaths);
        DOM.isElementDirty(deathDisplay);

        const activeRoundId = PageMtgGame.getActiveRoundId();
        const damageIndex = damageRecords.findIndex(damage => (
                damage[attrRoundId] == activeRoundId
            &&  damage[attrPlayerId] == playerId
            &&  damage[attrReceivedFromCommanderPlayerId] == null
        ));
        damageRecords[damageIndex][flagCommanderDeaths] = newDeaths;

        PageMtgGame.renderCommanderDamageLog();

        // PageMtgGame.debouncedSave();
        this.updateAndToggleShowButtonsSaveCancel();
    }

    toggleEliminate(playerId) {
        const card = document.querySelector(`.player-card[data-player-id="${playerId}"]`);
        if (!card) return;

        const eliminateBtn = card.querySelector(`.eliminate-btn[data-player-id="${playerId}"]`);
        const wasEliminated = card.classList.contains('eliminated');

        if (wasEliminated) {
            card.classList.remove('eliminated');
            eliminateBtn.textContent = 'Eliminate';
        } else {
            card.classList.add('eliminated');
            eliminateBtn.textContent = 'Revive';
        }
        const isEliminated = card.classList.contains('eliminated');

        const activeRoundId = PageMtgGame.getActiveRoundId();
        const damageIndex = damageRecords.findIndex(damage => (
                damage[attrRoundId] == activeRoundId
            &&  damage[attrPlayerId] == playerId
            &&  damage[attrReceivedFromCommanderPlayerId] == null
        ));
        damageRecords[damageIndex][flagIsEliminated] = isEliminated;

        DOM.setElementAttributeValueCurrent(eliminateBtn, isEliminated);
        DOM.isElementDirty(eliminateBtn);

        this.reorderPlayerCards();
        PageMtgGame.renderCommanderDamageLog();

        // PageMtgGame.debouncedSave();
        this.updateAndToggleShowButtonsSaveCancel();
    }
    reorderPlayerCards() {
        let playerGrid = document.getElementById('playersGrid');
        let currentPlayerCards = playerGrid.querySelectorAll('.player-card');
        let newPlayerCards = [];
        let playerCardMetas = [];
        currentPlayerCards.forEach((playerCard, index) => {
            newPlayerCards.push(playerCard.cloneNode(true));
            playerCardMetas.push({
                  [flagIsEliminated]: playerCard.classList.contains(flagIsEliminated)
                , [attrPlayerId]: playerCard.dataset["playerId"]
                , [flagDisplayOrder]: index
            });
        });
        let activeRoundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
        let newPlayerGridInnerHTML = '';
        let playerIdA, playerIdB, isEliminatedAsIntA, isEliminatedAsIntB, playerA, playerB, indexPlayerCard;
        playerCardMetas.sort((a, b) => {
            playerIdA = a[attrPlayerId];
            playerIdB = b[attrPlayerId];
            isEliminatedAsIntA = PageMtgGame.isPlayerEliminated(playerIdA, activeRoundDisplayOrder) ? 1 : 0;
            isEliminatedAsIntB = PageMtgGame.isPlayerEliminated(playerIdB, activeRoundDisplayOrder) ? 1 : 0;
            playerA = players.filter(p => p[attrPlayerId] == playerIdA)[0];
            playerB = players.filter(p => p[attrPlayerId] == playerIdB)[0];
            return (
                  players.length * isEliminatedAsIntA
                + playerA[flagDisplayOrder]
            ) - (
                  players.length * isEliminatedAsIntB
                + playerB[flagDisplayOrder]
            );
        }).forEach((playerCardMeta) => {
            indexPlayerCard = playerCardMeta[flagDisplayOrder];
            newPlayerGridInnerHTML += newPlayerCards[indexPlayerCard].outerHTML;
        });

        playerGrid.innerHTML = newPlayerGridInnerHTML;

        playerGrid.querySelectorAll('.' + flagInitialised).forEach((initialisedElement) => {
            initialisedElement.classList.remove(flagInitialised);
        });

        this.hookupPlayerCardEvents();
    }
    static isPlayerEliminated(playerId, roundDisplayOrder = null) {
        if (roundDisplayOrder == null) roundDisplayOrder = PageMtgGame.getActiveRoundDisplayOrder();
        const filteredRoundIds = rounds.filter(round => round[flagDisplayOrder] <= roundDisplayOrder)
            .map(round => round[attrRoundId]);
        let hasDamageWithIsEliminated = damageRecords.filter(damage => (
            //     damage[attrRoundId] <= roundDisplayOrder
                filteredRoundIds.includes(damage[attrRoundId])
            &&  damage[attrPlayerId] == playerId
            &&  damage[flagIsEliminated]
        )).length > 0;
        let damageFromOtherPlayers = {};
        let otherPlayerId;
        damageRecords.filter(damage => (
            //     damage[attrRoundId] <= roundId
                filteredRoundIds.includes(damage[attrRoundId])
            &&  damage[attrPlayerId] == playerId
            &&  damage[attrReceivedFromCommanderPlayerId] != null
        ))
            .forEach((damage) => {
                otherPlayerId = damage[attrReceivedFromCommanderPlayerId];
                damageFromOtherPlayers[otherPlayerId] = 
                      damage[flagLifeLoss]
                    + ((damageFromOtherPlayers[otherPlayerId] == null) ? 0 : damageFromOtherPlayers[otherPlayerId]);
            });
        let maxDamageFromOtherCommander = Object.keys(damageFromOtherPlayers)
            .map((playerId) => damageFromOtherPlayers[playerId])
            .reduce((acc, cur) => Math.max(acc, cur), 0);
        let totalDamageTaken = damageRecords.filter(damage => (
            //     damage[attrRoundId] <= roundId
                filteredRoundIds.includes(damage[attrRoundId])
            &&  damage[attrPlayerId] == playerId
        ))
            .map((damage) => damage[flagLifeLoss] - damage[flagLifeGain])
            .reduce((a, b) => a + b, 0);
        console.log({ roundDisplayOrder, filteredRoundIds, hasDamageWithIsEliminated, maxDamageFromOtherCommander, totalDamageTaken });
        return (
                hasDamageWithIsEliminated
            ||  maxDamageFromOtherCommander >= 21
            ||  totalDamageTaken >= game[flagStartingLife]
        );
    }

    static updatePlayerSetup() {
        const playerCountInput = document.getElementById('playerCount');
        if (!playerCountInput) return;

        const playerCount = parseInt(playerCountInput.value);
        const grid = document.getElementById('playerSetupGrid');
        if (!grid) return;

        grid.innerHTML = '';
        const wrapperTemplate = document.getElementById(playerSetupWrapperTemplateId);
        let player, wrapper, wrapperHeading, userDdl, deckDdl, nameInput;
        for (let i = 0; i < playerCount; i++) {
            if (i < players.length) {
                player = players[i];
            }
            else {
                player = PageMtgGame.makeDefaultGamePlayer();
                players.push(player);
            }
            wrapper = wrapperTemplate.cloneNode(true);
            wrapper.removeAttribute("id");
            wrapper.setAttribute(flagDisplayOrder, i + 1);
            wrapper.classList.remove(flagIsCollapsed);
            wrapperHeading = wrapper.querySelector('label');
            wrapperHeading.innerText = 'Player ' + (i + 1);
            userDdl = wrapper.querySelector('.playerUser select');
            DOM.setElementValuesCurrentAndPrevious(userDdl, player[attrUserId]);
            deckDdl = wrapper.querySelector('.playerDeck select');
            DOM.setElementValuesCurrentAndPrevious(deckDdl, player[attrDeckId]);
            nameInput = wrapper.querySelector('.playerName input');
            DOM.setElementValuesCurrentAndPrevious(nameInput, player[flagName]);
            console.log('player: ', player);
            grid.appendChild(wrapper);
        }
    }
    static makeDefaultGamePlayer() {
        return {
              [attrPlayerId]: -players.length
            , [attrGameId]: gameId
            , [attrUserId]: user[attrUserId]
            , [attrDeckId]: 0
            , [flagName]: ""
            , [flagNotes]: null
            , [flagDisplayOrder]: players.length
            , [flagActive]: true
        };
    }

    async startGame() {
        const playerCountInput = document.getElementById('playerCount');
        if (!playerCountInput) return;

        const playerCount = parseInt(playerCountInput.value);
        const playersToSave = [];

        let playerSetupWrapper, playerId, player, userDdl, userId, deckDdl, deckId, nameInput, name;
        for (let i = 0; i < playerCount; i++) {
            playerSetupWrapper = document.querySelector('.player-name-input-wrapper[' + flagDisplayOrder + '="' + (i + 1) + '"]');
            userDdl = playerSetupWrapper.querySelector('.playerUser select');
            deckDdl = playerSetupWrapper.querySelector('.playerDeck select');
            nameInput = playerSetupWrapper.querySelector('.playerName input');
            
            userId = DOM.getElementValueCurrent(userDdl);
            deckId = DOM.getElementValueCurrent(deckDdl);
            name = nameInput ? nameInput.value.trim() || null : null; // `Player ${i + 1}` : `Player ${i + 1}`;

            playerId = playerSetupWrapper.getAttribute(attrPlayerId);
            player = players.filter(p => p[attrPlayerId] == playerId)[0];
            playersToSave.push({
                  ...player
                , [attrGameId]: gameId
                , [attrUserId]: userId
                , [attrDeckId]: deckId
                , [flagName]: name
                , [flagDisplayOrder]: i + 1
                , [flagActive]: true
            });
        }

        // Save players to server
        const comment = 'Save players';
        const self = this;
        API.saveGamePlayers(playersToSave, null, comment)
            .then(data => {
                if (data[flagStatus] == flagSuccess) {
                    self.leave();
                    window.location.reload();
                }
                else {
                    console.error('Failed to save players:', data[flagMessage]);
                    PageMtgGame.showError('An error occurred while creating the game');
                }
            })
            .catch(error => {
                console.error('Error creating game:', error);
                PageMtgGame.showError('An error occurred while creating the game');
            })
            .finally(() => {
            });
    }

    static resetGame() {
        if (confirm('Are you sure you want to start a new game? Current game will be lost.')) {
            localStorage.removeItem(`mtgGame_${gameId}`);
            window.location.href = hashPageGames;
        }
    }

    async saveGame() {
        /*
        const gameState = {
              [flagPlayer]: players
            , [flagRound]: rounds
            , [flagDamage]: damageRecords
        };
        if (gameState[flagPlayer].length > 0) {
            localStorage.setItem(`mtgGame_${gameId}`, JSON.stringify(gameState));
            PageMtgGame.showSaveIndicator();
        }
        */
        const comment = 'Save player damage';
        const self = this;
        API.saveGameRoundPlayerDamages(rounds, damageRecords, null, comment)
            .then(data => {
                if (data[flagStatus] == flagSuccess) {
                    self.leave();
                    window.location.reload();
                }
                else {
                    console.error('Failed to save player damages:', data[flagMessage]);
                    PageMtgGame.showError('An error occurred while saving player damages');
                }
            })
            .catch(error => {
                console.error('Error saving player damages:', error);
                PageMtgGame.showError('An error occurred while saving player damages');
            })
            .finally(() => {
            });
    }
    /*
    static debouncedSave() {
        clearTimeout(PageMtgGame._saveTimeout);
        PageMtgGame._saveTimeout = setTimeout(() => PageMtgGame.saveGame(), 500);
    }

    static showSaveIndicator() {
        const indicator = document.getElementById('saveIndicator');
        if (indicator) {
            indicator.classList.add('show');
            setTimeout(() => {
                indicator.classList.remove('show');
            }, 2000);
        }
    }
    */
    saveRecordsTableDirty() {
        this.saveGame();
    }
    static showError(message) {
        // Check if there's an overlay error element
        const errorOverlay = document.getElementById('overlayError');
        if (errorOverlay) {
            const errorLabel = errorOverlay.querySelector('.error-message, #labelError');
            if (errorLabel) {
                errorLabel.textContent = message;
            }
            errorOverlay.classList.remove('hidden');
            errorOverlay.style.display = 'flex';
        } else {
            // Fallback to alert
            alert(message);
        }
    }

    updateAndToggleShowButtonsSaveCancel() {
        // let pageBody = document.querySelector(idPageBody);
        let isDirty = DOM.hasDirtyChildrenContainer(pageBody);

        let buttonContainerSelector = '.' + flagContainer + '.' + flagSave + '.' + flagCancel;
        let buttonSave = document.querySelector(buttonContainerSelector + ' ' + idButtonSave);
        let areVisibleSaveCancelButtons = !buttonSave.classList.contains(flagIsCollapsed);
        
        console.log({ pageBody, isDirty, areVisibleSaveCancelButtons });

        this.toggleShowButtonsSaveCancel(isDirty || areVisibleSaveCancelButtons);
    }

    leave() {
        super.leave();
    }
}

// Static timeout reference for debouncing
PageMtgGame._saveTimeout = null;
