/* eslint-disable no-bitwise */
import { Member, User } from 'eris';
import { randomBytes } from 'crypto';
import moment, { unitOfTime } from 'moment';
import { Client, RichEmbed } from '.';
import { Moderation as ModerationModel, ModerationInterface } from '../models';
import { moderation as channels } from '../configs/channels.json';

export default class Moderation {
  public client: Client;

  public logChannels: {
    modlogs: string
  };

  constructor(client: Client) {
    this.client = client;
    this.logChannels = {
      modlogs: channels.modlogs,
    };
  }

  public checkPermissions(member: Member, moderator: Member): boolean {
    if (member.id === moderator.id) return false;
    if (member.roles.some((r) => ['662163685439045632', '455972169449734144', '453689940140883988'].includes(r))) return false;
    const bit = Number(member.permissions.allow);
    if ((bit | 8) === bit) return false;
    if ((bit | 20) === bit) return false;
    return true;
  }

  /**
   * Converts some sort of time based duration to milliseconds based on length.
   * @param time The time, examples: 2h, 1m, 1w
   */
  public convertTimeDurationToMilliseconds(time: string): number {
    const lockLength = time.match(/[a-z]+|[^a-z]+/gi);
    const length = Number(lockLength[0]);
    const unit = lockLength[1] as unitOfTime.Base;
    return moment.duration(length, unit).asMilliseconds();
  }

  public async ban(user: User, moderator: Member, duration: number, reason?: string): Promise<ModerationInterface> {
    if (reason && reason.length > 512) throw new Error('Ban reason cannot be longer than 512 characters');
    await this.client.guilds.get(this.client.config.guildID).banMember(user.id, 7, reason);
    const logID = randomBytes(2).toString('hex');
    const mod = new ModerationModel({
      userID: user.id,
      logID,
      moderatorID: moderator.id,
      reason: reason || null,
      type: 5,
      date: new Date(),
    });
    const now: number = Date.now();
    let date: Date;
    let processed = true;
    if (duration > 0) {
      date = new Date(now + duration);
      processed = false;
    } else date = null;
    const expiration = { date, processed };
    mod.expiration = expiration;

    const embed = new RichEmbed();
    embed.setTitle(`Case ${logID} | Ban`);
    embed.setColor('#e74c3c');
    embed.setAuthor(user.username, user.avatarURL);
    embed.setThumbnail(user.avatarURL);
    embed.addField('User', `<@${user.id}>`, true);
    embed.addField('Moderator', `<@${moderator.id}>`, true);
    if (reason) {
      embed.addField('Reason', reason, true);
    }
    if (date) {
      embed.addField('Expiration', moment(date).calendar(), true);
    }
    embed.setFooter(this.client.user.username, this.client.user.avatarURL);
    embed.setTimestamp();
    this.client.createMessage(this.logChannels.modlogs, { embed });
    return mod.save();
  }

  public async unban(userID: string, moderator: Member, reason?: string): Promise<ModerationInterface> {
    this.client.unbanGuildMember(this.client.config.guildID, userID, reason);
    const user = await this.client.getRESTUser(userID);
    if (!user) throw new Error('Cannot get user.');
    const logID = randomBytes(2).toString('hex');
    const mod = new ModerationModel({
      userID,
      logID,
      moderatorID: moderator.id,
      reason: reason || null,
      type: 4,
      date: new Date(),
    });

    const embed = new RichEmbed();
    embed.setTitle(`Case ${logID} | Unban`);
    embed.setColor('#1abc9c');
    embed.setAuthor(user.username, user.avatarURL);
    embed.setThumbnail(user.avatarURL);
    embed.addField('User', `<@${user.id}>`, true);
    embed.addField('Moderator', `<@${moderator.id}>`, true);
    if (reason) {
      embed.addField('Reason', reason, true);
    }
    embed.setFooter(this.client.user.username, this.client.user.avatarURL);
    embed.setTimestamp();
    this.client.createMessage(this.logChannels.modlogs, { embed });
    return mod.save();
  }

