import {
  SlashCommandBuilder,
  PermissionFlagsBits,
  ChannelType,
  ChatInputCommandInteraction, // Import the specific _interaction type
  GuildMember, // Import GuildMember type
  VoiceBasedChannel, // Import VoiceBasedChannel type
} from "discord.js";
import logger from "../utils/logger"; // Use default import
import { BotClient } from "../index"; // Import the BotClient interface
import { Player } from "shoukaku"; // Import the Player type explicitly

export default {
  // Use export default for ES Modules
  data: new SlashCommandBuilder()
    .setName("join")
    .setDescription("Joins your current voice channel"),
  async execute(_interaction: ChatInputCommandInteraction, _client: BotClient) {
    // Add types
    // Ensure command is run in a guild
    if (!_interaction.guildId || !_interaction.guild || !_interaction.channelId) {
      // Reply might fail if _interaction is already replied/deferred, use editReply if needed
      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!); // Use non-null assertion for _client.user
    if (!permissions?.has(PermissionFlagsBits.Connect)) {
      // Optional chaining for permissions
      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
    // Correctly get player from the players map and type it
    let player: Player | undefined = shoukaku.players.get(_interaction.guildId);

    if (!player) {
      try {
        // Create player using the Shoukaku manager
        player = await shoukaku.joinVoiceChannel({
          guildId: _interaction.guildId,
          channelId: currentVoiceChannel.id,
          shardId: _interaction.guild.shardId, // Get shardId from guild
        });

        logger.info(
          `Created player and connected to voice channel ${currentVoiceChannel.name} (${currentVoiceChannel.id}) in guild ${_interaction.guild.name} (${_interaction.guildId})`,
        );
        await _interaction.editReply(`Joined ${currentVoiceChannel.name}! Ready to play music.`);
      } catch (error: unknown) {
        // Type error as unknown
        const errorMessage = error instanceof Error ? error.message : String(error);
        logger.error(
          `Failed to create/connect player for guild ${_interaction.guildId}: ${errorMessage}`,
          error,
        );
        // Attempt to leave voice channel if connection failed partially
        shoukaku.leaveVoiceChannel(_interaction.guildId).catch((e: unknown) => {
          // Type catch error
          const leaveErrorMsg = e instanceof Error ? e.message : String(e);
          logger.error(`Error leaving VC after failed join: ${leaveErrorMsg}`);
        });
        return _interaction.editReply("An error occurred while trying to join the voice channel.");
      }
    } else {
      // If player exists, get the corresponding connection
      const connection = shoukaku.connections.get(_interaction.guildId);

      // Check if connection exists and if it's in a different channel
      if (!connection || connection.channelId !== currentVoiceChannel.id) {
        try {
          // Rejoining should handle moving the bot
          // Note: joinVoiceChannel might implicitly destroy the old player/connection if one exists for the guild.
          // If issues arise, explicitly call leaveVoiceChannel first.
          player = await shoukaku.joinVoiceChannel({
            guildId: _interaction.guildId,
            channelId: currentVoiceChannel.id,
            shardId: _interaction.guild.shardId,
          });

          logger.info(
            `Moved player to voice channel ${currentVoiceChannel.name} (${currentVoiceChannel.id}) in guild ${_interaction.guildId}`,
          );
          await _interaction.editReply(`Moved to ${currentVoiceChannel.name}!`);
        } catch (error: unknown) {
          // Type error as unknown
          const errorMessage = error instanceof Error ? error.message : String(error);
          logger.error(
            `Failed to move player for guild ${_interaction.guildId}: ${errorMessage}`,
            error,
          );
          return _interaction.editReply(
            "An error occurred while trying to move to the voice channel.",
          );
        }
      } else {
        // Already in the correct channel
        await _interaction.editReply(`I'm already in ${currentVoiceChannel.name}!`);
      }
      // Example of updating a manually managed text channel context (if needed)
      // if (player.textChannelId !== _interaction.channelId) {
      //    player.textChannelId = _interaction.channelId;
      //    logger.debug(`Updated player text channel context to ${_interaction.channel?.name} (${_interaction.channelId}) in guild ${_interaction.guildId}`);
      // }
    }
  },
};
