do you see any mistakes in this file?

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import axios from 'axios';
import * as qs from 'qs';

admin.initializeApp();

const SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID || "ddb35d8c5a3148869c05b32ff64c057c";
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET || "474ff3197098498296cb6adacd8633b7";
const SPOTIFY_REDIRECT_URI = process.env.SPOTIFY_REDIRECT_URI || "http://localhost:5173/spotify-callback";

if (!SPOTIFY_CLIENT_ID || !SPOTIFY_CLIENT_SECRET || !SPOTIFY_REDIRECT_URI) {
    throw new Error('Spotify credentials are not properly configured in environment variables.');
}

async function getSpotifyTokenInternal(): Promise<string> {
    try {
        const response = await axios.post<any>(
            'https://accounts.spotify.com/api/token',
            qs.stringify({ grant_type: 'client_credentials' }),
            {
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Authorization': 'Basic ' + Buffer.from(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`).toString('base64')
                }
            }
        );

        return response.data.access_token;
    } catch (error) {
        console.error('Error getting Spotify token:', error);
        throw new Error('Failed to get Spotify token');
    }
}

export const createGame = functions.https.onCall(async (data, context) => {
    try {
        const gameRef = await admin.firestore().collection('games').add({
            ...data,
            status: 'lobby',
            createdAt: admin.firestore.FieldValue.serverTimestamp()
        });
        return gameRef.id;
    } catch (error) {
        console.error('Error creating game:', error);
        throw new functions.https.HttpsError('internal', 'Failed to create game');
    }
});

export const joinGame = functions.https.onCall(async (data: { gameId: string, playerName: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId, playerName } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (!gameSnap.exists) {
            throw new functions.https.HttpsError('not-found', 'Game not found');
        }

        const gameData = gameSnap.data();

        if (!gameData) {
            throw new functions.https.HttpsError('not-found', 'Game not found');
        }

        if (gameData.status !== 'lobby') {
            throw new functions.https.HttpsError('failed-precondition', 'Game has already started');
        }

        if (gameData.players.length >= gameData.maxPlayers) {
            throw new functions.https.HttpsError('failed-precondition', 'Game is full');
        }

        await gameRef.update({
            players: admin.firestore.FieldValue.arrayUnion({ id: context.auth.uid, name: playerName })
        });

        return true;
    } catch (error) {
        console.error('Error joining game:', error);
        throw new functions.https.HttpsError('internal', 'Failed to join game');
    }
});

export const startGame = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        await admin.firestore().doc(`games/${gameId}`).update({
            status: 'in_progress',
            startedAt: admin.firestore.FieldValue.serverTimestamp()
        });
        return { success: true, message: 'Game started successfully' };
    } catch (error) {
        console.error('Error starting game:', error);
        throw new functions.https.HttpsError('internal', 'Failed to start the game');
    }
});

export const updateJokerStatus = functions.https.onCall(async (data: { gameId: string, used: boolean }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId, used } = data;

    try {
        await admin.firestore().doc(`games/${gameId}`).update({
            jokerAvailable: !used,
            jokerUsed: used
        });
    } catch (error) {
        console.error('Error updating joker status:', error);
        throw new functions.https.HttpsError('internal', 'Failed to update joker status');
    }
});

export const addPlayerToTeam = functions.https.onCall(async (data: { gameId: string, playerName: string, teamId: number }, context) => {

    const { gameId, playerName, teamId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (!gameSnap.exists) {
            throw new functions.https.HttpsError('not-found', 'Game not found');
        }

        const gameData = gameSnap.data();

        if (!gameData) {
            throw new functions.https.HttpsError('not-found', 'Game not found');
        }

        const newPlayer = { name: playerName };
        const updatedTeams = gameData.teams.map((team: any) =>
            team.id === teamId
                ? { ...team, members: [...team.members, newPlayer] }
                : team
        );

        await gameRef.update({
            players: admin.firestore.FieldValue.arrayUnion(newPlayer),
            teams: updatedTeams
        });
    } catch (error) {
        console.error('Error adding player to team:', error);
        throw new functions.https.HttpsError('internal', 'Failed to add player to team');
    }
});

export const removePlayerFromTeam = functions.https.onCall(async (data: { gameId: string, teamId: number, playerId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId, teamId, playerId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (!gameSnap.exists) {
            throw new functions.https.HttpsError('not-found', 'Game not found');
        }

        const gameData = gameSnap.data();

        if (!gameData) {
            throw new functions.https.HttpsError('not-found', 'Game not found');
        }

        const updatedTeams = gameData.teams.map((team: any) =>
            team.id === teamId
                ? { ...team, members: team.members.filter((member: any) => member.id !== playerId) }
                : team
        );

        await gameRef.update({
            players: gameData.players.filter((player: any) => player.id !== playerId),
            teams: updatedTeams
        });
    } catch (error) {
        console.error('Error removing player from team:', error);
        throw new functions.https.HttpsError('internal', 'Failed to remove player from team');
    }
});

