import {
  SlashCommandBuilder,
  PermissionFlagsBits,
  ChannelType,
  ChatInputCommandInteraction,
  GuildMember,
  VoiceBasedChannel,
} from "discord.js";
import logger from "../utils/logger.js";
import { BotClient } from "../index.js";
import { Player } from "shoukaku";

export default {
  data: new SlashCommandBuilder()
    .setName("join")
    .setDescription("Joins your current voice channel"),
  async execute(_interaction: ChatInputCommandInteraction, _client: BotClient) {
    // Ensure command is run in a guild
    if (!_interaction.guildId || !_interaction.guild || !_interaction.channelId) {
      return _interaction
        .reply({ content: "This command can only be used in a server.", ephemeral: true })
        .catch(() => {});
    }
    // Ensure _interaction.member is a GuildMember
    if (!(_interaction.member instanceof GuildMember)) {
      return _interaction
        .reply({ content: "Could not determine your voice channel.", ephemeral: true })
        .catch(() => {});
    }

    // Use ephemeral deferral
    await _interaction.deferReply({ ephemeral: true });

    const member = _interaction.member; // Already checked it's GuildMember
    const voiceChannel = member?.voice?.channel;

    // 1. Check if user is in a voice channel
    if (!voiceChannel) {
      return _interaction.editReply("You need to be in a voice channel to use this command!");
    }

    // Type assertion for voiceChannel after check
    const currentVoiceChannel = voiceChannel as VoiceBasedChannel;

    // 2. Check bot permissions
    const permissions = currentVoiceChannel.permissionsFor(_client.user!);
    if (!permissions?.has(PermissionFlagsBits.Connect)) {
      return _interaction.editReply("I need permission to **connect** to your voice channel!");
    }
    if (!permissions?.has(PermissionFlagsBits.Speak)) {
      return _interaction.editReply("I need permission to **speak** in your voice channel!");
    }
    // Ensure it's a voice channel (not stage, etc.)
    if (currentVoiceChannel.type !== ChannelType.GuildVoice) {
      return _interaction.editReply("I can only join standard voice channels.");
    }

    // Get the initialized Shoukaku instance from the _client object
    const shoukaku = _client.shoukaku;
    if (!shoukaku) {
      logger.error("Shoukaku instance not found on _client object!");
      return _interaction.editReply("The music player is not ready yet. Please try again shortly.");
    }

    // 3. Get or create the player and connect using Shoukaku
    let player: Player | undefined = shoukaku.players.get(_interaction.guildId);

    // First, ensure clean state by disconnecting if already connected
    if (player) {
      try {
        logger.info(`Destroying existing player for guild ${_interaction.guildId} before reconnecting`);
        await player.destroy();
        player = undefined;
      } catch (error) {
        logger.warn(`Error destroying existing player: ${error}`);
        // Continue with connection attempt anyway
      }
    }

    // Attempt to join voice channel with retry logic
    let attempts = 0;
    const maxAttempts = 3;
    
    while (attempts < maxAttempts) {
      attempts++;
      try {
        // Wait a short time between retries to allow Discord's voice state to update
        if (attempts > 1) {
          await new Promise(resolve => setTimeout(resolve, 1000));
          logger.info(`Attempt ${attempts} to join voice channel ${currentVoiceChannel.id}`);
        }
        
        player = await shoukaku.joinVoiceChannel({
          guildId: _interaction.guildId,
          channelId: currentVoiceChannel.id,
          shardId: _interaction.guild.shardId,
          deaf: true // Set to true to avoid listening to voice data, saves bandwidth
        });

        logger.info(
          `Created player and connected to voice channel ${currentVoiceChannel.name} (${currentVoiceChannel.id}) in guild ${_interaction.guild.name} (${_interaction.guildId})`,
        );
        
        // Connection was successful
        await _interaction.editReply(`Joined ${currentVoiceChannel.name}! Ready to play music.`);
        return;
      } catch (error: unknown) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        logger.error(
          `Attempt ${attempts}: Failed to connect to voice channel for guild ${_interaction.guildId}: ${errorMessage}`,
          error,
        );
        
        // Clean up any partial connections on failure
        try {
          await shoukaku.leaveVoiceChannel(_interaction.guildId);
        } catch (leaveError) {
          // Ignore leave errors
        }
        
        if (attempts === maxAttempts) {
          return _interaction.editReply(`Failed to join voice channel after ${maxAttempts} attempts. Please try again later.`);
        }
      }
    }
  },
};
