const { addToQueue, playNextInQueue } = require('../utils/queueManager');
const { v4: uuidv4 } = require('uuid');
const path = require('path');
const { EmbedBuilder } = require('discord.js');
const fs = require('fs');
const { exec, spawn, execSync } = require('child_process');

const MAX_RETRIES = 3;
const RETRY_DELAY = 1000;

function spawnFFmpegProcess(args, callback, retries = 0) {
    const ffmpegProcess = spawn('ffmpeg', args);

    ffmpegProcess.on('close', (code) => {
        if (code === 0) {
            callback(null);
        } else if (retries < MAX_RETRIES) {
            console.warn(`FFmpeg process failed, retrying (${retries + 1}/${MAX_RETRIES})...`);
            setTimeout(() => {
                spawnFFmpegProcess(args, callback, retries + 1);
            }, RETRY_DELAY);
        } else {
            callback(new Error(`FFmpeg process failed after ${MAX_RETRIES} retries.`));
        }
    });

    ffmpegProcess.on('error', (err) => {
        if (retries < MAX_RETRIES) {
            console.warn(`FFmpeg process error, retrying (${retries + 1}/${MAX_RETRIES})...`, err);
            setTimeout(() => {
                spawnFFmpegProcess(args, callback, retries + 1);
            }, RETRY_DELAY);
        } else {
            callback(err);
        }
    });
}

