import { VoiceState, ChannelType, PermissionFlagsBits, VoiceChannel, ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, Client, GuildMember } from 'discord.js';
import { prisma } from '../database';
import { logger } from '../utils/logger';


import { ErrorDefs } from '../errors/ErrorCodes';
import { t, SupportedLocale, DEFAULT_LOCALE } from '../i18n';
import { getContextLocale } from '../i18n/localeHelper';
import { auditLogService } from './AuditLogService';

export class VoiceService {
  public static async syncChannels(client: Client) {
    logger.info('VoiceService: Starting channel synchronization...');
    const channels = await prisma.tempVoiceChannel.findMany();

    for (const temp of channels) {
      try {
        const guild = client.guilds.cache.get(temp.guildId) || await client.guilds.fetch(temp.guildId).catch(() => null);
        if (!guild) continue;


        const channel = guild.channels.cache.get(temp.channelId) || await guild.channels.fetch(temp.channelId).catch(() => null);

        if (!channel || channel.type !== ChannelType.GuildVoice) {
          await prisma.tempVoiceChannel.delete({ where: { channelId: temp.channelId } });
          logger.info(`VoiceService: Purged missing Discord channel ${temp.channelId} from DB`);
          continue;
        }

        const voiceChannel = channel as VoiceChannel;
        const humanCount = voiceChannel.members.filter(m => !m.user.bot).size;

        let shouldDelete = false;
        if (temp.deleteWhen === 'EMPTY' && humanCount === 0) {
          shouldDelete = true;
        } else if (temp.deleteWhen === 'OWNER_LEAVE') {
          const ownerInChannel = voiceChannel.members.has(temp.ownerId);
          if (!ownerInChannel) shouldDelete = true;
        }

        if (shouldDelete) {
          await voiceChannel.delete().catch(() => {});
          await prisma.tempVoiceChannel.delete({ where: { channelId: temp.channelId } }).catch(() => {});
          logger.info(`VoiceService: Cleaned up orphaned channel ${voiceChannel.name} during boot`);
        }
      } catch (error) {
        logger.error(`VoiceService: Error syncing channel ${temp.channelId}`, error);
      }
    }
    logger.info('VoiceService: Channel synchronization complete.');

  }
  public static async handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {
    const member = newState.member;
    if (!member) return;

    logger.debug(`VoiceService: handleVoiceStateUpdate - old: ${oldState.channelId}, new: ${newState.channelId}`);

    if (!oldState.channelId && newState.channelId) {
      await this.handleJoin(newState);
    } else if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) {
      await this.handleLeave(oldState);
      await this.handleJoin(newState);
    } else if (oldState.channelId && !newState.channelId) {
      await this.handleLeave(oldState);
    }
  }

  private static async handleJoin(state: VoiceState) {
    const guild = state.guild;
    const member = state.member!;
    const channelId = state.channelId!;

    const generator = await prisma.voiceGenerator.findUnique({
      where: { channelId }
    });

    if (!generator) {
      logger.debug(`VoiceService: handleJoin - channel ${channelId} is NOT a generator`);
      return;
    }

    logger.info(`VoiceService: handleJoin - detected generator join for ${member.user.tag} in ${channelId}`);

    const botMember = guild.members.me;
    if (!botMember?.permissions.has([
      PermissionFlagsBits.ManageChannels,
      PermissionFlagsBits.ManageRoles,
      PermissionFlagsBits.MoveMembers
    ])) {
      logger.error(`${ErrorDefs.BOT_MISSING_VOICE_PERMISSIONS.code}: Bot lacks required Voice permissions in guild ${guild.id}`);
      auditLogService.log(guild, {
        category: 'PERMISSION',
        severity: 'ERROR',
        title: '음성 채널 생성 권한 부족',
        description: `임시 음성 채널 생성에 필요한 권한이 부족하여 생성을 중단했습니다.`,
        fields: [{ name: '누락된 필수 권한', value: '`ManageChannels`, `ManageRoles`, `MoveMembers` 중 하나 이상' }],
        errorCode: ErrorDefs.BOT_MISSING_VOICE_PERMISSIONS.code
      }).catch(() => {});
      return;
    }

    const existingTemp = await prisma.tempVoiceChannel.findFirst({
      where: { guildId: guild.id, ownerId: member.id }
    });

    if (existingTemp) {
      try {
        await member.voice.setChannel(existingTemp.channelId);
        return; // Success, moved to existing channel
      } catch (e: any) {
        // If the channel no longer exists in Discord, clean up DB and proceed to create new one
        if (e.code === 10003 || e.status === 404) {
          logger.warn(`VoiceService: Found ghost channel ${existingTemp.channelId} in DB. Cleaning up and creating fresh one.`);
          await prisma.tempVoiceChannel.delete({ where: { channelId: existingTemp.channelId } }).catch(() => {});
          // FALL THROUGH to channel creation logic below
        } else {
          logger.error(`${ErrorDefs.DISCORD_API_ERROR.code}: Unexpected error moving user to existing voice channel`, e);
          return; // Stop for other errors to prevent spamming creations
        }
      }
    }

    // Resolve locale for this context
    const locale = await getContextLocale(guild.id, member.id);

    // Fetch guild-specific config
    const guildConfig = await prisma.voiceGuildConfig.findUnique({ where: { guildId: guild.id } });
    const profile = await prisma.userVoiceProfile.findUnique({
      where: { userId_guildId: { userId: member.id, guildId: guild.id } }
    });

    // Fallback logic for name resolution
    const effectiveName = this.getEffectiveName(member);
    
    // Naming priority: User Custom Profile > Guild Template > Default I18n
    let channelName: string;
    const profileName = profile?.customName;
    if (profileName) {
      channelName = profileName;
    } else {
      const template = guildConfig?.defaultNameTemplate || t(locale, 'voice.defaultRoomName');
      channelName = template.replace('{{username}}', effectiveName);
    }
    
    // Final safety fallback to avoid undefined
    if (!channelName) channelName = `${effectiveName}'s Room`;

    const userLimit = profile?.userLimit ?? guildConfig?.defaultUserLimit ?? 0;

    try {
      const parentId = generator.categoryId || state.channel?.parentId || undefined;
      
      let newChannel;
      try {
        newChannel = await guild.channels.create({
          name: channelName,
          type: ChannelType.GuildVoice,
          parent: parentId,
          userLimit: userLimit,
          permissionOverwrites: [
            {
              id: guild.roles.everyone.id,
              allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.Connect],
            },
            {
              id: member.id,
              allow: [
                PermissionFlagsBits.ManageChannels,
                PermissionFlagsBits.ManageRoles,
                PermissionFlagsBits.MoveMembers,
              ],
            },
          ],
        });
      } catch (firstError: any) {
        if (firstError.code === 50013) {
          logger.warn(`${ErrorDefs.DISCORD_MISSING_PERMISSIONS.code}: Missing Permissions when creating with overwrites. Bot perms: ${botMember?.permissions.bitfield.toString()}. Retrying without overwrites...`);
          newChannel = await guild.channels.create({
            name: channelName,
            type: ChannelType.GuildVoice,
            parent: parentId,
            userLimit: userLimit,
          });
          logger.info(`Channel created WITHOUT overwrites successfully.`);
        } else {
          throw firstError;
        }
      }

      await prisma.tempVoiceChannel.create({
        data: {
          channelId: newChannel.id,
          guildId: guild.id,
          ownerId: member.id,
        }
      });

      await member.voice.setChannel(newChannel);
      logger.info(`VoiceService: Created ${newChannel.name} for ${member.user.tag}`);
      auditLogService.log(guild, {
        category: 'VOICE',
        severity: 'INFO',
        title: '임시 음성 채널 생성',
        description: `유저 <@${member.id}> 님이 임시 채널을 생성했습니다.`,
        fields: [{ name: '채널', value: `<#${newChannel.id}>`, inline: true }]
      }).catch(() => {});

      await this.sendControlPanel(newChannel, member.id, locale);
    } catch (error) {
      logger.error(`${ErrorDefs.UNKNOWN_ERROR.code}: VoiceService Join Error`, error);
    }
  }

  private static async handleLeave(state: VoiceState) {
    const channelId = state.channelId!;
    const member = state.member!;

    const tempChannel = await prisma.tempVoiceChannel.findUnique({
      where: { channelId }
    });

    if (!tempChannel) return;

    const channel = state.channel as VoiceChannel;
    if (!channel) {
      await prisma.tempVoiceChannel.delete({ where: { channelId } }).catch(() => {});
      return;
    }

    let shouldDelete = false;
    if (tempChannel.deleteWhen === 'EMPTY') {
      const humanCount = channel.members.filter(m => !m.user.bot).size;
      shouldDelete = humanCount === 0;
      logger.info(`VoiceService: [handleLeave] ${channel.name} EMPTY check -> Human Count: ${humanCount}, shouldDelete: ${shouldDelete}`);
    } else if (tempChannel.deleteWhen === 'OWNER_LEAVE') {
      shouldDelete = tempChannel.ownerId === member.id;
      logger.info(`VoiceService: [handleLeave] ${channel.name} OWNER_LEAVE check -> isOwner: ${shouldDelete}`);
    }

    if (shouldDelete) {
      try {
        await channel.delete();
        await prisma.tempVoiceChannel.delete({ where: { channelId } });
        logger.info(`VoiceService: Successfully deleted temp channel ${channel.name}`);
        auditLogService.log(channel.guild, {
          category: 'VOICE',
          severity: 'INFO',
          title: '임시 음성 채널 삭제',
          description: `조건 충족으로 임시 채널이 삭제되었습니다.\n채널 이름: **${channel.name}**`
        }).catch(() => {});
      } catch (error: any) {
        // If already deleted in Discord, just clean up DB
        if (error.code === 10003 || error.status === 404) {
          await prisma.tempVoiceChannel.delete({ where: { channelId } }).catch(() => {});
          logger.info(`VoiceService: Purged ghost channel ${channelId} from DB`);
        } else {
          logger.error(`${ErrorDefs.DISCORD_MISSING_PERMISSIONS.code}: Failed to delete channel ${channelId}`, error);
        }
      }
    } 
    else if (tempChannel.deleteWhen === 'EMPTY' && tempChannel.ownerId === member.id && channel.members.filter(m => !m.user.bot).size > 0) {
      // Auto Transfer Ownership
      const newOwner = channel.members.filter(m => !m.user.bot).first();
      if (newOwner) {
        try {
          await prisma.tempVoiceChannel.update({
            where: { channelId },
            data: { ownerId: newOwner.id }
          });
          
          await this.applyOwnershipTransfer(channel, tempChannel.ownerId, newOwner.id);
          logger.info(`VoiceService: Auto-transferred ownership to ${newOwner.user.tag}`);
        } catch (error) {
          logger.error(`${ErrorDefs.UNKNOWN_ERROR.code}: VoiceService failed to auto-transfer ownership`, error);
        }
      }
    }
  }

  public static async applyOwnershipTransfer(channel: VoiceChannel, oldOwnerId: string, newOwnerId: string) {
    const locale = await getContextLocale(channel.guildId, newOwnerId);

    const profile = await prisma.userVoiceProfile.findUnique({
      where: { userId_guildId: { userId: newOwnerId, guildId: channel.guildId } }
    });
    const newMember = await channel.guild.members.fetch(newOwnerId);
    const finalName = profile?.customName || t(locale, 'voice.defaultRoomName', { username: newMember.user.username });
    
    await channel.setName(finalName).catch(() => {});
    
    // Give Manage Channels permission to new owner, remove from old
    await channel.permissionOverwrites.edit(newOwnerId, {
      ManageChannels: true,
      ManageRoles: true,
      MoveMembers: true
    }).catch(() => {});
    
    await channel.permissionOverwrites.delete(oldOwnerId).catch(() => {});

    // Send new control panel
    await this.sendControlPanel(channel, newOwnerId, locale);
  }

  public static async sendControlPanel(channel: VoiceChannel, ownerId: string, locale?: SupportedLocale) {
    // Resolve locale if not provided
    const resolvedLocale = locale ?? await getContextLocale(channel.guildId, ownerId);

    const selectMenu = new StringSelectMenuBuilder()
      .setCustomId(`vc_control_${ownerId}`)
      .setPlaceholder(t(resolvedLocale, 'voice.controlPanel.placeholder'))
      .addOptions(
        new StringSelectMenuOptionBuilder().setLabel(t(resolvedLocale, 'voice.controlPanel.rename')).setValue('rename').setEmoji('✏️'),
        new StringSelectMenuOptionBuilder().setLabel(t(resolvedLocale, 'voice.controlPanel.limit')).setValue('limit').setEmoji('👥'),
        new StringSelectMenuOptionBuilder().setLabel(t(resolvedLocale, 'voice.controlPanel.lock')).setValue('lock').setEmoji('🔒'),
        new StringSelectMenuOptionBuilder().setLabel(t(resolvedLocale, 'voice.controlPanel.kick')).setValue('kick').setEmoji('👢'),
        new StringSelectMenuOptionBuilder().setLabel(t(resolvedLocale, 'voice.controlPanel.ban')).setValue('ban').setEmoji('🚫'),
        new StringSelectMenuOptionBuilder().setLabel(t(resolvedLocale, 'voice.controlPanel.transfer')).setValue('transfer').setEmoji('👑')
      );

    const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(selectMenu);

    try {
      await channel.send({
        content: t(resolvedLocale, 'voice.channelReady', { owner: `<@${ownerId}>` }),
        components: [row]
      }).catch(() => {});
    } catch (e) {
      logger.error('Failed to send control panel UI', e);
    }
  }

  /**
   * Resolves the member's name based on hierarchy:
   * 1. Server Nickname
   * 2. Global Display Name
   * 3. Username
   * 4. User ID (Fallback)
   */
  public static getEffectiveName(member: GuildMember): string {
    return member.nickname || member.user.globalName || member.user.username || member.id;
  }
}
