
'use strict';

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

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}`);
    if (!ctx.isEmpty(redisCode)) {
      ctx.failed('请勿频繁操作');
    }

    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);
    } 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);
    const 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;
  }
}

module.exports = UserService;
