const logger = require('../utils/logger');
const { EmbedBuilder } = require('discord.js');

/**
 * Manages player instances and track playback using Shoukaku
 * @param {Client} client Discord.js client
 */
class MusicPlayer {
    constructor(client) {
        this.client = client;
        this.players = new Map(); // Store active players
    }

    /**
     * Creates a player for a guild or returns existing one
     * @param {Object} options Options for creating the player
     * @param {string} options.guildId The guild ID
     * @param {string} options.textChannel The text channel ID
     * @param {string} options.voiceChannel The voice channel ID
     * @returns {Object} The player object
     */
    async createPlayer({ guildId, textChannel, voiceChannel }) {
        // Check if player already exists
        if (this.players.has(guildId)) {
            return this.players.get(guildId);
        }

            // Get Shoukaku instance and node
            const shoukaku = this.client.shoukaku; // Get the main shoukaku instance
            const node = shoukaku.options.nodeResolver(shoukaku.nodes);
            if (!node) {
                throw new Error('No available Lavalink nodes!');
            }

            try {
                // Create a new connection to the voice channel using the shoukaku instance
                const connection = await shoukaku.joinVoiceChannel({
                    guildId: guildId,
                    channelId: voiceChannel,
                    shardId: 0, // Assuming shardId 0, adjust if sharding
                deaf: true
            });

            // Create a player object to track state and add methods
            const player = {
                guild: guildId,
                textChannel: textChannel,
                voiceChannel: voiceChannel,
                connection: connection,
                queue: [],
                current: null,
                playing: false,
                volume: 100,

                // Play a track
                async play(track) {
                    this.current = track;
                    
                    // Start playback
                    await this.connection.playTrack({ track: track.encoded });
                    this.playing = true;
                    
                    return this;
                },

                // Stop the current track
                stop() {
                    this.connection.stopTrack();
                    return this;
                },

                // Skip to the next track
                skip() {
                    this.stop();
                    if (this.queue.length > 0) {
                        const nextTrack = this.queue.shift();
                        this.play(nextTrack);
                    } else {
                        this.current = null;
                        this.playing = false;
                    }
                    return this;
                },

                // Set player volume
                setVolume(volume) {
                    this.volume = volume;
                    this.connection.setGlobalVolume(volume);
                    return this;
                },

                // Pause playback
                pause() {
                    this.connection.setPaused(true);
                    return this;
                },

                // Resume playback
                resume() {
                    this.connection.setPaused(false);
                    return this;
                },

                shoukaku: shoukaku, // Store shoukaku instance on the player object

                // Destroy the player and disconnect
                destroy() {
                    // Use the stored Shoukaku instance to leave the channel
                    this.shoukaku.leaveVoiceChannel(this.guild); 
                    // Remove the player instance from the manager's map
                    musicPlayer.players.delete(this.guild); 
                    logger.debug(`Destroyed player for guild ${this.guild}`);
                    return this; // Return this for potential chaining, though unlikely needed here
                },

                // Add a track to the queue or play it if nothing is playing
                async enqueue(track, immediate = false) {
                    if (immediate || (!this.playing && !this.current)) {
                        await this.play(track);
                    } else {
                        this.queue.push(track);
                    }
                    return this;
                }
            };

            // Set up event listeners for this player
            connection.on('start', () => {
                logger.info(`Track started in guild ${player.guild}: ${player.current?.info?.title || 'Unknown'}`);
                
                // Send now playing message
                if (player.current) {
                    const channel = this.client.channels.cache.get(player.textChannel);
                    if (channel) {
                        const embed = new EmbedBuilder()
                            .setColor('#0099ff')
                            .setTitle('Now Playing')
                            .setDescription(`[${player.current.info.title}](${player.current.info.uri})`)
                            .addFields({ name: 'Requested by', value: `${player.current.requester?.tag || 'Unknown'}`, inline: true })
                            .setTimestamp();
                            
                        if (player.current.info.thumbnail) {
                            embed.setThumbnail(player.current.info.thumbnail);
                        }
                        
                        channel.send({ embeds: [embed] }).catch(e => 
                            logger.error(`Failed to send trackStart message: ${e.message}`)
                        );
                    }
                }
            });

            connection.on('end', () => {
                logger.info(`Track ended in guild ${player.guild}: ${player.current?.info?.title || 'Unknown'}`);
                player.playing = false;
                player.current = null;
                
                // Play next track in queue if available
                if (player.queue.length > 0) {
                    const nextTrack = player.queue.shift();
                    player.play(nextTrack);
                } else {
                    // Send queue end message
                    const channel = this.client.channels.cache.get(player.textChannel);
                    if (channel) {
                        channel.send('Queue finished. Add more songs!').catch(e => 
                            logger.error(`Failed to send queueEnd message: ${e.message}`)
                        );
                    }
                    // Optional: Add timeout before disconnecting
                    // setTimeout(() => {
                    //     if (!player.playing) player.destroy();
                    // }, 300000); // 5 minutes
                    player.destroy();
                }
            });

            connection.on('exception', (error) => {
                logger.error(`Track error in guild ${player.guild}: ${error.message}`);
                const channel = this.client.channels.cache.get(player.textChannel);
                if (channel) {
                    channel.send(`An error occurred while trying to play: ${player.current?.info?.title || 'the track'}. 
                        Details: ${error.message || 'Unknown error'}`).catch(e => 
                        logger.error(`Failed to send trackError message: ${e.message}`)
                    );
                }
                player.skip();
            });

            // Store the player and return it
            this.players.set(guildId, player);
            return player;
        } catch (error) {
            logger.error(`Failed to create player for guild ${guildId}: ${error.message}`);
            throw error;
        }
    }

