/* eslint-disable no-continue */
import type { AxiosError, AxiosStatic } from 'axios';
import axios from 'axios';
import { Member, Message } from 'eris';
import { Client, Command, RichEmbed } from '../class';

export default class Apply extends Command {
  public services: Map<string, { description: string, type: 'HARD' | 'SOFT', saaOnly?: boolean, url: string, validation: (...cond: any) => Promise<boolean> | boolean, func?: Function }>;

  constructor(client: Client) {
    super(client);
    this.name = 'apply';
    this.description = 'apply';
    this.usage = `${this.client.config.prefix}apply [serviceName]\n${this.client.config.prefix}apply full`;
    this.permissions = 0;
    this.guildOnly = true;
    this.enabled = true;
    this.setServices();
  }

  protected setServices() {
    this.services = new Map();
    this.services.set('role::proficient', {
      description: 'Proficient role assignment.',
      type: 'HARD',
      saaOnly: true,
      url: 'https://eds.libraryofcode.org/roles/proficient',
      validation: (member: Member) => !member.roles.includes('506943223680466955'),
      func: async (client: Client, ...data: any[]) => {
        const member = await client.guilds.get(client.config.guildID).getRESTMember(data[0]);
        await member.addRole('506943223680466955', 'Proficient Approval from EDS');
      },
    });
    this.services.set('role::regulars', {
      description: 'Regulars role assignment.',
      type: 'HARD',
      saaOnly: true,
      url: 'https://eds.libraryofcode.org/roles/regulars',
      validation: (member: Member) => !member.roles.includes('472524444083159050'),
      func: async (client: Client, ...data: any[]) => {
        const member = await client.guilds.get(client.config.guildID).getRESTMember(data[0]);
        await member.addRole('472524444083159050', 'Regulars Approval from EDS');
      },
    });
    this.services.set('role::constants', {
      description: 'Constants role assignment.',
      type: 'HARD',
      url: 'https://eds.libraryofcode.org/roles/constants',
      validation: (member: Member) => !member.roles.includes('511771731891847168'),
      func: async (client: Client, ...data: any[]) => {
        const member = await client.guilds.get(client.config.guildID).getRESTMember(data[0]);
        await member.addRole('511771731891847168', 'Constants Approval from EDS');
      },
    });

    this.services.set('cs::t2', {
      description: 'Tier 2 upgrade for Cloud Services account.',
      type: 'HARD',
      url: 'https://eds.libraryofcode.org/cs/t2',
      validation: (member: Member) => member.roles.includes('546457886440685578'),
      func: async (client: Client, ...data: any[]) => {
        const member = await client.guilds.get(client.config.guildID).getRESTMember(data[0]);
        const ax = <AxiosStatic>require('axios');
        await ax({
          method: 'get',
          url: `https://api.cloud.libraryofcode.org/wh/t2?userID=${member.id}&auth=${client.config.internalKey}`,
        });
      },
    });

    this.services.set('cs::promot3', {
      description: 'Receive 25% off your first purchase of Tier 3.',
      type: 'SOFT',
      url: 'https://eds.libraryofcode.org/cs/t3-promo',
      validation: async (member: Member) => {
        if (!member.roles.includes('546457886440685578')) return false;
        const customer = await this.client.db.Customer.findOne({ userID: member.user.id }).lean().exec();
        if (!customer) return false;
        return true;
      },
      func: async (client: Client, ...data: any[]) => {
        const member = await client.guilds.get(client.config.guildID).getRESTMember(data[0]);
        const customer = await client.db.Customer.findOne({ userID: member.user.id }).lean().exec();
        const coupon = await client.stripe.coupons.create({
          percent_off: 25,
          duration: 'once',
          max_redemptions: 1,
          name: 'Tier 3 - EDS Discount',
          metadata: {
            userID: member.user.id,
          },
        });
        const promo = await client.stripe.promotionCodes.create({
          coupon: coupon.id,
          customer: customer.cusID,
          max_redemptions: 1,
          restrictions: {
            first_time_transaction: true,
          },
        });
        const doc = new client.db.Promo({
          code: promo.code,
          pID: promo.id,
        });
        await doc.save();
        const chan = await client.getDMChannel(customer.userID);
        chan.createMessage(`__**Tier 3 Coupon Code**__\n\`${promo.code}\`\n\n*Do not share this promotional code with anyone else. This promo code is good for your first purchase of Tier 2, 25% off applied. Will apply to your first invoice only, for more questions contact support.*`);
      },
    });

    this.services.set('p::role::constants', {
      description: 'Pre-approval for Constants role assignment.',
      type: 'SOFT',
      url: 'https://eds.libraryofcode.org/roles/preconstants',
      validation: (member: Member) => !member.roles.includes('511771731891847168'),
    });

    this.services.set('p::cs::t2', {
      description: 'Pre-approval for Tier 2.',
      type: 'SOFT',
      url: 'https://eds.libraryofcode.org/cs/t2pre',
      validation: (member: Member) => member.roles.includes('546457886440685578'),
    });
  }