export const startNewRound = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const newRoundNumber = (gameData.currentRound || 0) + 1;

            if (newRoundNumber > gameData.totalRounds) {
                await gameRef.update({ status: 'completed' });
                return { success: true, message: 'Game completed' };
            }

            const searchQuery = getSearchQuery(gameData, newRoundNumber);
            const tracks = await searchSpotifyTracks(searchQuery, 5);

            const roundRef = gameRef.collection('rounds').doc(`round${newRoundNumber}`);
            await roundRef.set({
                roundNumber: newRoundNumber,
                roundName: `Round ${newRoundNumber}`,
                tracks: tracks,
                teamSelections: {},
                teamPoints: {},
                songRankings: []
            });

            await gameRef.update({
                currentRound: newRoundNumber,
                status: 'song_selection',
                jokerAvailable: true,
                jokerUsed: false
            });

            return { success: true, message: 'New round started' };
        }

        throw new functions.https.HttpsError('not-found', 'Game not found');
    } catch (error) {
        console.error('Error starting new round:', error);
        throw new functions.https.HttpsError('internal', 'Failed to start new round');
    }
});

export const getGameSongs = functions.https.onCall(async (data: { gameId: string }, context) => {

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const currentRound = gameData.currentRound;
            const roundRef = gameRef.collection('rounds').doc(`round${currentRound}`);
            const roundSnap = await roundRef.get();

            if (roundSnap.exists) {
                const roundData = roundSnap.data();

                if (!roundData) {
                    throw new functions.https.HttpsError('not-found', 'Round not found');
                }

                return roundData.tracks as any[];
            }
        }

        throw new functions.https.HttpsError('not-found', 'Game or round not found');
    } catch (error) {
        console.error('Error getting game songs:', error);
        throw new functions.https.HttpsError('internal', 'Failed to get game songs');
    }
});

export const searchTracks = functions.https.onCall(async (data: { query: string, limit?: number }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { query, limit = 10 } = data;

    try {
        return await searchSpotifyTracks(query, limit);
    } catch (error) {
        console.error('Error searching tracks:', error);
        throw new functions.https.HttpsError('internal', 'Failed to search tracks');
    }
});

export const getSuggestedSongs = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const currentRound = gameData.currentRound;
            const roundRef = gameRef.collection('rounds').doc(`round${currentRound}`);
            const roundSnap = await roundRef.get();

            if (roundSnap.exists) {
                const roundData = roundSnap.data();

                if (!roundData) {
                    throw new functions.https.HttpsError('not-found', 'Round not found');
                }

                return roundData.tracks as any[];
            }
        }

        throw new functions.https.HttpsError('not-found', 'Game or round not found');
    } catch (error) {
        console.error('Error getting suggested songs:', error);
        throw new functions.https.HttpsError('internal', 'Failed to get suggested songs');
    }
});

export const getCurrentRoundInfo = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();
            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const currentRound = gameData.currentRound;
            const roundRef = gameRef.collection('rounds').doc(`round${currentRound}`);
            const roundSnap = await roundRef.get();

            if (roundSnap.exists) {
                const roundData = roundSnap.data();

                if (!roundData) {
                    throw new functions.https.HttpsError('not-found', 'Round not found');
                }

                return {
                    roundNumber: roundData.roundNumber,
                    roundName: roundData.roundName
                };
            }
        }

        return null;
    } catch (error) {
        console.error('Error getting current round info:', error);
        throw new functions.https.HttpsError('internal', 'Failed to get current round info');
    }
});

export const getRoundTimeLimit = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            return gameData.roundTimeLimit || 60; // Default to 60 seconds if not set
        }

        throw new functions.https.HttpsError('not-found', 'Game not found');
    } catch (error) {
        console.error('Error getting round time limit:', error);
        throw new functions.https.HttpsError('internal', 'Failed to get round time limit');
    }
});

export const getCurrentRound = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            return gameData.currentRound || 0;
        }

        throw new functions.https.HttpsError('not-found', 'Game not found');
    } catch (error) {
        console.error('Error getting current round:', error);
        throw new functions.https.HttpsError('internal', 'Failed to get current round');
    }
});

export const getGameScores = functions.https.onCall(async (data: { gameId: string }, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated', 'The function must be called while authenticated.');
    }

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const teamScores: any[] = gameData.teams.map((team: any) => ({
                teamId: team.id,
                teamName: team.name,
                totalPoints: team.totalPoints || 0
            }));

            const topSongs: any[] = []; // Implement logic to get top songs if needed

            return { teamScores, topSongs };
        }

        throw new functions.https.HttpsError('not-found', 'Game not found');
    } catch (error) {
        console.error('Error getting game scores:', error);
        throw new functions.https.HttpsError('internal', 'Failed to get game scores');
    }
});

