// Modul dedikovaný funkci spinkáček

import { ChannelType, Message, Presence, Role, TextBasedChannel, User, VoiceState } from "discord.js";
import fetch from "node-fetch";
import { emouty } from "../utils/emotes";
import { Modul, Spinkackar, SRecord } from "../utils/types";
import { formatCas, formatter, log, oddiakritikovat, ping } from "../utils/utils";

let spinkacky: SRecord<Spinkackar>;
const budouciSpinky: SRecord<NodeJS.Timeout> = {};
const autoSpinky: SRecord<[boolean, TextBasedChannel]> = {};

const contactSpinkServer = async (akce: string, id: string, nick: string = "", avatar: string | null = "") => {
  const options = `heslo=${process.env.spinkPass}&akce=${akce}&id=${id}&nick=${encodeURIComponent(nick)}&avatar=${encodeURIComponent(avatar ?? "")}`;
  return fetch(`https://spinkacek.ga/extapi.php?${options}`)
    .then(r => r.text());
};

const syncSpink = () => {
  fetch("https://spinkacek.ga/api/spinkacky")
    .then(r => r.json() as Promise<{ spinkacky: SRecord<Spinkackar>; }>)
    .then(d => {
      const data = d.spinkacky;
      const keys = Object.keys(data);
      const values = Object.values(data);
      spinkacky = {};

      for (let i = 0; i < keys.length; i++) {
        const k = keys[i];
        if (k[0] != "d") continue;
        const vstav = values[i].timeup;
        spinkacky[k.slice(8)] = { spinkacek: values[i].spinkacek, timeup: vstav === false ? false : vstav * 1000 };
      }
    });
};

syncSpink();

const handleSpink = async (act: "spinkacek" | "vstavacek", user: User) => {
  const ok = act == "spinkacek"
    ? await contactSpinkServer(act, user.id, user.username, user.avatarURL())
    : await contactSpinkServer(act, user.id);
  return ok.startsWith("OK") ? ok : false;
};

const ifUzRemove = (m: Message) => {
  const id = m.author.id;
  const uzPgn = budouciSpinky[id];
  if (uzPgn) {
    clearTimeout(uzPgn);
    delete budouciSpinky[id];
    return true;
  }
  return false;
};

const tovjemamvolepreceroliky = async (roliky: User) => {
  const odpoved = await handleSpink("vstavacek", roliky);
  if (!odpoved) return "uz jsi vzhuru ty hajzle";
  const cas = Number(odpoved.slice(3));
  const formatedCas = formatCas(cas);
  let zpr = "";
  if (cas >= 57600) zpr = "mas dat more vstavacek uz kdyz vstanes retarde";
  else if (cas >= 43200) zpr = "extrémní bídák";
  else if (cas >= 36000) zpr = "fakt bídák";
  else if (cas < 30) zpr = "dobrej mikrospánek debile";
  else if (cas < 10800) zpr = "dobrej fake spink debile";

  return `dobry rano hajzle ${roliky}\nspal sy ${formatedCas}\n${zpr}`;
};

function hmsToCislo(reg: RegExpExecArray | null) {
  if (!reg || reg.input == "") return NaN;
  const g = reg.groups!;
  return (Number(g.h) * 3600 || 0) + (Number(g.m) * 60 || 0) + (Number(g.s) || 0);
}