  public async run(message: Message, args: string[]) {
    try {
      if (!args[0] || args[0] === 'full') {
        const embed = new RichEmbed();
        embed.setTitle('Instant Application Service [IAS]');
        embed.setColor('#556cd6');
        if (args[0] !== 'full') {
          embed.setDescription(`*These applications are specifically targeted to you based on validation conditions. Run \`${this.client.config.prefix}apply full\` for a full list of all applications.*`);
          embed.setThumbnail(message.member.avatarURL);
          embed.setAuthor(message.member.username, message.member.avatarURL);
        }
        for (const service of this.services) {
          // eslint-disable-next-line no-await-in-loop
          const test = await service[1].validation(message.member);
          if (!test && args[0] !== 'full') continue;
          embed.addField(service[0], `**Description**: ${service[1].description}\n**Inquiry Type:** ${service[1].type}\n\n${service[1].saaOnly ? '*This application can only be ran as a Staff-Assisted Application and cannot be ran automatically. Please DM <@457750238208327691> to apply.*' : `*Run \`${this.client.config.prefix}apply ${service[0]}\` to apply.*`}`);
        }
        if (embed.fields?.length <= 0) embed.setDescription(`*We have no offers for you at this time. To see a full list of offers, please run \`${this.client.config.prefix}apply full\`.*`);
        embed.setFooter(this.client.user.username, this.client.user.avatarURL);
        embed.setTimestamp();
        return message.channel.createMessage({ embed });
      }

      if (!this.services.has(args[0])) return this.error(message.channel, 'Invalid service/product name.');
      const service = this.services.get(args[0]);
      const test = await this.services.get(args[0]).validation(message.member);
      if (!test) return this.error(message.channel, 'A condition exists which prevents you from applying, please try again later.');
      if (service.saaOnly) return this.error(message.channel, 'This application can only be ran as a Staff-Assisted Application and cannot be ran automatically. Please DM <@457750238208327691> to apply.');
      const msg = await this.loading(message.channel, 'Thank you for submitting an application. We are currently processing it, you will receive a DM with a decision.');
      return await this.client.queue.processApplication({ channelID: message.channel.id, guildID: this.mainGuild.id, messageID: msg.id }, service.url, message.author.id, service.func ? service.func.toString() : undefined);
    } catch (err) {
      return this.client.util.handleError(err, message, this);
    }
  }

  public static async apply(client: Client, url: string, userID: string) {
    try {
      const { data } = await axios({
        method: 'get',
        url: `${url}?userID=${userID}&auth=${client.config.internalKey}`,
      });

      return {
        status: 'SUCCESS',
        decision: data.decision,
        id: data.id,
        processedBy: data.processedBy,
        token: data.token,
      };
    } catch (err) {
      const error = <AxiosError>err;
      if (error.response?.status === 404 || error.response.status === 400 || error.response.status === 401) return { id: 'N/A', processedBy: 'N/A', status: 'CLIENT_ERROR', decision: 'PRE-DECLINED' };
      return { id: 'N/A', processedBy: 'N/A', status: 'SERVER_ERROR', decision: 'PRE-DECLINED' };
    }
  }
}