    /**
     * Get an existing player
     * @param {string} guildId The guild ID
     * @returns {Object|null} The player object or null
     */
    getPlayer(guildId) {
        return this.players.get(guildId) || null;
    }

    /**
     * Search for tracks using Shoukaku
     * @param {Object} options Options for the search
     * @param {string} options.query The search query
     * @param {string} options.requester The user who requested the track
     * @returns {Promise<Array>} Array of track objects
     */
    async search({ query, requester }) {
        // Get the first available node
        const node = this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes);
        if (!node) throw new Error('No available Lavalink nodes!');

        try {
            // Determine search type and prepare the identifier string
            let identifier;
            if (query.startsWith('http')) {
                // Direct URL
                identifier = query;
            } else {
                // Search with prefix (Lavalink handles ytsearch/ytmsearch automatically with the plugin)
                // identifier = `ytsearch:${query}`; // Prefix might not be needed with the plugin, let Lavalink decide
                identifier = query; // Pass the raw query for non-URLs
            }

            // Perform the search using the identifier string
            const result = await node.rest.resolve(identifier);
            if (!result || result.loadType === 'error' || result.loadType === 'empty') {
                // Log the identifier for debugging if search fails
                logger.debug(`Search failed for identifier: ${identifier}`); 
                throw new Error(result?.exception?.message || 'No results found');
            }

            // Process results
            let tracks = [];
            if (result.loadType === 'playlist') {
                // Playlist processing
                tracks = result.data.tracks.map(track => ({
                    track: track.encoded,
                    info: track.info,
                    requester: requester
                }));
            } else if (result.loadType === 'track') {
                // Single track
                const track = result.data;
                tracks = [{
                    track: track.encoded,
                    info: track.info,
                    requester: requester
                }];
            } else if (result.loadType === 'search') {
                // Search results
                tracks = result.data.slice(0, 10).map(track => ({
                    track: track.encoded,
                    info: track.info,
                    requester: requester
                }));
            }

            return tracks;
        } catch (error) {
            logger.error(`Search error: ${error.message}`);
            throw error;
        }
    }
}

// Create and export the player manager
const musicPlayer = new MusicPlayer(null);
module.exports = {
    setupPlayer: (client) => {
        if (!client || !client.shoukaku) {
            logger.error("ShoukakuEvents requires a client with an initialized shoukaku instance.");
            return;
        }
        
        // Initialize the player with the client
        musicPlayer.client = client;
        
        logger.info("Shoukaku music player initialized and ready.");
        return musicPlayer;
    },
    musicPlayer
};
