// Tady bude muzika, vole

import { AudioPlayerStatus, VoiceConnectionStatus } from "@discordjs/voice";
import { Client } from "discord.js";
import { search, validate, video_basic_info } from "play-dl";
import ytdlko from "@distube/ytdl-core";
import { emouty } from "../utils/emotes";
import { Modul } from "../utils/types";
import { adminLog, betterFetch, log, messageReply } from "../utils/utils";
import { getConn, Hratelny, novejJoin, novejPlay, Priority, stopPlayer } from "../utils/voice";
import { array, record, safeParse, string } from "valibot";

type QueueItem = {
  name: string;
  url: string;
  special?: true;
};

const guildy = new Map<string, { kju: QueueItem[]; loop: boolean; }>();

const searchScema = array(record(string(), string()));

async function createHratelny(url: string): Promise<Hratelny> {
  /* Verze pro knihovnu play-dl */
  /* const src = await stream(url);

  return {
    src: src.stream,
    volume: 1,
    type: src.type
  };*/

  /* Verze pro ytdl-core */
  const src = ytdlko(url, { filter: "audioonly", highWaterMark: 1 << 25 });

  return {
    src: src,
    volume: 1
  };
}

async function playNext(guildId: string) {
  try {
    const guilda = guildy.get(guildId)!;
    const kju = guilda.kju;
    const item = kju[0];

    let coZahrat: Hratelny;
    if (!item.special) {
      coZahrat = await createHratelny(item.url);
    } else {
      coZahrat = item.url;
    }

    novejPlay(guildId, coZahrat, Priority.Music)
      .then(state => {
        if (state.status != AudioPlayerStatus.Idle) return;

        if (!guilda.loop) kju.shift();

        if (kju.length)
          return playNext(guildId);
      })
      .catch(e => {
        log("chyba v hrani muziki", e);
      });
  } catch (e) {
    if (e instanceof Error && e.message.startsWith("Seeking beyond limit"))
      return stopPlayer(guildId, Priority.Music);

    log(e);
    const client: Client = module.exports.client;
    adminLog(client, "error v hani muziky", e as Error);
  }
}

