<script lang="ts">
    import { TABotTokenStore, TAServerPort, TAServerUrl, authTokenStore, client } from "$lib/stores";
    import { onMount, onDestroy } from "svelte";
    import { bkAPIUrl } from '$lib/config.json';
    import Notification from '$lib/components/notifications/Popup.svelte';
    //@ts-ignore
    import { 
        Match, 
        Tournament, 
        TAClient, 
        Response_ResponseType, 
        User, 
        RealtimeScore, 
        Push_SongFinished, 
        Map, 
        User_DownloadStates, 
        User_PlayStates,
        Tournament_TournamentSettings_Pool,

        Command_ModifyGameplay_Modifier

    } from 'moons-ta-client';
    import { writable } from "svelte/store";
    import { goto } from "$app/navigation";
    import { fetchMapByLevelId, fetchMapsByLevelIds, type BeatSaverMap } from "$lib/services/beatsaver.js";
    import SongQueue from "$lib/components/modules/SongQueue.svelte";
    import EditMap from "$lib/components/popups/EditMap.svelte";
    import Popup from "$lib/components/notifications/Popup.svelte";
    import PreviouslyPlayedSongs from "$lib/components/modules/PreviouslyPlayedSongs.svelte";
    import ResultsForMap from "$lib/components/popups/ResultsForMap.svelte";
    import AddMapsFromTAPool from "$lib/components/popups/AddMapsFromTAPool.svelte";
    import ModifyGameplay from "$lib/components/popups/ModifyGameplay.svelte";
    
    // Types
    import { 
        type CustomMap, 
        type RealTimeScoreForPlayers, 
        type PreviousResults, 
        type ButtonConfig, 
        type ColorPreset,
        MapDifficulty,

        type CustomTAMapPool

    } from "$lib/interfaces/match.interfaces";
    import AddMapsFromFile from "$lib/components/popups/AddMapsFromFile.svelte";

    export let data;

    // Route params
    const tournamentGuid: string = data.tournamentGuid;
    const matchGuid: string = data.matchGuid;

    // Core state
    let tournament: Tournament | undefined;
    let match: Match | undefined;
    let isLoading = false;
    let error: string | null = null;
    let matchPlayers: User[] = [];
    let currentSong: any = null;
    let currentSongData: BeatSaverMap;
    let isMatchActive = false;
    let streamSyncEnabled = false;

    // Song queue and results
    let upcomingQueueMaps: CustomMap[] = [];
    let realTimeScores: RealTimeScoreForPlayers[] = [];
    let previousMatchResults: PreviousResults[] = [];
    let showingResultsData: PreviousResults;

    // Modal states
    let showKickConfirmModal = false;
    let showBackToMenuModal = false;
    let showExitMatchModal = false;
    let playerToKick: User | null = null;
    let showCannotExitPage: boolean = false;
    let modalMessage: string = "";
    let modalIcon: string = "danger";
    let modalIconColor: ColorPreset = 'error';
    let modalButtons: ButtonConfig[] = [];
    let modalCustomImage: string = "";
    let modalAutoClose: number = 0;

    // Map editing states
    let showMapModal: boolean = false;
    let currentlyEditingMap: CustomMap | null = null;

    // Song loading states
    let loadingSongForPlayers: boolean = false;
    let failedToLoadSongForPlayers: boolean = false;

    // Results popup states
    let showResultsPopup: boolean = false;
    let dontShowResultsPopupForThisSong: boolean = false;
    let currentlyShowingOldMapResults: boolean = false;

    // Add maps from TA pool modal state
    let taPools: Tournament_TournamentSettings_Pool[] = [];
    let customTAPools: CustomTAMapPool[] = [];
    let showAddFromTAPoolPopup: boolean = false;
    let allowAddFromTAPool: boolean = true;

    // User editing state
    let isModifyGameplayPopupVisible: boolean = false;
    let editingPlayerPlatformId: string, editingPlayerName: string, editingPlayerPfp: string = "";

    // Active song tracking
    const activeSongPlayers = new Set<`${string}-${string}`>(); // Format: "userGuid-levelId"

    // Reactive statements
    $: isAnyPlayerPlaying = matchPlayers.some(player => (player.playState as any) === User_PlayStates.InGame);

    $: if(isAnyPlayerPlaying && currentSong) {
        matchPlayers
            .filter(x => x.playState == User_PlayStates.InGame)
            .forEach(player => {
                const playerKey: string = `${player.guid}-${currentSong.beatmap.levelId.toLowerCase()}`;
                if (!activeSongPlayers.has(playerKey as any)) {
                    activeSongPlayers.add(playerKey as any);
                }
            });
    }

    if ($authTokenStore) {
        const cleanToken = $authTokenStore.replace('Bearer ', '');
        client.setAuthToken(cleanToken);
    }

    // Show notification function
    function showNotification(message: string, type: 'warning' | 'error' | 'success' = 'warning') {
        // Your existing notification function
        console.log(`${type}: ${message}`);
    }

    function handleExitResultsPopupClicked() {
        showResultsPopup = false;
        if(!currentlyShowingOldMapResults) {
            dontShowResultsPopupForThisSong = true;
        }
        currentlyShowingOldMapResults = false;
    }

    function showResultsPopupWithOldScores(event: CustomEvent) {
        const oldData: PreviousResults = event.detail.map;
        currentlyShowingOldMapResults = true;
        showingResultsData = oldData;
        showResultsPopup = true;
        dontShowResultsPopupForThisSong = false;
    }

    // Handle back to matches with confirmation if needed
    async function handleBackToMatches(endMatch: boolean) {
        if(match!.leader !== client.stateManager.getSelfGuid()) {
            await removeSelfFromMatch();
            goto(`/tournaments/${tournamentGuid}`);
        }

        if(endMatch) {
            if (isAnyPlayerPlaying) {
                showExitMatchModal = true;
                modalMessage = `Players are currently in-game. Exiting will close the match for all players. Continue?`;
                modalIcon = 'warning';
                modalButtons = [
                    {
                        text: `Close (NO)`,
                        type: 'primary',
                        action: () => showBackToMenuModal = false,
                        icon: 'close'
                    },
                    {
                        text: `Yes (YES)`,
                        type: 'secondary',
                        action: async() => await confirmExitMatch(),
                        icon: 'backspace'
                    }
                ];
            } else {
                await removeSelfFromMatch();
                await client.deleteMatch(tournamentGuid, matchGuid);
                goto(`/tournaments/${tournamentGuid}`);
            }
        }

        showExitMatchModal = true;
        modalMessage = `This should not cause issues as it should not end the match. But TA can be weird sometimes. THIS SHOULD BE SAFE. Do you want to continue?`;
        modalIcon = 'info';
        modalIconColor = 'info';
        modalButtons = [
            {
                text: `Close (NO)`,
                type: 'primary',
                action: () => showBackToMenuModal = false,
                icon: 'close'
            },
            {
                text: `Yes (YES)`,
                type: 'secondary',
                action: async() => await confirmExitMatch(),
                icon: 'backspace'
            }
        ];
    }

    // Confirm exit match
    function confirmExitMatch() {
        showExitMatchModal = false;
        showNotification('Match will be closed', 'warning');
        goto(`/tournaments/${tournamentGuid}`);
    }

    // Handle kick player
    function handleKickPlayer(player: User) {
        playerToKick = player;
        modalMessage = `Are you sure you want to kick ${player.name} from the match?`;
        modalIcon = 'warning';
        modalButtons = [
            {
                text: `Close (NO)`,
                type: 'primary',
                action: () => showKickConfirmModal = false,
                icon: 'close'
            },
            {
                text: `Kick ${player.name} (YES)`,
                type: 'secondary',
                action: async() => await confirmKickPlayer(),
                icon: 'sports_martial_arts'
            }
        ];
        showKickConfirmModal = true;
    }

    // Confirm kick player
    async function confirmKickPlayer() {
        if (!playerToKick) {
            showKickConfirmModal = false;
            return;
        }
        
        try {
            // Implementation for kicking player from match
            await kickPlayerFromMatch(playerToKick.guid);
            showNotification(`${playerToKick.name} has been kicked from the match`, 'success');
            await fetchMatchData();
        } catch (err) {
            console.error('Error kicking player:', err);
            showNotification('Failed to kick player', 'error');
        }
        
        showKickConfirmModal = false;
        playerToKick = null;
    }

    // Start song for all players
    async function startSong() {
        try {
            if (!currentSong) {
                showNotification('No song selected', 'warning');
                return;
            }
            
            await startSongForMatch();
            isMatchActive = true;
        } catch (err) {
            console.error('Error starting song:', err);
            showNotification('Failed to start song', 'error');
        }
    }

    // Start song with stream sync
    async function startSongWithStreamSync() {
        if (!streamSyncEnabled) return;
        
        try {
            await startSongWithStreamSyncForMatch();
            showNotification('Song started with stream sync', 'success');
            isMatchActive = true;
        } catch (err) {
            console.error('Error starting song with stream sync:', err);
            showNotification('Failed to start song with stream sync', 'error');
        }
    }

    // Back to menu
    function handleBackToMenu() {
        showBackToMenuModal = true;
        modalMessage = `This will send all players back to the menu and stop the current song. Continue?`;
        modalIcon = 'warning';
        modalButtons = [
            {
                text: `Close (NO)`,
                type: 'primary',
                action: () => showBackToMenuModal = false,
                icon: 'close'
            },
            {
                text: `Yes (YES)`,
                type: 'secondary',
                action: async() => await confirmBackToMenu(),
                icon: 'backspace'
            }
        ];
    }

    // Confirm back to menu
    async function confirmBackToMenu() {
        try {
            await sendPlayersBackToMenu();
            showNotification('All players sent back to menu', 'success');
            isMatchActive = false;
            currentSong = null;
        } catch (err) {
            console.error('Error sending players back to menu:', err);
            showNotification('Failed to send players back to menu', 'error');
        }
        
        showBackToMenuModal = false;
    }

    // Return status color based on player status
    function getStatusColor(playState: User_PlayStates, downloadState: User_DownloadStates): string {
        if(downloadState == User_DownloadStates.Downloading) return "var(--danger-color)";
        if(downloadState == User_DownloadStates.DownloadError) return "var(--danger-color)";
        if(playState == User_PlayStates.InGame) return "var(--danger-color)";

        switch (playState) {
            case User_PlayStates.WaitingForCoordinator:
                return "#FCD34D"; // Yellow
            case User_PlayStates.InMenu:
                return "#10B981"; // Green
            default:
                return "var(--text-secondary)";
        }
    }

    // Handle match events
    async function handleMatchUpdated(params: [Match, Tournament]) {
        console.log("Match updated:", params);
        if (params[0].guid === matchGuid) {
            match = params[0];
            await refreshMatchDetails(params[0]);
        }
    }

    function handleMatchDeleted(params: [Match, Tournament]) {
        console.log("Match deleted:", params);
        if (params[0].guid === matchGuid) {
            goto(`/tournaments/${tournamentGuid}`);
        }
    }

    function handleSongChanged(songInfo: any) {
        console.log("Song changed:", songInfo);
        currentSong = songInfo;
    }

    async function addSelfToMatch() {
        if(!match?.associatedUsers.includes(client.stateManager.getSelfGuid())) {
            const response = await client.addUserToMatch(tournamentGuid, matchGuid, client.stateManager.getSelfGuid());
            console.log("Added Self to match!", response);
        }
    }

    async function removeSelfFromMatch() {
        if(match?.associatedUsers.includes(client.stateManager.getSelfGuid())) {
            const response = await client.removeUserFromMatch(tournamentGuid, matchGuid, client.stateManager.getSelfGuid());
            console.log("Removed Self from match!", response);
        }
    }

    async function refreshMatchDetails(match: Match) {
        // Get match players
        matchPlayers = match.associatedUsers
            .map(userGuid => client.stateManager.getUser(tournamentGuid, userGuid))
            .filter(user => user && user.clientType === 0) as User[];

        // Get current song if available
        currentSong = match.selectedMap?.gameplayParameters || null;
        if(currentSong) {
            try {
                const tempMapData = await fetchMapByLevelId(currentSong.beatmap.levelId);

                if(tempMapData) {
                    currentSongData = tempMapData;
                    if(!upcomingQueueMaps.some(x => x.taData.gameplayParameters?.beatmap?.levelId.toUpperCase() == currentSong.beatmap.levelId.toUpperCase())) {
                        upcomingQueueMaps = [
                            {
                                taData: match.selectedMap!,
                                beatsaverData: tempMapData!
                            },
                            ...upcomingQueueMaps
                        ];
                    }
                }
            } catch (error) {
                console.log('Current song beatsaver data fetch failed');
            }
        }

        const tournament = client.stateManager.getTournament(tournamentGuid);

        if(!tournament!.settings!.enablePools) {
            allowAddFromTAPool = false;
            return;
        }

        taPools = tournament!.settings!.pools;

        // First, collect all unique level IDs from all pools
        const allLevelIds = taPools.flatMap(pool => 
            pool.maps.map(map => map.gameplayParameters!.beatmap!.levelId)
        );

        // Remove duplicates to avoid fetching the same map multiple times
        const uniqueLevelIds = [...new Set(allLevelIds)];

        // Fetch all maps data in one go (your function handles chunking internally)
        const allMapBeatSaverDatas = await fetchMapsByLevelIds(uniqueLevelIds);

        // Create a lookup object for quick access
        const mapDataLookup: { [levelId: string]: any } = {};
        allMapBeatSaverDatas.forEach(mapData => {
            if (mapData && mapData.versions[0].hash) {
                mapDataLookup[mapData.versions[0].hash] = mapData;
            }
        });

        // Now process each pool using the pre-fetched data
        customTAPools = await Promise.all(
            taPools.map(async (pool) => {
                const poolCustomMaps: CustomMap[] = pool.maps
                    .map(map => {
                        const levelId = map.gameplayParameters!.beatmap!.levelId;
                        const beatsaverData = mapDataLookup[(levelId.toLowerCase().replace('custom_level_', ''))];
                        
                        if (!beatsaverData) return null;
                        
                        return {
                            taData: map,
                            beatsaverData: beatsaverData
                        };
                    })
                    .filter(result => result !== null) as CustomMap[];
                
                return {
                    name: pool.name,
                    guid: pool.guid,
                    image: pool.image,
                    maps: poolCustomMaps
                };
            })
        );
    }

    async function fetchMatchData() {
        if (!$authTokenStore) {
            window.location.href = '/discordAuth';
            return;
        }
        
        isLoading = true;
        error = null;
        
        try {
            if(!client.isConnected) {
                const connectResult = await client.connect($TAServerUrl, $TAServerPort);

                if (connectResult.details.oneofKind === "connect" && connectResult.type === Response_ResponseType.Fail) {
                    throw new Error(connectResult.details.connect.message);
                }
            }
            
            // Get tournament and match data
            tournament = client.stateManager.getTournament(tournamentGuid);
            match = client.stateManager.getMatch(tournamentGuid, matchGuid);

            if(!match?.associatedUsers.includes(client.stateManager.getSelfGuid())) {
                await addSelfToMatch();
            }
                        
            if (!match) {
                throw new Error('Match not found');
            }

            await refreshMatchDetails(match);
        } catch (err) {
            console.error('Error fetching match data:', err);
            error = err instanceof Error ? err.message : 'An unknown error occurred';
        } finally {
            isLoading = false;
        }
    }

    async function kickPlayerFromMatch(playerGuid: string) {
        const response = await client.removeUserFromMatch(tournamentGuid, matchGuid, playerGuid);
        console.log('Kicking player:', playerGuid);
        console.log("removeResponse", response);
    }

    async function startSongForMatch() {
        if (!currentSong) return;
        
        showResultsPopup = false;
        dontShowResultsPopupForThisSong = false;

        const levelId = currentSong.beatmap.levelId;
        const playingPlayerIds = matchPlayers.map(player => player.guid);
        
        // Clear any existing entries for this levelId
        Array.from(activeSongPlayers)
            .filter(key => key.endsWith(`-${levelId}`))
            .forEach(key => activeSongPlayers.delete(key));
        
        // Add all current players for this song
        playingPlayerIds.forEach(playerGuid => {
            activeSongPlayers.add(`${playerGuid}-${levelId.toLowerCase()}`);
        });
        
        const response = await client.playSong(tournamentGuid, currentSong, playingPlayerIds);
        console.log('startMap', response);
    }

    async function startSongWithStreamSyncForMatch() {
        // Implement start song with stream sync logic
        console.log('Starting song with stream sync');
    }

    async function sendPlayersBackToMenu() {
        const backToMenuResponse = await client.returnToMenu(tournamentGuid, matchPlayers.filter(x => x.playState == User_PlayStates.InGame).map(x => x.guid));
        console.log("Sent players back to menu!", backToMenuResponse)
    }

    async function handleUserDisconnected(params: [User, Tournament]) {
        if(matchPlayers.some(user => user.guid == params[0].guid)) {
            console.log("User disconnected from match. User: ", params[0])
            matchPlayers = matchPlayers.filter(x => x.guid !== params[0].guid);
        }
    }

    function getTimeStringFromSeconds(seconds: number) {
        let newString: string = "";
        const minutes = Math.floor(seconds / 60);

        if(minutes > 0) {
            newString += `${minutes} minutes `;
        }

        const timeSeconds = (seconds - Math.floor(seconds / 60) * 60).toFixed(1);

        if(!timeSeconds.startsWith("0")) {
            newString += `${timeSeconds} seconds `;
        }
        return newString;
    }

    async function handleRealtimeScoreUpdate(rts: RealtimeScore) {
        console.log(`Player ${matchPlayers.find(x => x.guid == rts.userGuid)?.name}: ${rts.accuracy}`, rts);

        const player = matchPlayers.find(x => x.guid === rts.userGuid)!;

        if(!player) return;

        const index = realTimeScores.findIndex(x => x.player.guid === rts.userGuid);

        if (index !== -1) {
            // Update the existing entry
            realTimeScores[index] = { player, recentScore: rts };
        } else {
            // Append only if it's a new player
            realTimeScores = [
                ...realTimeScores,
                { player, recentScore: rts }
            ];
        }
    }

    function getPlayerRtsScore(userGuid: string) {
        let playerScore = realTimeScores.find(x => x.player.guid == userGuid);
        if(!playerScore) {
            playerScore = {
                player: matchPlayers.find(x => x.guid == userGuid)!,
                recentScore: {
                    userGuid: userGuid,
                    score: 0,
                    scoreWithModifiers: 0,
                    maxScore: 0,
                    maxScoreWithModifiers: 0,
                    combo: 0,
                    playerHealth: 0,
                    accuracy: 0,
                    songPosition: 0,
                    notesMissed: 0,
                    badCuts: 0,
                    bombHits: 0,
                    wallHits: 0,
                    maxCombo: 0
                }
            }
        }

        return playerScore;
    }

    function getMaxScore(songInfo: BeatSaverMap, characteristic: string = "standard", difficulty: string): number {
        const diff = songInfo.versions[0].diffs.find(
            (x) => {
                const charMatch = x.characteristic.toLowerCase() === characteristic.toLowerCase();
                
                // Normalize difficulty strings for comparison
                const searchDiff = difficulty.toLowerCase() === "expert+" ? "expertplus" : difficulty.toLowerCase();
                const diffDiff = x.difficulty.toLowerCase() === "expert+" ? "expertplus" : x.difficulty.toLowerCase();
                
                return charMatch && diffDiff === searchDiff;
            }
        );

        return diff?.maxScore ?? 100000;
    }

    // Method to convert difficulty number to string
    function getDifficultyAsString(difficulty: number): string {
        return MapDifficulty[difficulty] || "ExpertPlus";
    }

    // Main accuracy calculation logic
    function calculateAccuracy(score: Push_SongFinished, mapWithSongInfo: CustomMap): number {
        const maxScore = getMaxScore(
            mapWithSongInfo.beatsaverData,
            score.beatmap?.characteristic?.serializedName ?? "Standard",
            getDifficultyAsString(score.beatmap?.difficulty ?? 4) || "ExpertPlus",
        );
        
        const accuracy = parseFloat(((score.score / maxScore) * 100).toFixed(2));
        return accuracy;
    }

    async function handleSongFinished(rts: Push_SongFinished) {
        if (rts.matchId !== matchGuid) return;
        
        const levelId = rts.beatmap?.levelId;
        if (!levelId) return;
                
        // Remove player from active song tracking
        activeSongPlayers.delete(`${rts.player!.guid}-${levelId.toLowerCase()}`);
        
        const map = upcomingQueueMaps.find(x => 
            x.taData.gameplayParameters!.beatmap!.levelId.toUpperCase() === levelId.toUpperCase()
        );
        
        if (!map) {
            console.log('Received a score for unknown map', rts);
            return;
        }
        
        let newRts: Push_SongFinished = {...rts, accuracy: 0};
        newRts.accuracy = calculateAccuracy(rts, map);
        
        // Check if any players are still playing this song
        const playersStillPlaying = Array.from(activeSongPlayers).some(key => key.endsWith(`-${levelId.toLowerCase()}`));
        console.log("Players still playing:", playersStillPlaying)
        
        const completionType = playersStillPlaying 
            ? 'Still Awaiting Scores' 
            : 'Completed';
        console.log("CompletionType", completionType)
        
        // Find or create the result record
        const existingRecordIndex = previousMatchResults.findIndex(x => 
            x.completionType === 'Still Awaiting Scores' && 
            x.taData.gameplayParameters?.beatmap?.levelId.toUpperCase() === levelId.toUpperCase()
        );
        console.log("existingRecordIndex", existingRecordIndex)
        
        if (existingRecordIndex === -1) {
            console.log("creating new record")
            previousMatchResults = [
                {
                    taData: map.taData,
                    beatsaverData: map.beatsaverData,
                    completionType,
                    scores: [newRts]
                },
                ...previousMatchResults
            ];
        } else {
            const existingRecord = previousMatchResults[existingRecordIndex];
            previousMatchResults = [
                {
                    taData: map.taData,
                    beatsaverData: map.beatsaverData,
                    completionType,
                    scores: [...existingRecord.scores, newRts]
                },
                ...previousMatchResults.filter((_, i) => i !== existingRecordIndex)
            ];
        }

        showingResultsData = previousMatchResults[0];
        showResultsPopup = true;
    }

    async function handleMapUpdated(event: CustomEvent) {
        const map = upcomingQueueMaps.find(x => x.taData.guid == event.detail.map.guid);
        if(!map) return;
        upcomingQueueMaps = [
            ...upcomingQueueMaps.filter(x => x.taData.guid !== event.detail.map.guid),
            {
                taData: (event.detail.map as Map),
                beatsaverData: map.beatsaverData
            }
        ];
    }
    
    async function handleMapAdded(event: CustomEvent) {
        const beatsaverData: BeatSaverMap | null = await fetchMapByLevelId(event.detail.map.gameplayParameters.beatmap.levelId);
        if(!beatsaverData) {
            console.log('Failed to fetch beatsaver map data for new map, aborting map addition!');
            return;
        }
        const newMap: CustomMap = {
            taData: event.detail.map,
            beatsaverData: beatsaverData
        };

        upcomingQueueMaps = [
            ...upcomingQueueMaps,
            newMap
        ];
    }

    function handleShowAddMapsFromTAPool() {
        showAddFromTAPoolPopup = true;
    }

    function handleCloseAddMapsFromTAPool() {
        showAddFromTAPoolPopup = false;
    }

    function handleAddMapsFromTAPool(event: CustomEvent) {
        showAddFromTAPoolPopup = false;
        const mapsToAdd: CustomMap[] = event.detail.maps;
        upcomingQueueMaps = [
            ...mapsToAdd,
            ...upcomingQueueMaps
        ];
    }

    function closeEditMapModal() {
        showMapModal = false;
    }

    async function handleAddMap() {
        currentlyEditingMap = null;
        showMapModal = true;
    }

    async function handleEditMap(event: CustomEvent) {
        currentlyEditingMap = event.detail.map;
        showMapModal = true;
    }

    async function handleRemoveMapFromQueue(event: CustomEvent) {
        upcomingQueueMaps = [
            ...upcomingQueueMaps.filter(x => x.taData.guid !== event.detail.map.taData.guid)
        ];
    }

    async function handleLoadMap(event: CustomEvent) {
        const map: CustomMap = event.detail.map;
        loadingSongForPlayers = false;
        failedToLoadSongForPlayers = false;
        dontShowResultsPopupForThisSong = false;

        let playingPlayerIds = matchPlayers
            .filter(player => player.playState !== User_PlayStates.InGame)
            .map(player => player.guid);

        if(!playingPlayerIds || playingPlayerIds.length == 0) {
            playingPlayerIds = [];
        }

        loadingSongForPlayers = true;
        const matchResponse = await client.setMatchMap(tournamentGuid, matchGuid, map.taData);
        if(matchResponse.type == Response_ResponseType.Fail) return;
        const loadResponse = await client.loadSong(tournamentGuid, map.taData.gameplayParameters!.beatmap!.levelId.toUpperCase().replace('CUSTOM_LEVEL_', 'custom_level_'), playingPlayerIds, 3 * 60 * 1000);
        failedToLoadSongForPlayers = false;

        const failedLoads = loadResponse.filter(x => x.response.type == Response_ResponseType.Fail);

        if(failedLoads.length > 0) {
            failedToLoadSongForPlayers = true;
            showNotification('Failed to load song as one or more players failed to load! Please try again', 'error');
        } else {
            loadingSongForPlayers = false;
            showNotification('Successfully loaded song!', 'success');
        }

        console.log("songLoaded", loadResponse);
    }

    async function handleUserUpdated(params: [User, Tournament]) {
        if(params[1].guid !== tournamentGuid) return;
        
        const userIndex = matchPlayers.findIndex(x => x.guid === params[0].guid);
        if(userIndex !== -1) {
            matchPlayers = [
                ...matchPlayers.slice(0, userIndex),
                params[0],
                ...matchPlayers.slice(userIndex + 1)
            ];
        } else {
            matchPlayers = [...matchPlayers, params[0]];
        }
    }

    async function handleSetGameplayModifiersForUsersVerbose(userIds: string[], modifiers: number) {
        try {
            const results: any[] = [];

            // Helper function to check if a specific modifier is enabled
            const isModifierEnabled = (modifier: Command_ModifyGameplay_Modifier): boolean => {
                return (modifiers & (1 << modifier)) !== 0;
            };

            // Apply each modifier if enabled
            if (isModifierEnabled(Command_ModifyGameplay_Modifier.InvertColors)) {
                console.log('Inverting colors for users:', userIds);
                const response = await client.flipColors(tournamentGuid, userIds);
                results.push({ type: 'InvertColors', response });
            }

            if (isModifierEnabled(Command_ModifyGameplay_Modifier.InvertHandedness)) {
                console.log('Inverting handedness for users:', userIds);
                const response = await client.flipHands(tournamentGuid, userIds);
                results.push({ type: 'InvertHandedness', response });
            }

            if (isModifierEnabled(Command_ModifyGameplay_Modifier.DisableBlueNotes)) {
                console.log('Disabling blue notes for users:', userIds);
                const response = await client.disableBlueNotes(tournamentGuid, userIds);
                results.push({ type: 'DisableBlueNotes', response });
            }

            if (isModifierEnabled(Command_ModifyGameplay_Modifier.DisableRedNotes)) {
                console.log('Disabling red notes for users:', userIds);
                const response = await client.disableRedNotes(tournamentGuid, userIds);
                results.push({ type: 'DisableRedNotes', response });
            }

            console.log(`Applied ${results.length} modifiers successfully`);
            console.log("Modifier apply res", results);
            return results;

        } catch (error) {
            console.error('Error applying gameplay modifiers:', error);
            throw error;
        }
    }

    async function handleModifyGameplayForPlayer(player: User) {
        editingPlayerName = player.name;
        editingPlayerPfp = player.discordInfo?.avatarUrl || "";
        editingPlayerPlatformId = player.guid;
        isModifyGameplayPopupVisible = true;
    }

    async function handleSendModifyGameplay(event: CustomEvent) {
        const { platformId, modifiers } = event.detail;
        
        // Convert platformId to userIds array if needed
        const userIds = [platformId]; // or however you map platform ID to user IDs
        
        await handleSetGameplayModifiersForUsersVerbose(userIds, modifiers);
    }

    const beforeUnloadHandler = (event: BeforeUnloadEvent) => {
        event.preventDefault(); // Not necessary in some browsers but safe
    };

    onMount(async() => {
        if ($authTokenStore) {
            // Fetch the match data that we need to display
            await fetchMatchData();
            // Using stateManger listen to global changes to our match
            client.stateManager.on('matchDeleted', handleMatchDeleted);
            client.stateManager.on('matchUpdated', handleMatchUpdated);
            client.stateManager.on('userDisconnected', handleUserDisconnected);
            client.stateManager.on('userUpdated', handleUserUpdated);
            client.on('realtimeScore', handleRealtimeScoreUpdate);
            client.on('songFinished', handleSongFinished);
            window.addEventListener('beforeunload', beforeUnloadHandler);
        } else {
            window.location.href = "/discordAuth"
        }
    });

    onDestroy(() => {
        // Remove the listeners that were added on mount
        client.stateManager.removeListener('matchUpdated', handleMatchUpdated);
        client.stateManager.removeListener('matchDeleted', handleMatchDeleted);
        client.stateManager.removeListener('userDisconnected', handleUserDisconnected);
        client.stateManager.removeListener('userUpdated', handleUserUpdated);
        client.removeListener('realtimeScore', handleRealtimeScoreUpdate);
        client.removeListener('songFinished', handleSongFinished);

        window.removeEventListener('beforeunload', beforeUnloadHandler);
    });
