import { AudioPlayer, AudioPlayerStatus, AudioResource, VoiceConnection, createAudioPlayer, createAudioResource, entersState, joinVoiceChannel, VoiceConnectionStatus, getVoiceConnection } from '@discordjs/voice';
import { ChatInputCommandInteraction, GuildMember, TextChannel } from 'discord.js';
import play from 'play-dl';
import { logger } from '../utils/logger';
import { settingsStore } from '../config/state';

export type LoopMode = 'off' | 'song' | 'queue';

interface QueueItem {
  title: string;
  url: string;
  requester: string;
  originalQuery?: string;
}

interface QueueState {
  connection: VoiceConnection;
  player: AudioPlayer;
  channel: TextChannel;
  loop: LoopMode;
  queue: QueueItem[];
  current?: QueueItem;
}

export class MusicService {
  private queues = new Map<string, QueueState>();

  private getQueue(guildId: string) {
    const cfg = settingsStore.get(guildId);
    if (cfg?.musicEnabled === false) return undefined;
    return this.queues.get(guildId);
  }

  private async ensureConnection(interaction: ChatInputCommandInteraction): Promise<VoiceConnection | null> {
    const member = interaction.member as GuildMember;
    const voice = member?.voice?.channel;
    if (!voice || !interaction.guildId) return null;
    const existing = getVoiceConnection(interaction.guildId);
    const connection = existing ?? joinVoiceChannel({
      channelId: voice.id,
      guildId: interaction.guildId,
      adapterCreator: interaction.guild!.voiceAdapterCreator
    });
    try {
      await entersState(connection, VoiceConnectionStatus.Ready, 15000);
      return connection;
    } catch {
      connection.destroy();
      return null;
    }
  }

  public async play(interaction: ChatInputCommandInteraction, query: string) {
    if (!interaction.guildId) return;
    const cfg = settingsStore.get(interaction.guildId);
    if (cfg?.musicEnabled === false) {
      await interaction.reply({ content: 'Musik ist fuer diese Guild deaktiviert.', ephemeral: true });
      return;
    }
    const trimmedQuery = (query || '').trim();
    if (!trimmedQuery) {
      await interaction.reply({ content: 'Bitte gib einen Titel oder Link an.', ephemeral: true });
      return;
    }
    const connection = await this.ensureConnection(interaction);
    if (!connection) {
      await interaction.reply({ content: 'Voice-Verbindung konnte nicht hergestellt werden. Bitte versuche es erneut.', ephemeral: true });
      return;
    }

    let track: { title: string; url: string } | null = null;
    try {
      track = await this.resolveTrack(trimmedQuery);
    } catch (err) {
      logger.error('Music resolve fatal', err);
    }
    if (!track) {
      await interaction.reply({ content: 'Nichts gefunden oder URL ungueltig.', ephemeral: true });
      return;
    }
    if (!/^https?:\/\//i.test(track.url ?? '')) {
      await interaction.reply({ content: 'Der gefundene Link ist ungueltig.', ephemeral: true });
      return;
    }
    const queueItem: QueueItem = { title: track.title ?? 'Unbekannt', url: track.url, requester: interaction.user.tag, originalQuery: trimmedQuery };
    const queue = this.getQueue(interaction.guildId);
    if (!queue) {
      const player = createAudioPlayer();
      const state: QueueState = {
        connection,
        player,
        channel: interaction.channel as TextChannel,
        loop: 'off',
        queue: [queueItem],
        current: undefined
      };
      this.registerPlayer(state, interaction.guildId);
      this.queues.set(interaction.guildId, state);
      await interaction.reply({ content: `Spiele: **${queueItem.title}**` });
      this.processQueue(interaction.guildId);
    } else {
      queue.queue.push(queueItem);
      await interaction.reply({ content: `Zur Queue hinzugefuegt: **${queueItem.title}**` });
    }
  }

  public skip(guildId: string) {
    const queue = this.getQueue(guildId);
    queue?.player.stop(true);
  }

  public stop(guildId: string) {
    const queue = this.getQueue(guildId);
    if (!queue) return;
    queue.queue = [];
    queue.player.stop(true);
    queue.connection.destroy();
    this.queues.delete(guildId);
  }

  public stopAll() {
    for (const guildId of Array.from(this.queues.keys())) {
      this.stop(guildId);
    }
  }

  public pause(guildId: string) {
    this.getQueue(guildId)?.player.pause(true);
  }

  public resume(guildId: string) {
    this.getQueue(guildId)?.player.unpause();
  }

  public setLoop(guildId: string, mode: LoopMode) {
    const queue = this.getQueue(guildId);
    if (!queue) return;
    queue.loop = mode;
  }