module.exports = {
    name: 'play',
    description: 'Play a song from YouTube, a URL, or an uploaded media file',
    aliases: ['p'],
    async execute(message, args) {
        const fetch = await import('node-fetch').then(module => module.default);
        const searchQuery = args.join(' ');
        const voiceChannel = message.member.voice.channel;

        console.log(`Received command: play ${searchQuery}`);
        console.log(`Voice channel: ${voiceChannel ? voiceChannel.name : 'None'}`);

        if (!voiceChannel) {
            console.error('User is not in a voice channel.');
            return message.reply('You need to be in a voice channel to play music!');
        }

        let title, tempFilePath, videoUrl = null, thumbnailUrl = null;
        let loadingMessage;

        try {
            const getDuration = (filePath) => {
                try {
                    const output = execSync(`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${filePath}"`).toString().trim();
                    const durationSeconds = parseFloat(output);
                    const minutes = Math.floor(durationSeconds / 60);
                    const seconds = Math.floor(durationSeconds % 60);
                    return `${minutes}:${seconds.toString().padStart(2, '0')}`;
                } catch (error) {
                    console.error('Error getting track duration:', error);
                    return 'Unknown';
                }
            };

            if (message.attachments.size > 0) {
                const attachment = message.attachments.first();
                const attachmentName = attachment.name.toLowerCase();

                if (attachmentName.endsWith('.mp3')) {
                    title = attachment.name;
                    tempFilePath = path.join(__dirname, '../utils/tmp', attachment.name);

                    console.log(`Attachment received: ${title}`);
                    console.log(`Downloading attachment to: ${tempFilePath}`);

                    const response = await fetch(attachment.url);
                    const buffer = await response.buffer();
                    fs.writeFileSync(tempFilePath, buffer);

                    console.log(`Downloaded and saved: ${tempFilePath}`);

                    const duration = getDuration(tempFilePath);

                    const embed = new EmbedBuilder()
                        .setColor('#0099ff')
                        .setTitle('Added To Queue')
                        .setDescription(`**${title}** (${duration})`)
                        .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                        .setTimestamp();

                    message.channel.send({ embeds: [embed] });

                    console.log('Adding to queue and attempting to play.');
                    addToQueue(message.guild.id, tempFilePath, title, voiceChannel, null, message.author.username, message.author.displayAvatarURL(), null);
                    playNextInQueue(message.guild.id);
                    return;
                } else if (attachmentName.endsWith('.mp4') || attachmentName.endsWith('.webm') || attachmentName.endsWith('.mov')) {
                    title = attachment.name;
                    const convertedFileName = `${uuidv4()}.mp3`;
                    tempFilePath = path.join(__dirname, '../utils/tmp', convertedFileName);

                    console.log(`Attachment received: ${title}`);
                    console.log(`Converting and downloading attachment to: ${tempFilePath}`);

                    const response = await fetch(attachment.url);
                    const buffer = await response.buffer();
                    const tempVideoPath = path.join(__dirname, '../utils/tmp', attachment.name);
                    fs.writeFileSync(tempVideoPath, buffer);

                    spawnFFmpegProcess(['-i', tempVideoPath, '-f', 'mp3', '-ab', '192000', '-vn', tempFilePath], (err) => {
                        if (err) {
                            console.error('Error converting file:', err);
                            message.reply('Failed to convert the video file.');
                            return;
                        }

                        console.log(`Converted and saved: ${tempFilePath}`);
                        fs.unlinkSync(tempVideoPath);

                        const duration = getDuration(tempFilePath);

                        const embed = new EmbedBuilder()
                            .setColor('#0099ff')
                            .setTitle('Added To Queue')
                            .setDescription(`**${title}** (${duration})`)
                            .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                            .setTimestamp();

                        message.channel.send({ embeds: [embed] });

                        console.log('Adding to queue and attempting to play.');
                        addToQueue(message.guild.id, tempFilePath, title, voiceChannel, null, message.author.username, message.author.displayAvatarURL(), null);
                        playNextInQueue(message.guild.id);
                    });

                    return;
                } else {
                    console.error('Attachment is not a supported media file.');
                    return message.reply('Only MP3, MP4, WEBM, and MOV files are supported for uploads.');
                }
            }

            if (isValidURL(searchQuery)) {
                if (searchQuery.endsWith('.mp3')) {
                    title = path.basename(searchQuery.split('?')[0]);
                    tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}_${title}`);

                    console.log(`MP3 link received: ${searchQuery}`);
                    console.log(`Downloading MP3 to: ${tempFilePath}`);

                    const response = await fetch(searchQuery);
                    if (!response.ok) throw new Error('Failed to download MP3 file.');
                    const buffer = await response.buffer();
                    fs.writeFileSync(tempFilePath, buffer);

                    console.log(`Downloaded and saved: ${tempFilePath}`);

                    const duration = getDuration(tempFilePath);

                    const embed = new EmbedBuilder()
                        .setColor('#0099ff')
                        .setTitle('Added To Queue')
                        .setDescription(`**${title}** (${duration})`)
                        .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                        .setTimestamp();

                    message.channel.send({ embeds: [embed] });

                    console.log('Adding to queue and attempting to play.');
                    addToQueue(message.guild.id, tempFilePath, title, voiceChannel, null, message.author.username, message.author.displayAvatarURL(), null);
                    playNextInQueue(message.guild.id);
                    return;
                } else if (searchQuery.includes("cdn.discordapp.com")) {
                    title = path.basename(searchQuery.split('?')[0]);
                    const isVideo = searchQuery.endsWith('.mp4') || searchQuery.endsWith('.webm') || searchQuery.endsWith('.mov');
                    const fileExtension = isVideo ? 'mp3' : 'original';
                    const convertedFileName = `${uuidv4()}.${fileExtension}`;
                    tempFilePath = path.join(__dirname, '../utils/tmp', convertedFileName);

                    console.log(`Discord media link received: ${searchQuery}`);
                    console.log(`Downloading media from Discord to: ${tempFilePath}`);

                    const response = await fetch(searchQuery);
                    if (!response.ok) throw new Error('Failed to download media file from Discord.');
                    const buffer = await response.buffer();

                    if (isVideo) {
                        const tempVideoPath = path.join(__dirname, '../utils/tmp', path.basename(searchQuery.split('?')[0]));
                        fs.writeFileSync(tempVideoPath, buffer);

                        spawnFFmpegProcess(['-i', tempVideoPath, '-f', 'mp3', '-ab', '192000', '-vn', tempFilePath], (err) => {
                            if (err) {
                                console.error('Error converting file:', err);
                                message.reply('Failed to convert the video file.');
                                return;
                            }

                            console.log(`Converted and saved: ${tempFilePath}`);
                            fs.unlinkSync(tempVideoPath);

                            const duration = getDuration(tempFilePath);

                            const embed = new EmbedBuilder()
                                .setColor('#0099ff')
                                .setTitle('Added To Queue')
                                .setDescription(`**${title}** (${duration})`)
                                .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                                .setTimestamp();

                            message.channel.send({ embeds: [embed] });

                            console.log('Adding to queue and attempting to play.');
                            addToQueue(message.guild.id, tempFilePath, title, voiceChannel, null, message.author.username, message.author.displayAvatarURL(), null);
                            playNextInQueue(message.guild.id);
                        });

                    } else {
                        fs.writeFileSync(tempFilePath, buffer);
                        console.log(`Downloaded and saved: ${tempFilePath}`);

                        const duration = getDuration(tempFilePath);

                        const embed = new EmbedBuilder()
                            .setColor('#0099ff')
                            .setTitle('Added To Queue')
                            .setDescription(`**${title}** (${duration})`)
                            .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                            .setTimestamp();

                        message.channel.send({ embeds: [embed] });

                        console.log('Adding to queue and attempting to play.');
                        addToQueue(message.guild.id, tempFilePath, title, voiceChannel, null, message.author.username, message.author.displayAvatarURL(), null);
                        playNextInQueue(message.guild.id);
                    }
                    return;
                } else {
                    console.log(`YouTube link received: ${searchQuery}`);
                    videoUrl = searchQuery;

                    exec(`yt-dlp --cookies ${path.join(__dirname, '../cookies.txt')} --print-json --skip-download ${searchQuery}`, async (error, stdout, stderr) => {
                        if (error) {
                            console.error(`Error getting video info: ${error}`);
                            message.reply('Failed to retrieve video info.');
                            return;
                        }

                        const info = JSON.parse(stdout);
                        title = info.title || "Unknown Title";
                        thumbnailUrl = info.thumbnail || null;

                        console.log(`Retrieved title: ${title}`);
                        console.log(`Thumbnail URL: ${thumbnailUrl}`);

                        loadingMessage = await message.channel.send(`**Loading...** ${title}`);

                        tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`);

                        exec(`yt-dlp --cookies ${path.join(__dirname, '../cookies.txt')} --format bestaudio --output "${tempFilePath}" ${searchQuery}`, async (error, stdout, stderr) => {
                            if (error) {
                                console.error(`Error downloading file: ${error}`);
                                message.reply('Failed to download audio file.');
                                return;
                            }

                            console.log(`Downloaded and saved: ${tempFilePath}`);

                            const duration = getDuration(tempFilePath);

                            if (loadingMessage) {
                                await loadingMessage.delete();
                            }

                            const embed = new EmbedBuilder()
                                .setColor('#0099ff')
                                .setTitle('Added To Queue')
                                .setDescription(`**${title}** (${duration})`)
                                .setThumbnail(thumbnailUrl)
                                .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                                .setTimestamp();

                            message.channel.send({ embeds: [embed] });

                            console.log('Adding to queue and attempting to play.');
                            addToQueue(message.guild.id, tempFilePath, title, voiceChannel, videoUrl, message.author.username, message.author.displayAvatarURL(), thumbnailUrl);
                            playNextInQueue(message.guild.id);
                        });
                    });
                }
            } else {
                loadingMessage = await message.channel.send(`Searching for: **${searchQuery}**...`);

                console.log(`Performing YouTube search: ${searchQuery}`);
                exec(`yt-dlp --cookies ${path.join(__dirname, '../cookies.txt')} --dump-single-json "ytsearch:${searchQuery}"`, (error, stdout, stderr) => {
                    if (error) {
                        console.error(`Error searching: ${error}`);
                        message.reply('Failed to search for video.');
                        return;
                    }

                    const info = JSON.parse(stdout);
                    const url = info.entries[0].webpage_url;
                    title = info.entries[0].title;
                    thumbnailUrl = info.entries[0].thumbnail;

                    tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`);
                    console.log(`Downloading file to: ${tempFilePath}`);

                    exec(`yt-dlp --cookies ${path.join(__dirname, '../cookies.txt')} --format bestaudio --output "${tempFilePath}" ${url}`, async (error, stdout, stderr) => {
                        if (error) {
                            console.error(`Error downloading file: ${error}`);
                            message.reply('Failed to download audio file.');
                            return;
                        }

                        console.log(`Downloaded and saved: ${tempFilePath}`);

                        const duration = getDuration(tempFilePath);

                        if (loadingMessage) {
                            await loadingMessage.delete();
                        }

                        const embed = new EmbedBuilder()
                            .setColor('#0099ff')
                            .setTitle('Added To Queue')
                            .setDescription(`**${title}** (${duration})`)
                            .setThumbnail(thumbnailUrl)
                            .setFooter({ text: `Requested by ${message.author.username}`, iconURL: message.author.displayAvatarURL() })
                            .setTimestamp();

                        message.channel.send({ embeds: [embed] });

                        console.log('Adding to queue and attempting to play.');
                        addToQueue(message.guild.id, tempFilePath, title, voiceChannel, url, message.author.username, message.author.displayAvatarURL(), thumbnailUrl);
                        playNextInQueue(message.guild.id);
                    });
                });
            }

        } catch (error) {
            console.error('Error:', error);
            if (loadingMessage) await loadingMessage.delete();
            message.reply('An error occurred while trying to play the music.');
        }
    },
};

function isValidURL(string) {
    try {
        new URL(string);
        return true;
    } catch (_) {
        return false;
    }
}