
'use strict';

const Service = require('egg').Service;
const uuidV4 = require('uuid/v4');
const moment = require('moment');
const R = require('ramda');
const _ = require('lodash');

class UserService extends Service {
  // 发送验证码
  async sendVerificationCode(input) {
    const { ctx } = this;
    const phone = input.phone;
    const APP_ID = '1C1wN39F4s61w62';
    const APP_KEY = '6643BBA3-2663-4A69-BA4E-2711D6B0AED5';

    if (!ctx.helper.isPhoneNumber(phone)) {
      ctx.failed('手机号不正确');
    }

    // const redisCode = await ctx.app.memcache.get(`course_verify_code_${phone}`);
    const redisCheck = await ctx.app.memcache.get(`course_verify_code_check_${phone}`);
    if (!ctx.isEmpty(redisCheck)) {
      ctx.failed('请勿频繁操作');
    }

    if (ctx.app.config.env === 'local') {
      await ctx.app.memcache.set(`course_verify_code_${phone}`, '12345', 300);
      await ctx.app.memcache.set(`course_verify_code_check_${phone}`, '1', 59);
      return { result: true };
    }

    const code = await this.getRandomNumber(5);

    const now = String(new Date().getTime() - 1000);
    const ret = await ctx.helper.send_request('http://b.jianbing.com/messages/index', {
      app_id: APP_ID,
      timestamp: now,
      sign: ctx.helper.md5(`${now}&${APP_KEY}`),
      params: {
        phone,
        content: `【趣选课】您的验证码是：${code}，您正在登录趣选课，验证码5分内有效，请勿泄露给他人。`,
      },
    }, {
      method: 'POST',
      dataType: 'json',
      contentType: 'json',
      timeout: [ 5000, 60000 ],
    });

    if (ret.status === 201 || ret.status === 200) {
      await ctx.app.memcache.set(`course_verify_code_${phone}`, code, 300);
      await ctx.app.memcache.set(`course_verify_code_check_${phone}`, '1', 59);
    } else {
      ctx.logger.info('course_send_verify_code_error: ' + JSON.stringify(ret.data));
      ctx.failed('发送验证码失败');
    }

    return { result: true };
  }

  // 生成随机位数的数字串
  async getRandomNumber(length) {
    let ret = '';
    for (let i = 0; i < length; i++) {
      ret += Math.floor(Math.random() * 10);
    }

    return ret;
  }

