import axios from 'axios';
import moment from 'moment';
import { randomBytes } from 'crypto';
import { AccountInterface } from '../models';
import { Client } from '..';

export const LINUX_USERNAME_REGEX = /^[a-z][-a-z0-9]*$/;

export default class AccountUtil {
  public client: Client;

  constructor(client: Client) {
    this.client = client;
  }

  /**
   * This function creates a new user account.
   * @param data Data/information on the new user account to create.
   * @param data.userID The Discord ID for the user.
   * @param data.username The username for the new user, this will also be their username on the machine.
   * @param data.emailAddress The user's email address.
   * @param moderator The Discord user ID for the Staff member that created the account.
   */
  public async createAccount(data: { userID: string, username: string, emailAddress: string }, moderator: string): Promise<{ account: AccountInterface, tempPass: string }> {
    const moderatorMember = this.client.guilds.cache.get('446067825673633794').members.cache.get(moderator);
    const tempPass = this.client.util.randomPassword();
    const passHash = (await this.client.util.createHash(tempPass)).replace(/[$]/g, '\\$').replace('\n', '');
    const acctName = this.client.users.cache.get(data.userID).username.replace(/[!@#$%^&*(),.?":{}|<>]/g, '-').replace(/\s/g, '-');
    const etcPasswd = `${acctName},${data.userID},,`;
    const code = randomBytes(3).toString('hex').toUpperCase();

    const accountInterface = await this.client.util.createAccount(passHash, etcPasswd, data.username, data.userID, data.emailAddress, moderator, code);
    await this.client.util.createModerationLog(data.userID, moderatorMember.user, 0);
    const req = await axios.get('https://loc.sh/int/directory');
    const find = req.data.find((mem) => mem.userID === moderator);

    this.client.util.transport.sendMail({
      to: data.emailAddress,
      from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',
      replyTo: 'cloud-help@libraryofcode.org',
      subject: 'Approval for CS Account',
      html: `
      <body>
        <style>* {font-family: 'Calibri';}</style>
        <h1>Library of Code | Cloud Services</h1>
        <h2>Congratulations, your CS Account application has been approved. Welcome! Please see below for some details regarding your account and our services</h2>
        <p><b>Username:</b> ${data.username}</p>
        <p><b>Support Key:</b> ${code} || <i>You may be asked for this support key when contacting Library of Code, please keep the code in a safe area.</i></p>
        <p><b>SSH Login:</b> <pre><code style="font-family: Courier;">ssh ${data.username}@cloud.libraryofcode.org</code></pre>
        <p><b>Underwritten by:</b> ${moderatorMember.user.username}, ${find.pn.join(', ')}
        <h2>Useful information</h2>
        <h3>How to log in:</h3>
        <ol>
          <li>Open your desired terminal application - we recommend using <a target="_blank" href="https://git-scm.com/downloads">Bash</a>, but you can use your computer's default</li>
          <li>Type in your SSH Login as above</li>
          <li>When prompted, enter your password <em>Please note that inputs will be blank, so be careful not to type in your password incorrectly</em></li>
        </ol>
        <p>If you fail to authenticate yourself too many times, you will be IP banned and will fail to connect. If this is the case, feel free to DM Ramirez with your <a target="_blank" href="https://whatismyip.com">public IPv4 address</a>.

        <h3>Channels and Links</h3>
        <ul>
          <li><a target="_blank" href="https://status.libraryofcode.org/">Status Page</a> - You can find the status of all our services, including the cloud machine, here.</li>
          <li><a target="_blank" href="https://status.libraryofcode.org/">Wiki</a> - Wiki site, includes information about the Cloud Server.</li>
          <li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/620355063088414769">#cloud-announcements</a> - Announcements regarding the cloud machine will be here. These include planned maintenance, updates to preinstalled services etc.</li>
          <li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/620349128295186472">#cloud-info</a> - Important information you will need to, or should, know to a certain extent.</li>
          <li><a target="_blank" href="https://discordapp.com/channels/446067825673633794/546457788184789013">#cloud-support</a> - A support channel specifically for the cloud machine.</li>
          <li><a target="_blank" href="https://loc.sh/cs-help">Library of Code Support Desk</a> - Our Support desk, you can contact Staff here.</li>
        </ul>
        <h3>Want to support us?</h3>
        <p>You can support us by purchasing a Tier 3 subscription! <a target="_blank" href="https://canary.discord.com/channels/446067825673633794/620355063088414769/774938174001774592">this site</a> for more information.</p>
        <b><i>Library of Code sp-us | Support Team</i></b>
        </body>
      `,
    });
    const guild = this.client.guilds.cache.get('446067825673633794');
    const member = guild.members.cache.get(data.userID);
    await member.roles.add('546457886440685578');
    const user = this.client.users.cache.get(data.userID);
    user.send('<:loc:607695848612167700> **Thank you for creating an account with us!** <:loc:607695848612167700>\n'
    + `Please log into your account by running \`ssh ${data.username}@cloud.libraryofcode.org\` in your terminal, then use the password \`${tempPass}\` to log in.\n`
    + `You will be asked to change your password, \`(current) UNIX password\` is \`${tempPass}\`, then create a password that is at least 12 characters long, with at least one number, special character, and an uppercase letter\n`
    + 'Bear in mind that when you enter your password, it will be blank, so be careful not to type in your password incorrectly.\n\n'
    + 'An email containing some useful information has also been sent.\n'
    + `Your support key is \`${code}\`. Pin this message, you may need this key to contact Library of Code in the future.`).catch();
    return { account: accountInterface, tempPass };
  }

  public async lock(username: string, moderatorID: string, data?: { reason?: string, time?: number }) {
    const account = await this.client.db.Account.findOne({ username });
    if (!account) throw new Error('Account does not exist.');
    if (account.locked) throw new Error('Account is already locked.');
    if (account.username === 'matthew' || account.root) throw new Error('Permission denied.');
    await this.client.util.exec(`lock ${account.username}`);
    await account.updateOne({ locked: true });

    await this.client.util.createModerationLog(account.userID, this.client.users.cache.get(moderatorID), 2, data?.reason, data?.time);

    this.client.util.transport.sendMail({
      to: account.emailAddress,
      from: 'Library of Code sp-us | Cloud Services <help@libraryofcode.org>',
      replyTo: 'cloud-help@libraryofcode.org',
      subject: 'Your account has been locked',
      html: `
      <h1>Library of Code | Cloud Services</h1>
      <p>Your Cloud Account has been locked until ${data?.time ? moment(data?.time).calendar() : 'indefinitely'} under the EULA.</p>
      <p><b>Reason:</b> ${data?.reason ? data.reason : 'none provided'}</p>
      <p><b>Technician:</b> ${moderatorID !== this.client.user.id ? (this.client.users.cache.get(moderatorID).username) : 'SYSTEM'}</p>
      <p><b>Expiration:</b> ${data?.time ? moment(data?.time).format('dddd, MMMM Do YYYY, h:mm:ss A') : 'N/A'}</p>

      <b><i>Library of Code sp-us | Support Team</i></b>
      `,
    });
  }
}