const { createAudioPlayer, createAudioResource, AudioPlayerStatus, joinVoiceChannel } = require('@discordjs/voice');
const fs = require('fs');
const { spawn } = require('child_process')

const queueMap = new Map();
const playerMap = new Map();
const currentTrackMap = new Map();
const repeatMap = new Map();
const voiceChannelMap = new Map();

function addToQueue(guildId, filePath, title, voiceChannel, url, requester, avatarURL, thumbnailUrl) {
    if (!queueMap.has(guildId)) {
        queueMap.set(guildId, []);
    }
    queueMap.get(guildId).push({ filePath, title, url, requester, avatarURL, thumbnailUrl });

    if (voiceChannel) {
        voiceChannelMap.set(guildId, voiceChannel);
    }

    const audioPlayer = playerMap.get(guildId);
    const isPlaying = audioPlayer && audioPlayer.state.status === AudioPlayerStatus.Playing;
    const repeat = repeatMap.get(guildId) || false;

    if (!isPlaying) {
        playNextInQueue(guildId);
    }
}

function removeFromQueue(guildId, index) {
    const queue = getQueue(guildId);

    if (index >= 0 && index < queue.length) {
        return queue.splice(index, 1)[0];
    }

    return null;
}

function getQueue(guildId) {
    return queueMap.get(guildId) || [];
}

function getPlayer(guildId) {
    return playerMap.get(guildId);
}

function getCurrentTrack(guildId) {
    return currentTrackMap.get(guildId) || null;
}

function toggleRepeat(guildId) {
    const currentRepeat = repeatMap.get(guildId) || false;
    repeatMap.set(guildId, !currentRepeat);
    return !currentRepeat;
}

function playNextInQueue(guildId) {
    const queue = getQueue(guildId);
    const currentTrack = getCurrentTrack(guildId);
    const repeat = repeatMap.get(guildId);
    const voiceChannel = voiceChannelMap.get(guildId);
    const player = playerMap.get(guildId);
    const isPlaying = player && player.state.status === AudioPlayerStatus.Playing;

    if (!voiceChannel) {
        console.error("Voice channel is undefined. Cannot play track.");
        return false;
    }

    let trackToPlay = null;

    if (currentTrack && repeat && isPlaying) {
        return true;
    } else if (currentTrack && repeat) {
        trackToPlay = currentTrack;
    } else if (queue.length > 0) {
        trackToPlay = queue.shift();
        currentTrackMap.set(guildId, trackToPlay);
    } else if (currentTrack && !repeat) {
        return false;
    } else {
        const connection = playerMap.get(guildId)?._state?.subscription?.connection;
        if (connection && connection.state.status !== 'destroyed') {
            connection.destroy();
        }
        return false;
    }

    playTrack(guildId, voiceChannel, trackToPlay);
}

function playTrack(guildId, voiceChannel, track) {
    const connection = joinVoiceChannel({
        channelId: voiceChannel.id,
        guildId: guildId,
        adapterCreator: voiceChannel.guild.voiceAdapterCreator,
    });

    let audioPlayer = playerMap.get(guildId);

    if (!audioPlayer) {
        audioPlayer = createAudioPlayer();
        playerMap.set(guildId, audioPlayer);

        audioPlayer.on(AudioPlayerStatus.Idle, () => {
            const isRepeatEnabled = repeatMap.get(guildId);
            const queue = getQueue(guildId);
            
            if (!isRepeatEnabled) {
                currentTrackMap.delete(guildId);
                fs.unlink(track.filePath, (err) => {
                    if (err) console.error('Error deleting file:', track.filePath, err);
                });
            }

            if (queue.length > 0) {
                playNextInQueue(guildId);
            } else if (isRepeatEnabled) {
                playNextInQueue(guildId);
            } else {
                if (connection && connection.state.status !== 'destroyed') {
                    connection.destroy();
                }
            }
        });

        audioPlayer.on('error', (err) => {
            console.error('AudioPlayer error:', err);
            currentTrackMap.delete(guildId);
            if (connection && connection.state.status !== 'destroyed') {
                connection.destroy();
            }
        });
    }

    if (track.ffmpegProcess) {
        try {
            track.ffmpegProcess.kill();
            console.log('Killed existing ffmpeg process.');
        } catch (err) {
            console.error('Error killing existing ffmpeg process:', err);
        }
    }

    if (!fs.existsSync(track.filePath)) {
        console.error('Audio file not found:', track.filePath);
        return false;
    }

    track.ffmpegProcess = spawn('ffmpeg', [
        '-i', track.filePath,
        '-analyzeduration', '0',
        '-loglevel', '0',
        '-acodec', 'libopus',
        '-f', 'opus',
        '-ar', '48000',
        '-ac', '2',
        'pipe:1'
    ]);

    const resource = createAudioResource(track.ffmpegProcess.stdout);

    audioPlayer.play(resource);
    connection.subscribe(audioPlayer);

    currentTrackMap.set(guildId, { ...track, resource });
}

function skipTrack(guildId) {
    const player = playerMap.get(guildId);
    const queue = getQueue(guildId);
    const wasRepeatEnabled = repeatMap.get(guildId);

    if (!player) {
        console.error('No player found for this guild.');
        return;
    }

    currentTrackMap.delete(guildId);
    
    if (wasRepeatEnabled) {
        repeatMap.set(guildId, false);
    }
    
    player.stop();

    if (queue.length > 0) {
        playNextInQueue(guildId);
    } else if (wasRepeatEnabled) {
        repeatMap.set(guildId, true);
        const connection = playerMap.get(guildId)?._state?.subscription?.connection;
        if (connection && connection.state.status !== 'destroyed') {
            connection.destroy();
        }
    } else {
        const connection = playerMap.get(guildId)?._state?.subscription?.connection;
        if (connection && connection.state.status !== 'destroyed') {
            connection.destroy();
        }
    }
}

function clearQueue(guildId) {
    queueMap.set(guildId, []);
}

module.exports = { addToQueue, getQueue, getCurrentTrack, playNextInQueue, skipTrack, toggleRepeat, clearQueue, removeFromQueue, getPlayer };