const exp: Modul = {
  more_komandy: {

    spinkacek: {
      als: ["spink", "spoink", "spinkake", "spoinkacek", "gn", emouty.spinkacek, emouty.gn],
      run: async mes => {
        if (mes.author.bot) return `až někdy${emouty.kapp}`;
        if (await handleSpink("spinkacek", mes.author)) {
          mes.react(emouty.spinkacek);
          ifUzRemove(mes);
        }
        else return 'nespis uz?????';
      }
    },

    vstavacek: {
      als: ["vstavcacek", "gm", "unspinkacek"],
      run: async mes => {
        if (mes.author.bot) return emouty.sjeta;

        return tovjemamvolepreceroliky(mes.author);
      }
    },

    pgn: {
      als: [`p${emouty.gn}`],
      run: (mes, kdy) => {
        if (mes.author.bot) return emouty.sjeta;

        const ted = new Date();
        const tedT = Number(ted);
        const spink = new Date();
        let random = false;

        if (kdy.startsWith("random")) {
          random = true;
          let rozdil = 540;
          let min = 60;
          if (kdy.length > 7) {
            const t = kdy.slice(7).replace(/ /g, "").split("-");
            if (t.length != 2) return "cos to napsal kkte";
            const regex = /^(?:(?<h>\d+)h)?(?:(?<m>\d+)m)?(?:(?<s>\d+)s?)?$/;
            const [from, to] = [regex.exec(t[0]), regex.exec(t[1])].map(v => hmsToCislo(v));
            if (Number.isNaN(from) || Number.isNaN(to)) return "cislo musy bit v formate xh xm  xs";
            if (to <= from) return "ja teda nevim jestli ti vys jak funguje cas ael vym ze tam mas chibu";

            rozdil = to - from;
            min = from;
          }
          spink.setTime(tedT + (Math.random() * rozdil + min) * 1000);
          log(`random spink pro ${oddiakritikovat(mes.member?.displayName || "neznamejmeno")} byl nastaven na ${formatter(spink)}`);
        } else if (/^\d+$/.test(kdy) || !kdy)
          spink.setTime(tedT + Number(kdy || 7200) * 1000);
        else {
          const cojavim = /^((?<d>\d+)\. ?((?<mo>\d+)\.)? ?|(?<z>z([ií]|ej)tra) )?(?<h>\d+)(:(?<m>\d+)(:(?<s>\d+))?)? ?(?<p>am|pm)?$/.exec(kdy.toLowerCase());
          if (!cojavim) return `cos to tam napsal  ty kokote?`;

          const g = cojavim.groups!;
          if (g.d) spink.setDate(Number(g.d));
          if (g.z) spink.setDate(ted.getDate() + 1);
          if (g.mo) spink.setMonth(Number(g.mo) - 1);
          const h = Number(g.h);
          spink.setHours(g.p == "pm" && h < 12 ? h + 12 : h);
          spink.setMinutes(Number(g.m) || 0);
          spink.setSeconds(Number(g.s) || 0);
          if (Number(spink) < tedT) return "to uz bylo ty magore";
        }

        ifUzRemove(mes);

        budouciSpinky[mes.author.id] = setTimeout(() => {
          handleSpink("spinkacek", mes.author);
          mes.react(emouty.spinkacek);
        }, Number(spink) - tedT);
        if (!random) return `tvuj spinkacek byl naplanovan na ${formatter(spink)}`;
        mes.react(emouty.lajk);
      }
    },

    ps: {
      als: ["poslednispink"],
      arg: "kdo (nepovinné)",
      run: (mes, arg) => {
        const f = arg != "";
        if (!ping.test(arg) && f) return "sikkt";
        const id = f ? mes.mentions.members?.first()?.id : mes.author.id;
        const tajmUp = spinkacky[id ?? ""]?.timeup;
        if (tajmUp == undefined) return `tuten kkt jeste nespal ${emouty.lukiw}`;
        if (!tajmUp) return "ten kkt jiz ickon spi";
        const cas = Number(new Date()) - Number(new Date(tajmUp));
        return `uz ${f ? "je" : "jsi"} vzhuru ${formatCas(cas / 1000)}`;
      }
    },

    zruspgn: {
      als: ["zpgn", "cancelpgn", "cpgn", "unpgn", `unp${emouty.gn}`],
      run: mes => {
        if (!ifUzRemove(mes)) return `spinkacek ale nemas naplanovanej ty kkte ${mes.author}`;
        return "to bylo teda trapny debile";
      }
    },

    autospink: mes =>
      `autospink je ${autoSpinky[mes.author.id] ? (delete autoSpinky[mes.author.id], "vypnut") : (autoSpinky[mes.author.id] = [true, mes.channel], "zapnut")}`,

    autoautospink: {
      als: ["🚗🚗spink"],
      run: mes =>
        `autoautospink je ${autoSpinky[mes.author.id] ? (delete autoSpinky[mes.author.id], "vypnut") : (autoSpinky[mes.author.id] = [false, mes.channel], "zapnut")}`
    }
  },

  on_voiceStateUpdate: (bef: VoiceState, aft: VoiceState) => {
    if (!aft.channel || bef.channel) return;
    if (spinkacky[aft.id]?.spinkacek) aft.disconnect("spinkacek")
      .catch(err => log("spinkacek odpojit se nepovedlo nebo co:", err));
  },

  super_on_messageCreate: (mes: Message, cmd?: string) => {
    if (mes.author.id === '831318260493844494') {
      syncSpink();
      const uzivatel = mes.content.match(/(?<=discord_)\d+/)?.[0];
      if (!uzivatel) return;
      for (const [, guild] of mes.client.guilds.cache) {
        const member = guild.members.cache.get(uzivatel);
        if (!member) continue;

        const c = guild.roles.cache;
        const spinkRole = c.find(r => r.name == "spink");
        const adminRole = c.find(r => r.name == "admin");

        const make = (akce: "a" | "r", role: Role) => {
          const handle = (e: Error) => {
            if (e.message == "Missing Permissions") return;
            log("chyba pri davani/odebirani role", e);
          };
          if (akce == "a") member.roles.add(role).catch(handle);
          else member.roles.remove(role).catch(handle);
        };

        if (mes.content[0] == "s") {
          if (spinkRole) make("a", spinkRole);
          if (adminRole) make("r", adminRole);
        } else {
          if (spinkRole) make("r", spinkRole);
          if (adminRole) make("a", adminRole);
          continue;
        }
        for (const [, channel] of guild.channels.cache) {
          if (channel.type != ChannelType.GuildVoice) continue;
          for (const [, member] of channel.members) {
            if (member.id !== uzivatel) continue;
            member.voice.disconnect("spinkacek");
            return;
          }
        }
      }
    } else if (spinkacky[mes.author.id]?.spinkacek && cmd != "vstavacek") {
      if (mes.channel.type == ChannelType.DM) return mes.channel.send("drz hubu");

      mes.delete();
      (async () => {
        const chanel = mes.author.dmChannel ?? await mes.author.createDM();
        chanel.send("spis tak nepis");
      })();
      return true;
    }
  },

  on_userPresenceUpdate: async (bef: Presence | null, aft: Presence) => {
    if (!bef) return;
    const befoff = bef.status == "offline";
    const aftoff = aft.status == "offline";
    const prop = autoSpinky[aft.userId];
    if (!prop) return;
    if (!befoff && aftoff) {
      handleSpink("spinkacek", aft.member!.user);
    }
    if (befoff && !aftoff) {
      if (prop[0]) delete autoSpinky[aft.userId];
      const kanel = prop[1];
      if (kanel?.type == ChannelType.GuildText) kanel.send(await tovjemamvolepreceroliky(aft.member!.user));
    }
  }
};

module.exports = exp;