</script>

<div class="layout">
    <main class="match-dashboard">
        <div class="content-area">
            <div class="content-header">
                <div class="header-left">
                    <button 
                        class="back-button {isAnyPlayerPlaying ? 'disabled' : ''}" 
                        on:click={() => handleBackToMatches(false)}
                        disabled={isAnyPlayerPlaying}
                    >
                        <span class="material-icons">arrow_back</span>
                        Back to Matches
                        {#if isAnyPlayerPlaying}
                            <span class="material-icons warning-icon">warning</span>
                        {/if}
                    </button>
                    <button class="kick-button" on:click={() => handleBackToMatches(true)}>
                        <span class="material-icons">person_remove</span>
                        End Match
                    </button>
                    <div class="match-info">
                        <h2>Match: {match?.guid || 'Undefined'}</h2>
                        <p class="tournament-name">{tournament?.settings?.tournamentName}</p>
                    </div>
                </div>
                <div class="actions">
                    <button class="action-button refresh" on:click={fetchMatchData}>
                        <span class="material-icons">refresh</span>
                    </button>
                </div>
            </div>
            
            {#if isLoading}
                <div class="loading">
                    <div class="spinner"></div>
                    <p>Loading match data...</p>
                </div>
            {:else if error}
                <div class="error-container">
                    <span class="material-icons">error_outline</span>
                    <p>{error}</p>
                    <button class="retry-button" on:click={fetchMatchData}>Retry</button>
                </div>
            {:else}
                <div class="match-content">
                    <!-- Current Song Section -->
                    <div class="song-section">
                        <h3>Current Song</h3>
                        {#if currentSong && currentSongData}
                            <div class="current-song">
                                <div class="song-cover">
                                    <img src={currentSongData.versions[0].coverURL || '/default-song-cover.png'} alt="Song Cover" />
                                </div>
                                <div class="song-info">
                                    <h4>{currentSong.beatmap.name || 'Unknown Song'}</h4>
                                    <p>{currentSongData.metadata.songAuthorName || 'Unknown Artist'}</p>
                                </div>
                            </div>
                        {:else}
                            <div class="empty-state">
                                <span class="material-icons">music_note</span>
                                <p>No song selected</p>
                            </div>
                        {/if}

                        {#if failedToLoadSongForPlayers}
                            <div class="warning-box">
                                <span class="material-icons">warning</span>
                                <div>
                                    <strong>FAILED TO LOAD SONG FOR ALL PLAYERS!</strong> You can still start the map, but it will not start for those whos state is "Downloading"!
                                </div>
                            </div>
                        {/if}

                        {#if loadingSongForPlayers}
                            <div class="warning-box">
                                <span class="material-icons">warning</span>
                                <div>
                                    <strong>LOADING SONG FOR PLAYERS!</strong> You can still start the map, but it will not start for those whos state is "Downloading"!
                                </div>
                            </div>
                        {/if}

                        {#if isAnyPlayerPlaying}
                            <div class="warning-box">
                                <span class="material-icons">warning</span>
                                <div>
                                    <strong>PLAYERS ARE IN GAME!</strong> Some players are currently in game. Please refrain from loading new songs, starting maps, or exiting this match. You may of course add new maps to the queue etc.
                                </div>
                            </div>
                        {/if}
                        
                        <div class="song-controls">
                            <button class="action-button start-song" on:click={startSong} disabled={!currentSong}>
                                <span class="material-icons">play_arrow</span>
                                Start Song
                            </button>
                            <button 
                                class="action-button start-sync {!streamSyncEnabled ? 'disabled' : ''}" 
                                on:click={startSongWithStreamSync} 
                                disabled={!streamSyncEnabled || !currentSong}
                            >
                                <span class="material-icons">sync</span>
                                Start with Stream Sync
                            </button>
                            {#if isAnyPlayerPlaying}
                                <button class="action-button back-to-menu" on:click={handleBackToMenu}>
                                    <span class="material-icons">home</span>
                                    Back to Menu
                                </button>
                            {/if}
                        </div>
                    </div>

                    <!-- Real Time Score Display -->
                    {#if matchPlayers.some(player => (player.playState) === User_PlayStates.InGame)}
                        <div class="rts-section">
                            <div class="players-list">
                                {#each realTimeScores as rtsPlayer}
                                    <div class="player-card">
                                        <div class="player-status" style="background-color: {getStatusColor(rtsPlayer.player.playState, rtsPlayer.player.downloadState)}"></div>
                                        <img 
                                            src={rtsPlayer.player.discordInfo?.avatarUrl || "https://api.dicebear.com/7.x/identicon/svg?seed=" + rtsPlayer.player.guid} 
                                            alt="Player" 
                                            class="player-avatar"
                                        />
                                        <div class="player-info">
                                            <h4>{rtsPlayer.player.name} ({rtsPlayer.player.discordInfo?.username || 'Discord Unknown'})</h4>
                                            <p class="player-status-text">{(rtsPlayer.recentScore.accuracy * 100).toFixed(2)}%</p>
                                            <p class="player-status-text">{rtsPlayer.recentScore.notesMissed} Misses - {rtsPlayer.recentScore.badCuts} Bad Cuts - {rtsPlayer.recentScore.bombHits} Bomb Hits - {rtsPlayer.recentScore.wallHits} Wall Hits</p>
                                        </div>
                                        <div class="player-info">
                                            <h4>Song Progress: {((rtsPlayer.recentScore.songPosition / currentSongData.metadata.duration) * 100).toFixed(2)}%</h4>
                                            <p>{getTimeStringFromSeconds(rtsPlayer.recentScore.songPosition)} of {getTimeStringFromSeconds(currentSongData.metadata.duration)}</p>
                                        </div>
                                        
                                    </div>
                                {/each}
                            </div>
                        </div>
                    {/if}

                    <!-- Players Section -->
                    <div class="players-section">
                        <h3>Players ({matchPlayers.length})</h3>
                        {#if matchPlayers.length === 0}
                            <div class="empty-state">
                                <span class="material-icons">person_off</span>
                                <p>No players in match</p>
                            </div>
                        {:else}
                            <div class="players-list">
                                {#each matchPlayers as player}
                                    <div class="player-card">
                                        <div class="player-status" style="background-color: {getStatusColor(player.playState, player.downloadState)}"></div>
                                        <img 
                                            src={player.discordInfo?.avatarUrl || "https://api.dicebear.com/7.x/identicon/svg?seed=" + player.guid} 
                                            alt="Player" 
                                            class="player-avatar"
                                        />
                                        <div class="player-info">
                                            <h4>{player.name} ({player.discordInfo?.username || 'Unknown'})</h4>
                                            <p class="player-status-text">{User_DownloadStates[player.downloadState]}</p>
                                            <p class="player-status-text">{User_PlayStates[player.playState]}</p>
                                        </div>
                                        <div style="display: inline-block; margin-right: 0.4rem;">
                                            <button class="kick-button" on:click={() => handleKickPlayer(player)}>
                                                <span class="material-icons">person_remove</span>
                                            </button>
                                            <button class="modify-button" on:click={() => handleModifyGameplayForPlayer(player)}>
                                                <span class="material-icons">settings</span>
                                            </button>
                                        </div>
                                        
                                    </div>
                                {/each}
                            </div>
                        {/if}
                    </div>

                    <!-- Song Upcoming Queue Section -->
                    <div class="queue-section">
                        <SongQueue 
                            bind:maps={upcomingQueueMaps} 
                            on:addMap={handleAddMap} 
                            on:editMap={handleEditMap} 
                            on:removeMap={handleRemoveMapFromQueue}
                            on:songLoad={handleLoadMap}
                            on:addMapsFromTAPool={handleShowAddMapsFromTAPool}
                        />
                    </div>

                    <!-- Previously Played Maps Section -->
                    <div class="queue-section">
                        <PreviouslyPlayedSongs
                            maps={previousMatchResults}
                            on:showMapDetails={showResultsPopupWithOldScores}
                        />
                    </div>
                </div>
            {/if}
        </div>
    </main>
</div>

{#if showMapModal}
    <EditMap
        tournamentGuid={tournamentGuid}
        map={currentlyEditingMap} 
        on:close={closeEditMapModal} 
        on:mapUpdated={handleMapUpdated}
        on:mapAdded={handleMapAdded}
        mode='playing' 
    />
{/if}

<Popup 
    open={showKickConfirmModal || showBackToMenuModal || showExitMatchModal} 
    message={modalMessage} 
    icon={modalIcon}
    iconColor={modalIconColor}
    buttons={modalButtons}
    customImage={modalCustomImage}
    autoClose={modalAutoClose}
/>

<ResultsForMap 
    mapData={showingResultsData}
    isVisible={showResultsPopup && !dontShowResultsPopupForThisSong}
    on:close={handleExitResultsPopupClicked}
/>

{#if allowAddFromTAPool}
    <AddMapsFromTAPool
        isOpen={showAddFromTAPoolPopup}
        mapPools={customTAPools}
        on:close={handleCloseAddMapsFromTAPool}
        on:addMaps={handleAddMapsFromTAPool}
    />
{/if}

<ModifyGameplay
    bind:isVisible={isModifyGameplayPopupVisible}
    playerName={editingPlayerName}
    playerPfp={editingPlayerPfp}
    playerPlatformId={editingPlayerPlatformId}
    on:sendModifyGameplay={handleSendModifyGameplay}
/>

<style>
    .match-dashboard {
        display: flex;
        height: calc(100vh - var(--navbar-height));
    }
    
    .content-area {
        flex: 1;
        padding: 1.5rem;
        overflow-y: auto;
    }
    
    .content-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 1.5rem;
    }
    
    .header-left {
        display: flex;
        align-items: center;
        gap: 1rem;
    }
    
    .back-button {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.5rem 1rem;
        border-radius: 0.375rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
        border: none;
        background-color: var(--bg-tertiary);
        color: var(--text-primary);
    }
    
    .back-button:hover:not(.disabled) {
        background-color: var(--bg-secondary);
    }
    
    .back-button.disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }
    
    .warning-icon {
        color: var(--danger-color);
        font-size: 1rem;
    }
    
    .match-info h2 {
        font-size: 1.75rem;
        font-weight: 600;
        margin: 0;
    }
    
    .tournament-name {
        color: var(--text-secondary);
        font-size: 0.875rem;
        margin: 0;
    }
    
    .actions {
        display: flex;
        gap: 0.75rem;
    }
    
    .action-button {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        padding: 0.5rem 1rem;
        border-radius: 0.375rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
        border: none;
        background-color: var(--bg-tertiary);
        color: var(--text-primary);
    }
    
    .action-button.refresh:hover {
        background-color: var(--bg-secondary);
    }
    
    .action-button.start-song {
        background-color: var(--accent-color);
    }
    
    .action-button.start-song:hover:not(:disabled) {
        background-color: var(--accent-hover);
        box-shadow: 0 0 10px var(--accent-glow);
    }
    
    .action-button.start-sync {
        background-color: #8B5CF6;
    }
    
    .action-button.start-sync:hover:not(.disabled) {
        background-color: #7C3AED;
        box-shadow: 0 0 10px rgba(139, 92, 246, 0.5);
    }
    
    .action-button.back-to-menu {
        background-color: #F59E0B;
    }
    
    .action-button.back-to-menu:hover {
        background-color: #D97706;
        box-shadow: 0 0 10px rgba(245, 158, 11, 0.5);
    }
    
    .action-button.disabled,
    .action-button:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }
    
    /* Match Content Layout */
    .match-content {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-template-rows: auto auto;
        gap: 1.5rem;
    }
    
    .song-section {
        grid-column: 1 / -1;
    }

    .rts-section {
        grid-column: 1 / -1;
    }
    
    .players-section {
        grid-column: 1;
    }
    
    .queue-section {
        grid-column: 2;
        background-color: var(--bg-secondary);
        border-radius: 0.75rem;
        padding-top: 1rem;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        max-height: 20rem;
        overflow-y: scroll;
    }
    
    /* Section Styles */
    .song-section, .players-section {
        background-color: var(--bg-secondary);
        border-radius: 0.75rem;
        padding: 1.5rem;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    }
    
    .song-section h3, .players-section h3, .queue-section h3 {
        font-size: 1.25rem;
        font-weight: 500;
        margin-bottom: 1rem;
    }
    
    /* Current Song */
    .current-song {
        display: flex;
        align-items: center;
        gap: 1rem;
        background-color: var(--bg-tertiary);
        padding: 1rem;
        border-radius: 0.5rem;
        margin-bottom: 1rem;
    }
    
    .song-cover {
        width: 4rem;
        height: 4rem;
        border-radius: 0.5rem;
        overflow: hidden;
        flex-shrink: 0;
    }
    
    .song-cover img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    
    .song-info h4 {
        font-size: 1.125rem;
        font-weight: 500;
        margin: 0 0 0.25rem 0;
    }
    
    .song-info p {
        color: var(--text-secondary);
        margin: 0;
    }
    
    .song-controls {
        display: flex;
        gap: 0.75rem;
        flex-wrap: wrap;
    }
    
    /* Players List */
    .players-list {
        display: flex;
        flex-direction: column;
        gap: 0.75rem;
        max-height: 20rem;
        overflow-y: scroll;
    }
    
    .player-card {
        display: flex;
        align-items: center;
        gap: 1rem;
        padding: 1rem;
        background-color: var(--bg-tertiary);
        border-radius: 0.5rem;
    }
    
    .player-status {
        width: 0.75rem;
        height: 0.75rem;
        border-radius: 50%;
        flex-shrink: 0;
    }
    
    .player-avatar {
        width: 2.5rem;
        height: 2.5rem;
        border-radius: 50%;
        object-fit: cover;
    }
    
    .player-info {
        flex: 1;
    }
    
    .player-info h4 {
        font-size: 1rem;
        font-weight: 500;
        margin: 0 0 0.25rem 0;
    }
    
    .player-status-text {
        color: var(--text-secondary);
        font-size: 0.875rem;
        margin: 0;
    }
    
    .kick-button {
        display: flex;
        align-items: center;
        gap: 0.375rem;
        padding: 0.375rem 0.75rem;
        background-color: var(--danger-color);
        color: white;
        border: none;
        border-radius: 0.375rem;
        font-size: 0.875rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
    }
    
    .kick-button:hover {
        background-color: var(--danger-hover);
        box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
    }

    .modify-button {
        display: flex;
        align-items: center;
        gap: 0.375rem;
        padding: 0.375rem 0.75rem;
        background-color: var(--accent-color);
        color: white;
        border: none;
        border-radius: 0.375rem;
        font-size: 0.875rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
    }
    
    .modify-button:hover {
        background-color: var(--accent-hover);
        box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
    }
    
    /* Empty State */
    .empty-state {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 2rem;
        color: var(--text-secondary);
        gap: 0.75rem;
    }
    
    .empty-state .material-icons {
        font-size: 2.5rem;
        opacity: 0.6;
    }
    
    /* Loading */
    .loading {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 50vh;
        gap: 1rem;
    }
    
    .spinner {
        width: 2.5rem;
        height: 2.5rem;
        border: 3px solid rgba(79, 70, 229, 0.2);
        border-radius: 50%;
        border-top-color: var(--accent-color);
        animation: spin 1s linear infinite;
    }
    
    @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }
    
    /* Error Container */
    .error-container {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 2rem;
        background-color: rgba(239, 68, 68, 0.1);
        border-radius: 0.75rem;
        color: var(--danger-color);
        gap: 0.75rem;
    }
    
    .error-container .material-icons {
        font-size: 2.5rem;
    }
    
    .retry-button {
        padding: 0.5rem 1rem;
        background-color: var(--danger-color);
        color: white;
        border: none;
        border-radius: 0.375rem;
        font-weight: 500;
        cursor: pointer;
        margin-top: 0.75rem;
    }
    
    .retry-button:hover {
        background-color: var(--danger-hover);
    }
    
    /* Modal Styles */
    .modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0, 0, 0, 0.7);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 1000;
    }
    
    .modal {
        background-color: var(--bg-secondary);
        border-radius: 0.75rem;
        max-width: 400px;
        width: 90%;
        box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
    }
    
    .modal-header {
        display: flex;
        align-items: center;
        gap: 0.75rem;
        padding: 1.5rem 1.5rem 1rem 1.5rem;
    }
    
    .modal-header .material-icons.warning {
        color: var(--danger-color);
        font-size: 1.5rem;
    }
    
    .modal-header h3 {
        font-size: 1.25rem;
        font-weight: 500;
        margin: 0;
    }
    
    .modal-content {
        padding: 0 1.5rem 1rem 1.5rem;
    }
    
    .modal-content p {
        color: var(--text-secondary);
        margin: 0;
    }
    
    .modal-actions {
        display: flex;
        gap: 0.75rem;
        padding: 1rem 1.5rem 1.5rem 1.5rem;
        justify-content: flex-end;
    }
    
    .modal-button {
        padding: 0.5rem 1rem;
        border-radius: 0.375rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
        border: none;
    }
    
    .modal-button.cancel {
        background-color: var(--bg-tertiary);
        color: var(--text-primary);
    }
    
    .modal-button.cancel:hover {
        background-color: var(--bg-primary);
    }
    
    .modal-button.confirm {
        background-color: var(--danger-color);
        color: white;
    }
    
    .modal-button.confirm:hover {
        background-color: var(--danger-hover);
    }

    .warning-box {
        border-radius: 0.5rem;
        padding: 1rem;
        margin-bottom: 1.5rem;
        display: flex;
        gap: 1rem;
        align-items: flex-start;
        background-color: rgba(245, 158, 11, 0.1);
        border-left: 4px solid #f59e0b;
    }
    
    /* Material Icons */
    .material-icons {
        font-family: 'Material Icons';
        font-weight: normal;
        font-style: normal;
        font-size: 24px;
        line-height: 1;
        letter-spacing: normal;
        text-transform: none;
        display: inline-block;
        white-space: nowrap;
        word-wrap: normal;
        direction: ltr;
        -webkit-font-feature-settings: 'liga';
        -webkit-font-smoothing: antialiased;
    }
    
    /* Responsive Design */
    @media (max-width: 768px) {
        .match-content {
            grid-template-columns: 1fr;
        }
        
        .players-section {
            grid-column: 1;
        }
        
        .queue-section {
            grid-column: 1;
        }
        
        .song-controls {
            flex-direction: column;
        }
        
        .current-song {
            flex-direction: column;
            text-align: center;
        }
        
        .player-card {
            flex-direction: column;
            text-align: center;
            gap: 0.75rem;
        }
    }
</style>