import { commandTypeHelpers as ct } from "../../../commandTypes";
import { slowmodeCmd } from "../types";
import { TextChannel } from "eris";
import humanizeDuration from "humanize-duration";
import { sendErrorMessage, sendSuccessMessage } from "src/pluginUtils";
import { asSingleLine, DAYS, disableInlineCode, HOURS, MINUTES } from "src/utils";
import { disableBotSlowmodeForChannel } from "../util/disableBotSlowmodeForChannel";
import { actualDisableSlowmodeCmd } from "../util/actualDisableSlowmodeCmd";
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
import { missingPermissionError } from "../../../utils/missingPermissionError";
import { BOT_SLOWMODE_PERMISSIONS, NATIVE_SLOWMODE_PERMISSIONS } from "../requiredPermissions";

const MAX_NATIVE_SLOWMODE = 6 * HOURS; // 6 hours
const MAX_BOT_SLOWMODE = DAYS * 365 * 100; // 100 years
const MIN_BOT_SLOWMODE = 15 * MINUTES;

const validModes = ["bot", "native"];
type TMode = "bot" | "native";

export const SlowmodeSetCmd = slowmodeCmd({
  trigger: "slowmode",
  permission: "can_manage",
  source: "guild",

  // prettier-ignore
  signature: [
    {
      time: ct.delay(),

      mode: ct.string({ option: true, shortcut: "m" }),
    },
    {
      channel: ct.textChannel(),
      time: ct.delay(),

      mode: ct.string({ option: true, shortcut: "m" }),
    }
  ],

  async run({ message: msg, args, pluginData }) {
    const channel: TextChannel = args.channel || msg.channel;

    if (args.time === 0) {
      // Workaround until we can call SlowmodeDisableCmd from here
      return actualDisableSlowmodeCmd(msg, { channel }, pluginData);
    }

    const defaultMode: TMode =
      pluginData.config.getForChannel(channel).use_native_slowmode && args.time <= MAX_NATIVE_SLOWMODE
        ? "native"
        : "bot";

    const mode = (args.mode as TMode) || defaultMode;
    if (!validModes.includes(mode)) {
      sendErrorMessage(pluginData, msg.channel, "--mode must be 'bot' or 'native'");
      return;
    }

    // Validate durations
    if (mode === "native" && args.time > MAX_NATIVE_SLOWMODE) {
      sendErrorMessage(pluginData, msg.channel, "Native slowmode can only be set to 6h or less");
      return;
    }

    if (mode === "bot" && args.time > MAX_BOT_SLOWMODE) {
      sendErrorMessage(
        pluginData,
        msg.channel,
        `Sorry, bot managed slowmodes can be at most 100 years long. Maybe 99 would be enough?`,
      );
      return;
    }

    if (mode === "bot" && args.time < MIN_BOT_SLOWMODE) {
      sendErrorMessage(
        pluginData,
        msg.channel,
        asSingleLine(`
          Bot managed slowmode must be 15min or more.
          Use \`--mode native\` to use native slowmodes for short slowmodes instead.
        `),
      );
      return;
    }

    // Verify permissions
    const channelPermissions = channel.permissionsOf(pluginData.client.user.id);

    if (mode === "native") {
      const missingPermissions = getMissingPermissions(channelPermissions, NATIVE_SLOWMODE_PERMISSIONS);
      if (missingPermissions) {
        sendErrorMessage(
          pluginData,
          msg.channel,
          `Unable to set native slowmode. ${missingPermissionError(missingPermissions)}`,
        );
        return;
      }
    }

    if (mode === "bot") {
      const missingPermissions = getMissingPermissions(channelPermissions, BOT_SLOWMODE_PERMISSIONS);
      if (missingPermissions) {
        sendErrorMessage(
          pluginData,
          msg.channel,
          `Unable to set bot managed slowmode. ${missingPermissionError(missingPermissions)}`,
        );
        return;
      }
    }

    // Apply the slowmode!
    const rateLimitSeconds = Math.ceil(args.time / 1000);

    if (mode === "native") {
      // If there is an existing bot-maintained slowmode, disable that first
      const existingBotSlowmode = await pluginData.state.slowmodes.getChannelSlowmode(channel.id);
      if (existingBotSlowmode) {
        await disableBotSlowmodeForChannel(pluginData, channel);
      }

      // Set native slowmode
      try {
        await channel.edit({
          rateLimitPerUser: rateLimitSeconds,
        });
      } catch (e) {
        return sendErrorMessage(
          pluginData,
          msg.channel,
          `Failed to set native slowmode: ${disableInlineCode(e.message)}`,
        );
      }
    } else {
      // If there is an existing native slowmode, disable that first
      if (channel.rateLimitPerUser) {
        await channel.edit({
          rateLimitPerUser: 0,
        });
      }

      await pluginData.state.slowmodes.setChannelSlowmode(channel.id, rateLimitSeconds);
    }

    const humanizedSlowmodeTime = humanizeDuration(args.time);
    const slowmodeType = mode === "native" ? "native slowmode" : "bot-maintained slowmode";
    sendSuccessMessage(
      pluginData,
      msg.channel,
      `Set ${humanizedSlowmodeTime} slowmode for <#${channel.id}> (${slowmodeType})`,
    );
  },
});
