/* eslint-disable camelcase */
'use strict';

const got = require('got');
require('dotenv').config();
const QRCode = require('qrcode');
const { addEnv, delEnv, getEnvs, getEnvsCount, updateEnv } = require('./ql');
const path = require('path');
const qlDir = process.env.QL_DIR || '/ql';
const notifyFile = path.join(qlDir, 'shell/notify.sh');
const { exec } = require('child_process');
const { GET_RANDOM_TIME_UA } = require('./utils/USER_AGENT');

const api = got.extend({
  retry: { limit: 0 },
  responseType: 'json',
});

module.exports = class User {
  ua;
  pt_key;
  pt_pin;
  cookie;
  eid;
  timestamp;
  nickName;
  token;
  okl_token;
  cookies;
  QRCode;
  remark;
  #s_token;

  constructor({ token, okl_token, cookies, pt_key, pt_pin, cookie, eid, remarks, remark, ua }) {
    this.token = token;
    this.okl_token = okl_token;
    this.cookies = cookies;
    this.pt_key = pt_key;
    this.pt_pin = pt_pin;
    this.cookie = cookie;
    this.eid = eid;
    this.remark = remark;
    this.ua = ua;

    if (pt_key && pt_pin) {
      this.cookie = 'pt_key=' + this.pt_key + ';pt_pin=' + this.pt_pin + ';';
    }

    if (cookie) {
      this.pt_pin = cookie.match(/pt_pin=(.*?);/)[1];
      this.pt_key = cookie.match(/pt_key=(.*?);/)[1];
    }

    if (remarks) {
      this.remark = remarks.match(/remark=(.*?);/) && remarks.match(/remark=(.*?);/)[1];
    }
  }

  async getQRConfig() {
    this.ua = this.ua || process.env.NINJA_UA || GET_RANDOM_TIME_UA();
    const taskUrl = `https://plogin.m.jd.com/cgi-bin/mm/new_login_entrance?lang=chs&appid=300&returnurl=https://wq.jd.com/passport/LoginRedirect?state=${Date.now()}&returnurl=https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport`;
    const response = await api({
      url: taskUrl,
      headers: {
        Connection: 'Keep-Alive',
        'Content-Type': 'application/x-www-form-urlencoded',
        Accept: 'application/json, text/plain, */*',
        'Accept-Language': 'zh-cn',
        Referer: taskUrl,
        'User-Agent': this.ua,
        Host: 'plogin.m.jd.com',
      },
    });
    const headers = response.headers;
    const data = response.body;
    await this.#formatSetCookies(headers, data);

    if (!this.#s_token) {
      throw new Error('二维码创建失败!');
    }

    const nowTime = Date.now();
    // eslint-disable-next-line prettier/prettier
    const taskPostUrl = `https://plogin.m.jd.com/cgi-bin/m/tmauthreflogurl?s_token=${
      this.#s_token
    }&v=${nowTime}&remember=true`;

    const configRes = await api({
      method: 'post',
      url: taskPostUrl,
      body: `lang=chs&appid=300&source=wq_passport&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=${nowTime}&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action`,
      headers: {
        Connection: 'Keep-Alive',
        'Content-Type': 'application/x-www-form-urlencoded',
        Accept: 'application/json, text/plain, */*',
        'Accept-Language': 'zh-cn',
        Referer: taskUrl,
        'User-Agent': this.ua,
        Host: 'plogin.m.jd.com',
        Cookie: this.cookies,
      },
    });
    const configHeaders = configRes.headers;
    const configData = configRes.body;

    this.token = configData.token;
    if (this.token)
      this.QRCode = await QRCode.toDataURL(
        `https://plogin.m.jd.com/cgi-bin/m/tmauth?appid=300&client_type=m&token=${this.token}`
      );
    const cookies = configHeaders['set-cookie'][0];
    this.okl_token = cookies.substring(cookies.indexOf('=') + 1, cookies.indexOf(';'));
  }

  async checkQRLogin() {
    if (!this.token || !this.okl_token || !this.cookies) {
      throw new Error('初始化登录请求失败!');
    }
    const nowTime = Date.now();
    const loginUrl = `https://plogin.m.jd.com/login/login?appid=300&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=${nowTime}&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action&source=wq_passport`;
    const getUserCookieUrl = `https://plogin.m.jd.com/cgi-bin/m/tmauthchecktoken?&token=${this.token}&ou_state=0&okl_token=${this.okl_token}`;
    const response = await api({
      method: 'POST',
      url: getUserCookieUrl,
      body: `lang=chs&appid=300&source=wq_passport&returnurl=https://wqlogin2.jd.com/passport/LoginRedirect?state=${nowTime}&returnurl=//home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&/myJd/home.action`,
      headers: {
        Connection: 'Keep-Alive',
        'Content-Type': 'application/x-www-form-urlencoded; Charset=UTF-8',
        Accept: 'application/json, text/plain, */*',
        'Accept-Language': 'zh-cn',
        Referer: loginUrl,
        'User-Agent': this.ua,
        Cookie: this.cookies,
      },
    });
    const data = response.body;
    const headers = response.headers;
    if (data.errcode === 0) {
      const pt_key = headers['set-cookie'][1];
      this.pt_key = pt_key.substring(pt_key.indexOf('=') + 1, pt_key.indexOf(';'));
      const pt_pin = headers['set-cookie'][2];
      this.pt_pin = pt_pin.substring(pt_pin.indexOf('=') + 1, pt_pin.indexOf(';'));
      this.cookie = 'pt_key=' + this.pt_key + ';pt_pin=' + this.pt_pin + ';';

      const result = await this.CKLogin();
      result.errcode = 0;
      return result;
    }

    return {
      errcode: data.errcode,
      message: data.message,
    };
  }

  async CKLogin() {
    let message;
    await this.#getNickname();
    const envs = await getEnvs();
    const poolInfo = await User.getPoolInfo();
    const env = await envs.find((item) => item.value.match(/pt_pin=(.*?);/)[1] === this.pt_pin);
    if (!env) {
      // 新用户
      if (!poolInfo.allowAdd) {
        throw new UserError('管理员已关闭注册,去其他地方看看吧', 210, 200);
      } else if (poolInfo.marginCount === 0) {
        throw new UserError('本站已到达注册上限,你来晚啦', 211, 200);
      } else {
        const remarks = `remark=${this.nickName};`;
        const body = await addEnv(this.cookie, remarks);
        if (body.code !== 200) {
          throw new UserError(body.message || '添加账户错误,请重试', 220, body.code || 200);
        }
        this.eid = body.data[0]._id;
        this.timestamp = body.data[0].timestamp;
        message = `注册成功,${this.nickName}`;
        this.#sendNotify('Ninja 运行通知', `用户 ${this.nickName}(${decodeURIComponent(this.pt_pin)}) 已上线`);
      }
    } else {
      this.eid = env._id;
      const body = await updateEnv(this.cookie, this.eid);
      if (body.code !== 200) {
        throw new UserError(body.message || '更新账户错误,请重试', 221, body.code || 200);
      }
      this.timestamp = body.data.timestamp;
      message = `欢迎回来,${this.nickName}`;
      this.#sendNotify('Ninja 运行通知', `用户 ${this.nickName}(${decodeURIComponent(this.pt_pin)}) 已更新 CK`);
    }
    return {
      nickName: this.nickName,
      eid: this.eid,
      timestamp: this.timestamp,
      message,
    };
  }

  async getUserInfoByEid() {
    const envs = await getEnvs();
    const env = await envs.find((item) => item._id === this.eid);
    if (!env) {
      throw new UserError('没有找到这个账户,重新登录试试看哦', 230, 200);
    }
    this.cookie = env.value;
    this.timestamp = env.timestamp;
    const remarks = env.remarks;
    if (remarks) {
      this.remark = remarks.match(/remark=(.*?);/) && remarks.match(/remark=(.*?);/)[1];
    }
    await this.#getNickname();
    return {
      nickName: this.nickName,
      eid: this.eid,
      timestamp: this.timestamp,
      remark: this.remark,
    };
  }

  async updateRemark() {
    if (!this.eid || !this.remark || this.remark.replace(/(^\s*)|(\s*$)/g, '') === '') {
      throw new UserError('参数错误', 240, 200);
    }

    const envs = await getEnvs();
    const env = await envs.find((item) => item._id === this.eid);
    if (!env) {
      throw new UserError('没有找到这个账户,重新登录试试看哦', 230, 200);
    }
    this.cookie = env.value;

    const remarks = `remark=${this.remark};`;

    const updateEnvBody = await updateEnv(this.cookie, this.eid, remarks);
    if (updateEnvBody.code !== 200) {
      throw new UserError('更新/上传备注出错,请重试', 241, 200);
    }

    return {
      message: '更新/上传备注成功',
    };
  }

  async delUserByEid() {
    await this.getUserInfoByEid();
    const body = await delEnv(this.eid);
    if (body.code !== 200) {
      throw new UserError(body.message || '删除账户错误,请重试', 240, body.code || 200);
    }
    this.#sendNotify('Ninja 运行通知', `用户 ${this.nickName}(${decodeURIComponent(this.pt_pin)}) 删号跑路了`);
    return {
      message: '账户已移除',
    };
  }

  static async getPoolInfo() {
    const count = await getEnvsCount();
    const allowCount = (process.env.ALLOW_NUM || 40) - count;
    return {
      marginCount: allowCount >= 0 ? allowCount : 0,
      allowAdd: Boolean(process.env.ALLOW_ADD) || true,
    };
  }

  static async getUsers() {
    const envs = await getEnvs();
    const result = envs.map(async (env) => {
      const user = new User({ cookie: env.value, remarks: env.remarks });
      await user.#getNickname(true);
      return {
        pt_pin: user.pt_pin,
        nickName: user.nickName,
        remark: user.remark || user.nickName,
      };
    });
    return Promise.all(result);
  }

  async #getNickname(nocheck) {
    const body = await api({
      url: `https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?orgFlag=JD_PinGou_New&callSource=mainorder&channel=4&isHomewhite=0&sceneval=2&_=${Date.now()}&sceneval=2&g_login_type=1&g_ty=ls`,
      headers: {
        Accept: '*/*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-cn',
        Connection: 'keep-alive',
        Cookie: this.cookie,
        Referer: 'https://home.m.jd.com/myJd/newhome.action',
        'User-Agent':
          'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
        Host: 'me-api.jd.com',
      },
    }).json();
    if (!body.data?.userInfo && !nocheck) {
      throw new UserError('获取用户信息失败,请检查您的 cookie !', 201, 200);
    }
    this.nickName = body.data?.userInfo.baseInfo.nickname || decodeURIComponent(this.pt_pin);
  }

  #formatSetCookies(headers, body) {
    return new Promise((resolve) => {
      let guid, lsid, ls_token;
      this.#s_token = body.s_token;
      guid = headers['set-cookie'][0];
      guid = guid.substring(guid.indexOf('=') + 1, guid.indexOf(';'));
      lsid = headers['set-cookie'][2];
      lsid = lsid.substring(lsid.indexOf('=') + 1, lsid.indexOf(';'));
      ls_token = headers['set-cookie'][3];
      ls_token = ls_token.substring(ls_token.indexOf('=') + 1, ls_token.indexOf(';'));
      this.cookies = `guid=${guid};lang=chs;lsid=${lsid};ls_token=${ls_token};`;
      resolve();
    });
  }

  #sendNotify(title, content) {
    const notify = process.env.NINJA_NOTIFY || true;
    if (!notify) {
      console.log('Ninja 通知已关闭\n' + title + '\n' + content + '\n' + '已跳过发送');
      return;
    }
    exec(`${notifyFile} "${title}" "${content}"`, (error, stdout, stderr) => {
      if (error) {
        console.log(stderr);
      } else {
        console.log(stdout);
      }
    });
  }
};

class UserError extends Error {
  constructor(message, status, statusCode) {
    super(message);
    this.name = 'UserError';
    this.status = status;
    this.statusCode = statusCode || 200;
  }
}