import { ChannelType, PermissionsBitField, VoiceState, Guild, Client } from 'discord.js';
import { settingsStore } from '../config/state';
import { logger } from '../utils/logger';
import { context } from '../config/context';

export class DynamicVoiceService {
  private created = new Map<string, Set<string>>();

  private getSet(guildId: string) {
    if (!this.created.has(guildId)) this.created.set(guildId, new Set());
    return this.created.get(guildId)!;
  }

  private track(guildId: string, channelId: string) {
    this.getSet(guildId).add(channelId);
  }

  private untrack(guildId: string, channelId: string) {
    this.getSet(guildId).delete(channelId);
  }

  private isManaged(guildId: string, channelId?: string | null) {
    if (!channelId) return false;
    return this.getSet(guildId).has(channelId);
  }

  public async handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {
    const guild = newState.guild || oldState.guild;
    if (!guild) return;
    const cfg = settingsStore.get(guild.id);
    const dvCfg = (cfg?.dynamicVoiceConfig as any) || (cfg as any)?.automodConfig?.dynamicVoiceConfig || {};
    const enabled = cfg?.dynamicVoiceEnabled === true || dvCfg.enabled === true;
    const lobbyId = dvCfg.lobbyChannelId;
    if (!enabled || !lobbyId) {
      if (oldState.channelId) await this.cleanupIfEmpty(guild, oldState.channelId);
      return;
    }

    // User joins the lobby
    if (newState.channelId === lobbyId && oldState.channelId !== lobbyId) {
      await this.createAndMove(guild, newState, dvCfg);
    }

    // Clean up old managed channel when empty
    if (oldState.channelId && this.isManaged(guild.id, oldState.channelId)) {
      await this.cleanupIfEmpty(guild, oldState.channelId);
    }
  }

  private async createAndMove(guild: Guild, state: VoiceState, dvCfg: any) {
    try {
      const member = state.member;
      const lobby = state.channel;
      if (!member || !lobby) return;
      const parentId = dvCfg.categoryId || lobby.parentId || undefined;
      const template = dvCfg.template || '{user}s Channel';
      const name = template.replace('{user}', member.displayName || member.user.username);
      const channel = await guild.channels.create({
        name,
        type: ChannelType.GuildVoice,
        parent: parentId ?? null,
        userLimit: dvCfg.userLimit ?? undefined,
        bitrate: dvCfg.bitrate ?? undefined,
        permissionOverwrites: [
          { id: guild.roles.everyone, allow: [PermissionsBitField.Flags.Connect, PermissionsBitField.Flags.ViewChannel] },
          {
            id: member.id,
            allow: [
              PermissionsBitField.Flags.ManageChannels,
              PermissionsBitField.Flags.MoveMembers,
              PermissionsBitField.Flags.MuteMembers,
              PermissionsBitField.Flags.DeafenMembers,
              PermissionsBitField.Flags.Connect,
              PermissionsBitField.Flags.ViewChannel
            ]
          }
        ]
      });
      this.track(guild.id, channel.id);
      context.logging.logSystem(guild, `DynamicVoice erstellt: ${channel.name} (${channel.id})`);
      context.admin.pushGuildLog({
        guildId: guild.id,
        level: 'INFO',
        message: `DynamicVoice erstellt: ${channel.name} (${channel.id})`,
        timestamp: Date.now(),
        category: 'dynamicVoice'
      });
      // Move user if still in lobby
      if (state.channelId === lobby.id) {
        await state.setChannel(channel.id).catch(() => undefined);
      }
    } catch (err) {
      logger.error('Dynamic voice create failed', err);
      context.logging.logSystem(guild, 'DynamicVoice Fehler beim Erstellen');
      context.admin.pushGuildLog({
        guildId: guild.id,
        level: 'ERROR',
        message: 'DynamicVoice Fehler beim Erstellen: ' + (err as any)?.message,
        timestamp: Date.now(),
        category: 'dynamicVoice'
      });
    }
  }

  private async cleanupIfEmpty(guild: Guild, channelId: string) {
    try {
      const channel = await guild.channels.fetch(channelId).catch(() => null);
      if (!channel || channel.type !== ChannelType.GuildVoice) return;
      if (channel.members.size === 0 && this.isManaged(guild.id, channelId)) {
        await channel.delete('Dynamic voice cleanup').catch(() => undefined);
        this.untrack(guild.id, channelId);
        context.logging.logSystem(guild, `DynamicVoice entfernt: ${channel.name} (${channel.id})`);
        context.admin.pushGuildLog({
          guildId: guild.id,
          level: 'INFO',
          message: `DynamicVoice entfernt: ${channel.name} (${channel.id})`,
          timestamp: Date.now(),
          category: 'dynamicVoice'
        });
      }
    } catch (err) {
      logger.error('Dynamic voice cleanup failed', err);
      context.logging.logSystem(guild, 'DynamicVoice Cleanup Fehler');
      context.admin.pushGuildLog({
        guildId: guild.id,
        level: 'ERROR',
        message: 'DynamicVoice Cleanup Fehler: ' + (err as any)?.message,
        timestamp: Date.now(),
        category: 'dynamicVoice'
      });
    }
  }

  public async cleanupGuild(guildId: string, client?: Client | null) {
    const channelIds = Array.from(this.getSet(guildId).values());
    if (!channelIds.length) return;
    if (client) {
      const guild = client.guilds.cache.get(guildId) ?? (await client.guilds.fetch(guildId).catch(() => null));
      if (guild) {
        for (const id of channelIds) {
          const channel = await guild.channels.fetch(id).catch(() => null);
          if (channel && channel.type === ChannelType.GuildVoice && channel.members.size === 0) {
            await channel.delete('Dynamic voice cleanup (module disabled)').catch(() => undefined);
          }
          this.untrack(guildId, id);
        }
        return;
      }
    }
    channelIds.forEach((id) => this.untrack(guildId, id));
  }
}