export const calculateRoundScores = functions.https.onCall(async (data: { gameId: string }, context) => {

    const { gameId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const currentRound = gameData.currentRound;
            const roundRef = gameRef.collection('rounds').doc(`round${currentRound}`);
            const roundSnap = await roundRef.get();

            if (roundSnap.exists) {
                const roundData = roundSnap.data();

                if (!roundData) {
                    throw new functions.https.HttpsError('not-found', 'Round not found');
                }

                // Calculate scores based on your game logic
                const teamPoints: { [key: number]: number } = {};
                const songRankings = roundData.tracks.map((track: any, index: number) => ({
                    ...track,
                    points: 50 - index * 10 // Example scoring
                }));

                gameData.teams.forEach((team: any) => {
                    const selectedSong = roundData.teamSelections[team.id];
                    const songScore = songRankings.find((song: any) => song.id === selectedSong)?.points || 0;
                    teamPoints[team.id] = songScore;
                });

                // Update round document with scores
                await roundRef.update({
                    teamPoints: teamPoints,
                    songRankings: songRankings
                });

                // Update overall game scores
                const updatedTeams = gameData.teams.map((team: any) => ({
                    ...team,
                    totalPoints: (team.totalPoints || 0) + (teamPoints[team.id] || 0)
                }));

                await gameRef.update({ teams: updatedTeams });

                return { success: true };
            }
        }

        throw new functions.https.HttpsError('not-found', 'Game or round not found');
    } catch (error) {
        console.error('Error calculating round scores:', error);
        throw new functions.https.HttpsError('internal', 'Failed to calculate round scores');
    }
});

// Helper functions

function getSearchQuery(gameData: any, roundNumber: number): string {
    // Implement your logic to generate a search query based on game data and round number
    const themes = ['pop', 'rock', '80s', 'dance', 'love songs'];
    return themes[roundNumber - 1] || 'popular';
}

async function searchSpotifyTracks(query: string, limit: number = 10): Promise<any[]> {
    try {
        const token = await getSpotifyTokenInternal();

        const response = await axios.get('https://api.spotify.com/v1/search', {
            params: {
                q: query,
                type: 'track',
                limit: limit
            },
            headers: {
                'Authorization': `Bearer ${token}`
            }
        });

        return response.data.tracks.items.map((track: any) => ({
            id: track.id,
            name: track.name,
            artist: track.artists[0].name,
            album: track.album.name,
            previewUrl: track.preview_url
        }));
    } catch (error) {
        console.error('Error searching Spotify tracks:', error);
        throw new Error('Failed to search Spotify tracks');
    }
}

export const submitSongSelection = functions.https.onCall(async (data: { gameId: string, teamId: number, songId: string }, context) => {

    const { gameId, teamId, songId } = data;

    try {
        const gameRef = admin.firestore().doc(`games/${gameId}`);
        const gameSnap = await gameRef.get();

        if (gameSnap.exists) {
            const gameData = gameSnap.data();

            if (!gameData) {
                throw new functions.https.HttpsError('not-found', 'Game not found');
            }

            const currentRound = gameData.currentRound;
            const roundRef = gameRef.collection('rounds').doc(`round${currentRound}`);

            await admin.firestore().runTransaction(async (transaction) => {
                const roundDoc = await transaction.get(roundRef);
                const roundData = roundDoc.data();

                if (!roundData) {
                    throw new functions.https.HttpsError('not-found', 'Round not found');
                }

                const updatedSelections = {
                    ...roundData.teamSelections,
                    [teamId]: songId
                };

                transaction.update(roundRef, { teamSelections: updatedSelections });

                // if (Object.keys(updatedSelections).length === gameData.teams.length) {
                //     await calculateRoundScores({ gameId });
                // }
            });

            return { success: true, message: 'Song selection submitted' };
        }

        throw new functions.https.HttpsError('not-found', 'Game not found');
    } catch (error) {
        console.error('Error submitting song selection:', error);
        throw new functions.https.HttpsError('internal', 'Failed to submit song selection');
    }
});

export const voteForSong = functions.https.onCall(async (data: { gameId: string, roundId: string, songId: string, userId: string }, context) => {

    const { gameId, roundId, songId, userId } = data;

    try {
        const roundRef = admin.firestore().doc(`games/${gameId}/rounds/${roundId}`);

        await admin.firestore().runTransaction(async (transaction) => {
            const roundDoc = await transaction.get(roundRef);
            const roundData = roundDoc.data();

            if (!roundData) {
                throw new functions.https.HttpsError('not-found', 'Round not found');
            }

            // Ensure the user hasn't already voted
            if (roundData.votes && roundData.votes[userId]) {
                throw new functions.https.HttpsError('already-exists', 'User has already voted');
            }

            const updatedVotes = {
                ...roundData.votes,
                [userId]: songId
            };

            transaction.update(roundRef, { votes: updatedVotes });

            // Check if voting is complete and trigger score calculation if needed
        });

        return { success: true, message: 'Vote submitted' };
    } catch (error) {
        console.error('Error submitting vote:', error);
        throw new functions.https.HttpsError('internal', 'Failed to submit vote');
    }
});