  public async mute(user: User, moderator: Member, duration: number, reason?: string): Promise<ModerationInterface> {
    if (reason && reason.length > 512) throw new Error('Mute reason cannot be longer than 512 characters');
    const member = await this.client.getRESTGuildMember(this.client.config.guildID, user.id);
    if (!member) throw new Error('Cannot find member.');
    await member.addRole('478373942638149643', `Muted by ${moderator.username}#${moderator.discriminator}`);
    const logID = randomBytes(2).toString('hex');
    const mod = new ModerationModel({
      userID: user.id,
      logID,
      moderatorID: moderator.id,
      reason: reason || null,
      type: 2,
      date: new Date(),
    });
    const now: number = Date.now();
    let date: Date;
    let processed = true;
    if (duration > 0) {
      date = new Date(now + duration);
      processed = false;
    } else date = null;
    const expiration = { date, processed };
    mod.expiration = expiration;
    await this.client.db.local.muted.set(`muted-${member.id}`, true);

    const embed = new RichEmbed();
    embed.setTitle(`Case ${logID} | Mute`);
    embed.setColor('#ffff00');
    embed.setAuthor(user.username, user.avatarURL);
    embed.setThumbnail(user.avatarURL);
    embed.addField('User', `<@${user.id}>`, true);
    embed.addField('Moderator', `<@${moderator.id}>`, true);
    if (reason) {
      embed.addField('Reason', reason, true);
    }
    if (date) {
      embed.addField('Expiration', moment(date).calendar(), true);
    }
    embed.setFooter(this.client.user.username, this.client.user.avatarURL);
    embed.setTimestamp();
    this.client.createMessage(this.logChannels.modlogs, { embed });
    return mod.save();
  }

  public async unmute(userID: string, moderator: Member, reason?: string): Promise<ModerationInterface> {
    const member = await this.client.getRESTGuildMember(this.client.config.guildID, userID);
    const user = await this.client.getRESTUser(userID);
    if (member) {
      await member.removeRole('478373942638149643');
    }
    const logID = randomBytes(2).toString('hex');
    const mod = new ModerationModel({
      userID,
      logID,
      moderatorID: moderator.id,
      reason: reason || null,
      type: 1,
      date: new Date(),
    });

    await this.client.db.local.muted.del(`muted-${member.id}`);

    const embed = new RichEmbed();
    embed.setTitle(`Case ${logID} | Unmute`);
    embed.setColor('#1abc9c');
    embed.setAuthor(user.username, user.avatarURL);
    embed.setThumbnail(user.avatarURL);
    embed.addField('User', `<@${user.id}>`, true);
    embed.addField('Moderator', `<@${moderator.id}>`, true);
    if (reason) {
      embed.addField('Reason', reason, true);
    }
    embed.setFooter(this.client.user.username, this.client.user.avatarURL);
    embed.setTimestamp();
    this.client.createMessage(this.logChannels.modlogs, { embed });
    return mod.save();
  }

  public async kick(user: Member | User, moderator: Member, reason?: string): Promise<ModerationInterface> {
    if (reason && reason.length > 512) throw new Error('Kick reason cannot be longer than 512 characters');
    await this.client.guilds.get(this.client.config.guildID).kickMember(user.id, reason);
    const logID = randomBytes(2).toString('hex');
    const mod = new ModerationModel({
      userID: user.id,
      logID,
      moderatorID: moderator.id,
      reason: reason || null,
      type: 5,
      date: new Date(),
    });

    const embed = new RichEmbed();
    embed.setTitle(`Case ${logID} | Kick`);
    embed.setColor('#e74c3c');
    embed.setAuthor(user.username, user.avatarURL);
    embed.setThumbnail(user.avatarURL);
    embed.addField('User', `<@${user.id}>`, true);
    embed.addField('Moderator', `<@${moderator.id}>`, true);
    if (reason) {
      embed.addField('Reason', reason, true);
    }
    embed.setFooter(this.client.user.username, this.client.user.avatarURL);
    embed.setTimestamp();
    this.client.createMessage(this.logChannels.modlogs, { embed });
    return mod.save();
  }
}