const exp: Modul = {
  more_komandy: {
    zahraj: {
      DMUnsafe: true,
      run: async (mes, txt) => {
        const kanel = mes.member?.voice.channelId;
        if (!kanel) return "nejsi ve vojsu ty kkt";

        const guildId = mes.guildId!;
        const ajtem: QueueItem = { name: "", url: "" };

        const druh = await validate(txt);
        const soubor = mes.attachments.first();
        if (soubor) {
          ajtem.name = soubor.name;
          ajtem.url = soubor.url;
          ajtem.special = true;
          messageReply(mes, `zahraju \`${soubor.name}\``);
        } else if (txt == "") return "co mam zahrat??";
        else if (!druh) {
          ajtem.name = `nejaka picovina ot ${mes.author}`;
          ajtem.url = txt;
          ajtem.special = true;
          messageReply(mes, `zkusim to zahrat`);
        }
        else if (druh == "search") {
          const msg = messageReply(mes, "hledam");
          const hledani = await search(txt, { limit: 1 });
          if (!hledani[0]) return "nic sem nenašel";
          ajtem.url = hledani[0].url;
          ajtem.name = hledani[0].title!;
          msg.then(m => void m.edit(`zahraju \`${ajtem.name}\``));
        } else if (druh == "yt_video") {
          ajtem.url = txt;
          try {
            const video = await video_basic_info(txt);
            ajtem.name = video.video_details.title!;
          } catch (e) {
            if (!(e instanceof Error)) {
              log("chyba ale nechyba v mzike", e);
              return "neco se fatk posralo";
            }

            if (e.message.includes("age"))
              return "sorka bracho ael tuto je nesjpis ajdzreztrigtnuti";

            log("chyba v hledani muziki", e);

            return "doslo k random chybe";
          }
          if (typeof ajtem.name == "undefined") {
            const client: Client = module.exports.client;
            adminLog(client, "video nemá název");
          }
          messageReply(mes, `zahraju \`${ajtem.name}\``);
        } else return "tuto neumim zahrat";

        if (!guildy.has(guildId)) {
          guildy.set(guildId, { kju: [], loop: false });
        }
        const kju = guildy.get(guildId)!.kju;
        kju.push(ajtem);

        if (kju.length != 1) return;

        await novejJoin(mes.guild!, kanel);
        getConn(guildId)?.once(VoiceConnectionStatus.Destroyed, () => {
          kju.length = 0;
        });
        playNext(guildId);
      }
    },

    queue: {
      als: ["q", "kju"],
      DMUnsafe: true,
      run: mes => {
        const kju = guildy.get(mes.guildId!)?.kju;
        if (!kju?.length) return "nehraje nic";

        return `tuto je kurentni repertoar:\n${kju.reduce((acc, cur, i) => {
          const jmeno = `${cur.name} (<${cur.url}>)`;
          return `${acc}\n${i ? `${i}. ${jmeno}` : `-- *${jmeno}* --`}`;
        }, "")}`;
      }
    },

    skip: {
      DMUnsafe: true,
      run: mes => {
        const kju = guildy.get(mes.guildId!)?.kju;
        if (!kju?.length) return "nehraje nic";

        stopPlayer(mes.guildId!, Priority.Music);
        mes.react(emouty.d3k);
      }
    },

    remove: {
      DMUnsafe: true,
      als: ["odebrat"],
      arg: "pořadový číslo věci, kterou odebrat",
      run: (mes, arg) => {
        const numero = Number(arg);
        if (isNaN(numero)) return "cokundo?";

        const kju = guildy.get(mes.guildId!)?.kju;
        if (!kju?.length) return "nehraje nic";

        if (kju.length <= numero) return "tolik toho nehrae";

        kju.splice(numero, 1);
        mes.react(emouty.d3k);
      }
    },

    seek: "nu tuto us nefunkuje pac ytdl-core je kokotina",

    loop: {
      DMUnsafe: true,
      run: mes => {
        if (!guildy.has(mes.guildId!)) {
          guildy.set(mes.guildId!, { kju: [], loop: false });
        }

        const guilda = guildy.get(mes.guildId!)!;

        guilda.loop = !guilda.loop;

        return `lup je ${guilda.loop ? "zapnutej" : "ukoncen"}`;
      }
    },

    najditext: {
      DMUnsafe: true,
      run: async mes => {
        const kju = guildy.get(mes.guildId!)?.kju;
        if (!kju?.length) return "nehraje nic";

        const nameSplited = kju[0].name.split("-");

        const query = nameSplited[1] ?? kju[0].name;
        const res = await betterFetch(`https://search.karaoketexty.cz/index.php?q=${query}`)
          .then(res => res.text())
          .catch(log);

        const json = res ? JSON.parse(res.slice(1, -1)) : [];

        const vyhledavani = safeParse(searchScema, json);

        if (!vyhledavani.success) return "KaraokeTexty se na mi visarly";

        let vysledek;

        const vyfiltrovano = vyhledavani.output.filter(result => nameSplited.length && result.label.includes(nameSplited[0]) && result.label.includes(nameSplited[1]));

        if (vyfiltrovano.length) {
          vysledek = vyfiltrovano[0];
        } else {
          vysledek = vyhledavani.output[0];
        }

        if (!vysledek) return "KaraokeTexty nic nenasli";

        const res2 = await betterFetch(`https://www.karaoketexty.cz/texty-pisni/h/h-${vysledek.id}`)
          .then(res => res.text())
          .catch(log);

        if (!res2) return "KaraokeTexty mi tekst nedali";

        let text = "";

        for (const radek of res2.matchAll(/<span class="para_1.+?">(.+?)<\/span>/gs)) {
          text += `${radek[1].replace(/<br \/>/g, "")}\n`;
        }

        return text;
      },
    }
  }
};

module.exports = exp;