  // 手机号登录
  async loginByPhone(input) {
    const { ctx, app } = this;
    const phone = input.phone;
    const code = input.code;

    if (!ctx.helper.isPhoneNumber(phone)) {
      ctx.failed('手机号不正确');
    }

    // 判断用户是否存在
    let userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { phone, is_deleted: 0 } });
    if (ctx.isEmpty(userInfo)) {
      const uuid = uuidV4();
      await ctx.classModel.V5.CourseUser.findOrCreate({ where: { phone, is_deleted: 0 }, defaults: { uuid, phone } });
      userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { phone, is_deleted: 0 } });
    }

    // 校验验证码
    const redisCode = await ctx.app.memcache.get(`course_verify_code_${phone}`);
    if (ctx.isEmpty(redisCode) || code !== redisCode) {
      ctx.failed('验证码错误');
    }

    const key = 'course_v5_user_session_' + userInfo.uuid;
    const value = {
      user_uuid: userInfo.uuid,
      openid: userInfo.openid,
      session_key: '',
      phone,
    };
    await ctx.app.memcache.set(key, value, 7 * 24 * 3600);
    let authToken = await app.memcache.get('course_auth_token_' + userInfo.uuid);
    if (ctx.isEmpty(authToken)) {
      authToken = await this.service.jwt.apply({ user_uuid: userInfo.uuid, openid: userInfo.openid });
    }
    await app.memcache.set('course_auth_token_' + userInfo.uuid, authToken, 30 * 24 * 3600);

    const ret = {
      user_uuid: userInfo.uuid,
      openid: userInfo.openid,
      bind_phone: ctx.isEmpty(userInfo.phone) ? 0 : 1,
      auth_token: authToken,
    };

    return ret;
  }

  // 微信登录
  async loginByWX(input) {
    const { ctx, app } = this;
    const code = input.code;
    if (ctx.isEmpty(code)) {
      ctx.failed('error code');
    }

    // 请求微信授权 获取openid
    const wxAuthInfo = await this.requestWxAuth(code);
    const openid = wxAuthInfo.openid;// 获取openid
    const session_key = wxAuthInfo.session_key;

    // 判断用户是否存在
    let userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { openid, is_deleted: 0 } });
    if (ctx.isEmpty(userInfo)) {
      const uuid = uuidV4();
      userInfo = await ctx.classModel.V5.CourseUser.findOrCreate({ where: { openid, is_deleted: 0 }, defaults: { uuid, openid } });
      userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { openid, is_deleted: 0 } });
    }

    // 存储缓存标识
    const userUuid = userInfo.uuid;
    const key = 'course_v5_user_session_' + userUuid;
    const value = {
      user_uuid: userInfo.uuid,
      openid,
      session_key,
      phone: userInfo.phone,
    };
    await app.memcache.set(key, value, 7 * 24 * 3600);
    const authToken = ctx.isEmpty(userInfo.phone) ? '' : await this.service.jwt.apply({ user_uuid: userInfo.uuid, openid: userInfo.openid });
    if (!ctx.isEmpty(authToken)) {
      await app.memcache.set('course_auth_token_' + userInfo.uuid, authToken, 30 * 24 * 3600);
    }
    const ret = {
      user_uuid: userInfo.uuid,
      openid: userInfo.openid,
      bind_phone: ctx.isEmpty(userInfo.phone) ? 0 : 1,
      auth_token: authToken,
    };

    return ret;
  }

  // 微信授权
  async requestWxAuth(code) {
    const { ctx } = this;
    const APPID = ctx.app.config.COURSE_WX_APPID;
    const SECRET = ctx.app.config.COURSE_WX_SECRET;
    const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`;

    const result = await ctx.helper.send_request(url, {}, { method: 'GET' });
    // const result = {"data":{"session_key":"Ce7HE1+MXfyZpWLYmkP0Iw==","openid":"oSjKI5LlG6AF7_vdV5Qb_DsbHcf4"},"status":200,"headers":{"connection":"keep-alive","content-type":"text/plain","date":"Tue, 24 Sep 2019 06:18:58 GMT","content-length":"82"},"res":{"status":200,"statusCode":200,"statusMessage":"OK","headers":{"connection":"keep-alive","content-type":"text/plain","date":"Tue, 24 Sep 2019 06:18:58 GMT","content-length":"82"},"size":82,"aborted":false,"rt":113,"keepAliveSocket":false,"data":{"session_key":"Ce7HE1+MXfyZpWLYmkP0Iw==","openid":"oSjKI5LlG6AF7_vdV5Qb_DsbHcf4"},"requestUrls":["https://api.weixin.qq.com/sns/jscode2session?appid=wx4769ebba9b91f8ec&secret=680440637b4e38c9b66529cfd5dc590e&js_code=021678ss18NNAk0Fohps1oA6ss1678sT&grant_type=authorization_code"],"timing":{"queuing":15,"dnslookup":15,"connected":27,"requestSent":57,"waiting":111,"contentDownload":113},"remoteAddress":"101.227.162.120","remotePort":443,"socketHandledRequests":1,"socketHandledResponses":1}};
    ctx.logger.info(JSON.stringify({ course_mini_auth_ret: result }));
    if (result.status !== 200) {
      ctx.failed('授权失败');
    }

    const ret = result.data;
    if (!ret.session_key && !ret.openid && ret.errcode !== 0) {
      ctx.failed(ret.errmsg);
    }

    const openid = ret.openid;
    const session_key = ret.session_key;

    return { openid, session_key };
  }

  // 保存用户信息
  async registerUserInfo(input) {
    const { ctx, app } = this;
    const { avatar, nickname, sex, encryptedData, iv, userUuid } = input;
    // 查找用户是否存在并更新
    const user = await ctx.classModel.V5.CourseUser.findOne({ where: { uuid: userUuid, is_deleted: 0 } });
    if (ctx.isEmpty(user)) {
      ctx.failed('用户不存在');
    }

    ctx.setUserUuid(userUuid);

    const data = { is_deleted: 0 };
    if (!ctx.isEmpty(avatar)) {
      data.avatar = avatar;
    }
    if (!ctx.isEmpty(nickname)) {
      data.nickname = nickname;
    }
    if (!ctx.isEmpty(sex)) {
      data.sex = sex;
    }

    if (!ctx.isEmpty(encryptedData) && !ctx.isEmpty(iv)) {
      const decoded = await ctx.service.course.v5.wechat.decodeData(encryptedData, iv);
      if (!ctx.isEmpty(decoded) && !ctx.isEmpty(decoded.phoneNumber)) {
        data.phone = decoded.phoneNumber;
      }
    }

    // 校验手机号是否已经存在
    const userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { phone: data.phone, is_deleted: 0 } });
    if (!ctx.isEmpty(userInfo)) {
      // 新微信覆盖旧微信
      data.openid = user.openid;
      await ctx.classModel.V5.CourseUser.update(data, { where: { id: userInfo.id } });
      data.uuid = userInfo.uuid;
      if (user.id !== userInfo.id) {
        await ctx.classModel.V5.CourseUser.update({ is_deleted: 1 }, { where: { id: user.id } });
      }
    } else {
      await ctx.classModel.V5.CourseUser.update(data, { where: { id: user.id } });
      data.uuid = user.uuid;
    }

    // 更新登录信息
    const userSession = app.memcache.get('course_v5_user_session_' + user.uuid);
    const key = 'course_v5_user_session_' + data.uuid;
    const value = {
      user_uuid: data.uuid,
      openid: user.openid,
      session_key: ctx.isEmpty(userSession) ? '' : userSession.session_key,
      phone: data.phone,
    };
    await app.memcache.set(key, value, 7 * 24 * 3600);
    const authToken = await this.service.jwt.apply({ user_uuid: data.uuid, openid: user.openid });
    await app.memcache.set('course_auth_token_' + data.uuid, authToken, 30 * 24 * 3600);

    const ret = {
      user_uuid: data.uuid,
      openid: user.openid,
      bind_phone: 1,
      auth_token: authToken,
    };
    return ret;
  }

  // 获取用户信息
  async getUserInfo() {
    const { ctx } = this;
    const userUuid = ctx.userUuid;
    const userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { uuid: userUuid, is_deleted: 0 } });
    if (ctx.isEmpty(userInfo)) {
      ctx.failed('用户不存在');
    }

    // 获取用户宝宝信息
    const userBabyInfo = await ctx.classModel.V5.CourseV5UserBaby.findOne({ where: { user_uuid: userUuid, status: 1, is_deleted: 0 } });

    const ret = {
      user_uuid: userInfo.uuid,
      nickname: userInfo.nickname,
      avatar: userInfo.avatar,
      sex: userInfo.sex,
      openid: userInfo.openid,
      bind_phone: ctx.isEmpty(userInfo.phone) ? 0 : 1,
      baby_name: ctx.isEmpty(userBabyInfo) ? '' : userBabyInfo.baby_name,
      baby_age: ctx.isEmpty(userBabyInfo) ? '' : userBabyInfo.baby_age,
      baby_sex: ctx.isEmpty(userBabyInfo) ? '' : userBabyInfo.baby_sex,
      baby_birth: ctx.isEmpty(userBabyInfo) ? '' : userBabyInfo.baby_birth,
    };

    return ret;
  }

  // 上传用户宝宝信息
  async addUserBaby(input) {
    const { ctx } = this;
    const name = input.baby_name || '';
    const age = input.baby_age || 0;
    const sex = input.baby_sex || 1;
    const birth = input.baby_birth || '';

    const data = {
      user_uuid: ctx.userUuid,
      baby_name: name,
      baby_age: age,
      baby_sex: sex,
      baby_birth: birth,
    };

    // 是否原来已经有数据
    const babyInfo = await ctx.classModel.V5.CourseV5UserBaby.findOne({ where: { user_uuid: ctx.userUuid, status: 1, is_deleted: 0 } });
    if (ctx.isEmpty(babyInfo)) {
      await ctx.classModel.V5.CourseV5UserBaby.findOrCreate({ where: { user_uuid: ctx.userUuid, status: 1, is_deleted: 0 }, defaults: data });
    } else {
      await ctx.classModel.V5.CourseV5UserBaby.update(data, { where: { id: babyInfo.id } });
    }

    const ret = {
      result: true,
    };
    return ret;
  }


  // 新增用户收货地址
  async addUserAddress(input) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;

    if (ctx.isEmpty(userUuid)) {
      ctx.failed('用户异常');
    }

    const data = {
      user_uuid: userUuid,
      province: input.province || '',
      city: input.city || '',
      area: input.area || '',
      address: input.address || '',
      name: input.name || '',
      phone: input.phone || '',
      is_deleted: 0,
    };

    await ctx.classModel.V5.CourseUserAddress.update({ is_deleted: 1 }, { where: { user_uuid: userUuid, is_deleted: 0 } });
    await ctx.classModel.V5.CourseUserAddress.create(data);

    return { result: true };
  }


  // 获取用户收货地址
  async getUserAddress() {
    const { ctx } = this;

    const address = await ctx.classModel.V5.CourseUserAddress.findOne({ where: { user_uuid: ctx.userUuid, is_deleted: 0 } });
    const ret = ctx.isEmpty(address) ? {} : {
      id: address.id,
      user_uuid: address.user_uuid,
      province: address.province,
      city: address.city,
      area: address.area,
      address: address.address,
      name: address.name,
      phone: address.phone.substr(0, 3) + '****' + address.phone.substr(7),
    };

    return ret;
  }


  // 购买课程
  async buyClass(id) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;

    if (ctx.isEmpty(userUuid)) {
      ctx.failed('用户异常');
    }

    // 先判断用户订单
    let order = await ctx.classModel.V5.CourseUserOrder.findOne({ where: { user_uuid: userUuid, class_id: id, is_deleted: 0 }, order: [[ 'id', 'asc' ]] });
    if (!ctx.isEmpty(order) && order.status === 1) {
      ctx.failed('请勿重复购买');
    }

    // 获取课程详情
    const classInfo = await ctx.classModel.V5.CourseV5Class.findOne({ where: { id, status: 1, is_deleted: 0 }, attributes: [ 'name', 'pay_price' ] });
    if (ctx.isEmpty(classInfo)) {
      ctx.failed('课程不存在');
    }

    // 获取用户地址
    const address = await ctx.classModel.V5.CourseUserAddress.findOne({ where: { user_uuid: ctx.userUuid, is_deleted: 0 }, attributes: [ 'province', 'city', 'area', 'address', 'name', 'phone' ], raw: true });
    if (ctx.isEmpty(address)) {
      ctx.failed('地址不能为空');
    }

    const orderNo = '11' + moment().format('YYYYMMDDHHMMSS') + await this.getRandomNumber(6);
    if (ctx.isEmpty(order)) {
      const data = {
        order_no: orderNo,
        user_uuid: userUuid,
        class_id: id,
        pay: classInfo.pay_price,
        type: 1,
        address: JSON.stringify(address),
      };
      await ctx.classModel.V5.CourseUserOrder.create(data);
      order = await ctx.classModel.V5.CourseUserOrder.findOne({ where: { user_uuid: userUuid, class_id: id, is_deleted: 0 }, order: [[ 'id', 'asc' ]] });
    }

    await ctx.classModel.V5.CourseUserOrder.update({ pay: classInfo.pay_price, order_no: orderNo, type: 1, redeem: '', address: JSON.stringify(address), status: 0 }, { where: { id: order.id } });

    let response = await this.service.course.v5.wechat.pay({ pay: order.pay, order_no: orderNo, body: `趣选课-订单${orderNo}` });
    response = await ctx.helper.xmlTojson(response);

    if (ctx.isEmpty(response)) {
      ctx.failed('未知错误');
    }
    if (response.return_code[0] === 'FAIL') {
      ctx.failed(response.return_msg[0]);
    }
    if (response.result_code[0] === 'FAIL') {
      ctx.failed(response.err_code_des[0]);
    }

    const appId = await this.service.course.v5.wechat.getAppId();
    const timeStamp = moment().unix();
    const arr = [ `appId=${appId}`, `timeStamp=${timeStamp}`, `nonceStr=${response.nonce_str[0]}`, `package=prepay_id=${response.prepay_id[0]}`, 'signType=MD5' ];
    const paySign = await this.service.course.v5.wechat.sign(arr);


    const ret = {
      appId,
      timeStamp,
      nonceStr: response.nonce_str[0],
      package: `prepay_id=${response.prepay_id[0]}`,
      signType: 'MD5',
      paySign,
    };

    return ret;
  }


  // 我的订单列表
  async getUserOrderList() {
    const { ctx } = this;
    const userUuid = ctx.userUuid;

    const orderList = await ctx.classModel.V5.CourseUserOrder.findAll({ where: { user_uuid: userUuid, status: 1, is_deleted: 0 }, order: [[ 'id', 'desc' ]] });
    let classList = await ctx.classModel.V5.CourseV5Class.findAll({ where: { id: { $in: R.pluck('class_id', orderList) } }, attributes: [ 'id', 'name', 'logo' ], raw: true });
    classList = _.groupBy(classList, 'id');

    const results = [];
    for (const v of orderList) {
      results.push({
        id: v.id,
        order_no: v.order_no,
        class_id: v.class_id,
        class_name: ctx.isEmpty(classList[v.class_id]) ? '' : classList[v.class_id][0].name,
        class_logo: ctx.isEmpty(classList[v.class_id]) ? '' : classList[v.class_id][0].logo,
      });
    }

    const ret = {
      list: results,
    };

    return ret;
  }


  // 兑换课程
  async redeemClass(input) {
    const { ctx } = this;
    const userUuid = ctx.userUuid;
    const redeemCode = input.code;
    const classId = Number(input.class_id) || 0;
    if (ctx.isEmpty(userUuid)) {
      ctx.failed('用户异常');
    }

    ctx.logger.info('course_redeem_code: ' + redeemCode + '  ' + userUuid);

    const codeInfo = await ctx.classModel.V5.CourseRedeemCode.findOne({ where: { code: redeemCode, status: 1, is_deleted: 0 } });
    if (ctx.isEmpty(codeInfo)) {
      ctx.failed('无效码');
    }

    if (codeInfo.is_used === 1) {
      ctx.failed('兑换码已被使用');
    }

    const classInfo = await ctx.classModel.V5.CourseV5Class.findOne({ where: { id: codeInfo.class_id, status: 1, is_deleted: 0 }, attributes: [ 'id', 'institution_id', 'name', 'button_url' ] });
    if (ctx.isEmpty(classInfo)) {
      ctx.failed('课程不存在');
    }

    // 判断是否是同一个课程
    if (classId > 0 && classId !== codeInfo.class_id) {
      const institution = await ctx.classModel.V5.CourseV5Institution.findOne({ where: { id: classInfo.institution_id } });
      return {
        class_id: codeInfo.class_id,
        result: false,
        text: `您输入的兑换码只可兑换【${institution.name}】的【${classInfo.name}】 是否需要兑换`,
      };
    }

    // 订单处理
    const orderInfo = await ctx.classModel.V5.CourseUserOrder.findOne({ where: { user_uuid: userUuid, class_id: codeInfo.class_id, is_deleted: 0 } });
    if (ctx.isEmpty(orderInfo)) {
      const orderNo = '12' + moment().format('YYYYMMDDHHMMSS') + await this.getRandomNumber(6);
      await ctx.classModel.V5.CourseUserOrder.create({ user_uuid: userUuid, order_no: orderNo, class_id: codeInfo.class_id, pay: 0, pay_time: moment().format('YYYY-MM-DD HH:mm:ss'), pay_result: '', type: 2, redeem: redeemCode, address: '', status: 1 });
    } else {
      if (orderInfo.status === 1) {
        ctx.failed('您已购买过该课程');
      } else {
        await ctx.classModel.V5.CourseUserOrder.update({ pay: 0, pay_time: moment().format('YYYY-MM-DD HH:mm:ss'), pay_result: '', type: 2, redeem: redeemCode, address: '', status: 1 }, { where: { id: orderInfo.id } });
      }
    }

    await ctx.classModel.V5.CourseRedeemCode.update({ user_uuid: userUuid, is_used: 1, used_time: moment().format('YYYY-MM-DD HH:mm:ss') }, { where: { id: codeInfo.id } });

    return { class_id: codeInfo.class_id, result: true, url: classInfo.button_url };
  }


  // app登录
  async loginByApp() {
    const { ctx } = this;
    const appUserId = ctx.cookies.get('app_user_id', { signed: false });
    const token = ctx.cookies.get('token', { signed: false });
    const userId = ctx.cookies.get('user_id', { signed: false });
    const deviceId = ctx.cookies.get('device_id', { signed: false });
    const deviceLoginId = ctx.cookies.get('device_login_id', { signed: false });

    const params = {
      app_user_id: appUserId,
      token,
      uid: userId,
      device_id: deviceId,
      device_login_id: deviceLoginId,
    };

    const result = await ctx.helper.send_request(ctx.app.config.NODE_URL + '/user/auth', params, {
      method: 'POST',
    });
    ctx.logger.info(JSON.stringify({ url: ctx.app.config.NODE_URL + '/user/auth', request_header: ctx.request.header, user_auth_params: params, user_auth_result: result }));
    if (result.status !== 201 || ctx.isEmpty(result.data)) {
      ctx.throw(401, 'token已失效');
      return;
    }

    const phone = ctx.isEmpty(result.data.data.passport) ? '' : result.data.data.passport;
    if (ctx.isEmpty(phone)) {
      ctx.failed('phone异常');
    }

    // 判断用户是否存在
    let userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { phone, is_deleted: 0 } });
    if (ctx.isEmpty(userInfo)) {
      const uuid = uuidV4();
      await ctx.classModel.V5.CourseUser.findOrCreate({ where: { phone, is_deleted: 0 }, defaults: { uuid, phone } });
      userInfo = await ctx.classModel.V5.CourseUser.findOne({ where: { phone, is_deleted: 0 } });
    }

    const data = {
      app_id: result.data.data.app_id,
      app_type_id: result.data.data.app_type_id,
      user_id: result.data.data.user_id,
      app_user_id: result.data.data.app_user_id,
    };
    await ctx.classModel.V5.CourseUser.update(data, { where: { id: userInfo.id } });

    const key = 'course_v5_user_session_' + userInfo.uuid;
    const value = {
      user_uuid: userInfo.uuid,
      openid: userInfo.openid,
      session_key: '',
      phone,
    };
    const session = await ctx.app.memcache.get(key);
    if (ctx.isEmpty(session)) {
      await ctx.app.memcache.set(key, value, 7 * 24 * 3600);
    }
    let authToken = await ctx.app.memcache.get('course_auth_token_' + userInfo.uuid);
    if (ctx.isEmpty(authToken)) {
      authToken = await this.service.jwt.apply({ user_uuid: userInfo.uuid, openid: userInfo.openid });
      await ctx.app.memcache.set('course_auth_token_' + userInfo.uuid, authToken, 30 * 24 * 3600);
    }

    const ret = {
      user_uuid: userInfo.uuid,
      auth_token: authToken,
    };

    return ret;
  }
}

module.exports = UserService;