  public getQueueInfo(guildId: string) {
    const queue = this.getQueue(guildId);
    return queue
      ? {
          nowPlaying: queue.current,
          upcoming: queue.queue
        }
      : null;
  }

  private async processQueue(guildId: string) {
    const queue = this.getQueue(guildId);
    if (!queue || queue.player.state.status === AudioPlayerStatus.Playing) return;

    let next: QueueItem | undefined = undefined;
    let safety = 0;
    while (safety++ < 5) {
      next = queue.queue.shift();
      if (!next && queue.loop === 'queue' && queue.current) {
        next = queue.current;
      }
      if (!next) break;
      const streamUrlCheck = typeof next.url === 'string' ? next.url.trim() : '';
      if (streamUrlCheck && streamUrlCheck !== 'undefined' && /^https?:\/\//i.test(streamUrlCheck)) {
        break;
      }
      logger.error('Music stream error', { reason: 'invalid_url', item: next });
      queue.channel.send({ content: `Ungueltiger Track-Link, ueberspringe: **${next.title ?? 'Unbekannt'}**.` }).catch(() => undefined);
      next = undefined;
    }
    if (!next) {
      queue.channel.send({ content: 'Queue beendet.' }).catch(() => undefined);
      return;
    }
    const streamUrl = (next.url || '').trim();

    queue.current = next;
    try {
      const kind = await play.validate(streamUrl);
      if (kind !== 'so_track') {
        logger.error('Music stream error', { reason: 'unsupported_url', kind, item: next });
        queue.channel.send({ content: `Nur SoundCloud wird unterstuetzt, ueberspringe: **${next.title ?? 'Unbekannt'}**.` }).catch(() => undefined);
        queue.current = undefined;
        this.processQueue(guildId);
        return;
      }
      const finalUrl = streamUrl;
      if (!finalUrl || !/^https?:\/\//i.test(finalUrl) || finalUrl === 'undefined') throw new Error('soundcloud_url_invalid');
      const stream = await play.stream(finalUrl);
      if (!stream?.stream) throw new Error('stream_invalid');
      const resource: AudioResource = createAudioResource(stream.stream, {
        inputType: stream.type
      });
      queue.player.play(resource);
      queue.connection.subscribe(queue.player);
    } catch (err) {
      logger.error('Music stream error', err);
      queue.channel.send({ content: `Fehler beim Laden von **${next.title}**.` }).catch(() => undefined);
      queue.current = undefined;
      this.processQueue(guildId);
    }
  }

  private registerPlayer(queue: QueueState, guildId: string) {
    queue.player.on(AudioPlayerStatus.Idle, () => {
      if (queue.loop === 'song' && queue.current) {
        queue.queue.unshift(queue.current);
      }
      this.processQueue(guildId);
    });

    queue.connection.on(VoiceConnectionStatus.Disconnected, () => {
      this.queues.delete(guildId);
    });

    queue.player.on('error', (err) => {
      logger.error('Audio player error', err);
      this.processQueue(guildId);
    });
  }

  public getStatus() {
    const sessions = Array.from(this.queues.entries())
      .filter(([guildId]) => settingsStore.get(guildId)?.musicEnabled !== false)
      .map(([guildId, q]) => ({
        guildId,
        nowPlaying: q.current,
        queueLength: q.queue.length,
        loop: q.loop
      }));
    return { activeGuilds: sessions.length, sessions };
  }

  private async resolveTrack(query: string, opts?: { skipPlaylist?: boolean }): Promise<{ title: string; url: string } | null> {
    const trimmed = query.trim();
    if (!trimmed) return null;
    try {
      let validation: string | null = null;
      try {
        validation = await play.validate(trimmed);
      } catch (err) {
        logger.warn('Music validate error', err);
      }
      if (validation === 'so_track') {
        return { title: trimmed, url: trimmed };
      }
      // nur SoundCloud erlaubt, alles andere ignorieren
    } catch (err) {
      logger.error('Music resolve error', err);
    }

    const scSearch = await play.search(trimmed, { source: { soundcloud: 'tracks' }, limit: 1 }).catch((err) => {
      logger.warn('SoundCloud search skipped', err?.message || err);
      return [];
    });
    if (scSearch && scSearch.length) {
      const sc = scSearch[0];
      const url = sc.url || '';
      if (url && /^https?:\/\//i.test(url)) return { title: sc.title ?? 'Unbekannt', url };
    }
    return null;
  }

  private buildVideoUrl(details: any): string | null {
    if (!details) return null;
    const url = details.url || details.permalink;
    if (typeof url === 'string' && /^https?:\/\//i.test(url)) return url;
    if (details.id) return `https://www.youtube.com/watch?v=${details.id}`;
    if (details.videoId) return `https://www.youtube.com/watch?v=${details.videoId}`;
    return null;
  }
}
