/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/

import { APIEmbed, CommandInteractionOption, Message, TextBasedChannel } from "discord.js";
import { CClient, Modul } from "../utils/types";
import { bazenek, log, messageLinks, sendWithoutReply, strankovani } from "../utils/utils";

const zpracovatZpravu = (mes: Message) => new Promise<void>(async (res, rej) => {
  if ([6, 7, 8, 9, 10, 11, 18, 23, 46].includes(mes.type)) return res();
  if (mes.type != 0 && mes.type != 20 && mes.type != 19) throw new Error("Neznamej message type " + mes.type.toString());

  const reference = mes.reference?.messageId;
  const attachments: { name: string; file: string; }[] = [];

  if (mes.attachments.size != 0) {

    for (const [, attachment] of mes.attachments) {
      attachments.push({ name: attachment.name, file: attachment.id });
    }
  }

  const sql = `INSERT INTO Zpravy (ID, Content, Author ${reference ? ", Reference" : ""} , Attachments) VALUES (${mes.id}, ?, ${mes.author.id} ${reference ? `,${reference}` : ""} , ?)`;

  bazenek.query({ sql, values: [mes.content, JSON.stringify(attachments)] }, err => {
    if (err) rej(err);
    res();
  });
});

function utect(string: string) {
  return string
    .replace(/~/g, "~~")
    .replace(/%/g, "~%")
    .replace(/_/g, "~_");
}

const porovnat = (filtr: CommandInteractionOption): string[] => {
  switch (filtr.name) {
    case "kontent": return [`Content LIKE ? ESCAPE "~"`, `%${utect(`${filtr.value}`)}%`];
    case "regex": return [`Content REGEXP ?`, `${filtr.value}`];
    case "uzivatel": return [`Author=?`, `${filtr.user!.id}`];
    case "od":
    case "do": {
      const datum = new Date(filtr.value as string);
      const cislo = (Number(datum) - 1420070400000) * 4194304;
      return [`ID ${filtr.name == "od" ? ">" : "<"} ${cislo}`];
    }
    case "obrasek": return [`${filtr.value ? "" : " NOT "}Attachments REGEXP "\.(png|jpe?g|gif|webp)"`];
    case "obrazekOdkaz": return [`${filtr.value ? "" : " NOT "}Content REGEXP "\.(png|jpe?g|gif|webp)"`];
    case "link": return [`${filtr.value ? "" : " NOT "}Content REGEXP "https?://"`];
    case "ma_soubor": return [`${filtr.value ? "" : " NOT "}Attachments != "[]"`];
    case "nazev_souboru": return [`Attachments LIKE ? ESCAPE "~"`, `%${utect(`${filtr.value}`)}%`];
  }
  return [""];
};

const exp: Modul = {
  more_komandy: {
    vyhledej: {
      slashRun: async int => {
        const moznosti = int.options;
        const filtry = moznosti.data;
        if (!filtry.length) return "co takhle tam alespon jeden filtr";

        // Nelgel kombinace a tak
        if (moznosti.get("kontent") && moznosti.get("regex")) return "kontent a regex nejde kombynovat";
        const od = moznosti.get("od");
        if (od && isNaN(Number(new Date(od.value as string)))) return "od neny platni datum";
        const dod = moznosti.get("do");
        if (dod && isNaN(Number(new Date(dod.value as string)))) return "do neny platni datum";

        const parsedFiltry: string[] = [];
        const mista: string[] = [];
        let seradit = "DESC";

        for (const filtr of filtry) {
          if (filtr.name != "seradit") {
            const [parsed, misto] = porovnat(filtr);
            parsedFiltry.push(parsed);
            if (misto) mista.push(misto);
          } else {
            seradit = filtr.value as string;
          }
        }

        const odpoved = await int.reply("hledam");
        messageLinks.set(odpoved.id, "<nic>"); // Hack, ale já na tohle už nechci šahat

        const sql = `SELECT CONVERT(ID, CHAR) as idcko, SUBSTRING(Content, 1, 20) AS kontent FROM Zpravy WHERE ${parsedFiltry.join(" AND ")} ORDER BY ID ${seradit}`;
        bazenek.query({ sql, values: mista }, (err, res?: { idcko: string; kontent: string; }[]) => {
          if (!int.channel?.isSendable()) return int.reply("PIČOVINA");

          if (err) return odpoved.edit(`chyba: ${err}`);

          if (!res || !res.length) return odpoved.edit("nic takoviho sem nenasel");

          odpoved.edit("nasel sem tuto:");

          let celkovadylka = 0;
          const stranky: string[][] = [[]];
          let aktualniStranka = 0;
          for (const row of res) {
            const vlozka = `[${row.kontent}] https://discord.com/channels/555779161067749446/555779161067749448/${row.idcko}`;

            const dylka = vlozka.length + 3;
            celkovadylka += dylka;
            if (celkovadylka > 1024 || stranky[aktualniStranka].length == 20) {
              aktualniStranka++;
              celkovadylka = dylka;
            }

            const stranka = stranky[aktualniStranka] ??= [];

            stranka.push(vlozka);
          }

          const zacatekNazvu = "visltki";
          const embed: APIEmbed = {
            color: Math.random() * 16777215,
            fields: [{ name: zacatekNazvu, value: `• ${stranky[0].join("\n• ")}` }]
          };

          if (stranky.length == 1) return sendWithoutReply(int.channel, { embeds: [embed] });

          embed.fields![0].name += ` (1/${stranky.length})`;

          strankovani(int.channel!, embed, zacatekNazvu, stranky);
        });
      }
    }
  },

  on_clientReady: () => {
    const client = module.exports.client as CClient;

    if (process.env.noGeneralSync) return;

    client.channels.fetch("555779161067749448")
      .then(async ch => {
        const channel = ch as TextBasedChannel;
        let posledni: string | undefined;

        while (true) {
          const msgs = await (channel.messages.fetch(posledni ? { before: posledni } : undefined));

          for (const [, mes] of msgs) {

            try {
              await zpracovatZpravu(mes);
            } catch (e) {
              if (!(e instanceof Error && e.message.includes("ER_DUP_ENTRY")))
                log(e);
              log("general sync ukoncen");
              return;
            }
          }

          posledni = msgs.last()!.id;

          // Radši
          await new Promise(res => setTimeout(res, 500));
        }

      });
  },

  on_messageCreate: mes => {
    if (!process.env.noGeneralSync && mes.channelId == "555779161067749448")
      zpracovatZpravu(mes);
  }
};

module.exports = exp;
