package com.qs.mp.web.controller.api.common;

import cn.jsms.api.ValidSMSResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qs.mp.channel.domain.Channel;
import com.qs.mp.channel.service.IChannelService;
import com.qs.mp.common.constant.Constants;
import com.qs.mp.common.core.domain.AjaxResult;
import com.qs.mp.common.enums.UserIdentityEnum;
import com.qs.mp.common.enums.WxActTypeEnum;
import com.qs.mp.common.jsms.JSMSUtils;
import com.qs.mp.common.qcloud.QcloudFileUtils;
import com.qs.mp.common.utils.LogUtil;
import com.qs.mp.common.utils.StringUtils;
import com.qs.mp.common.utils.UUIDUtils;
import com.qs.mp.common.utils.http.HttpUtils;
import com.qs.mp.core.domain.LoginUser;
import com.qs.mp.framework.domain.WxAccount;
import com.qs.mp.framework.security.handle.HostHolder;
import com.qs.mp.framework.service.IAppTokenService;
import com.qs.mp.framework.service.IWxAccountService;
import com.qs.mp.framework.web.service.SysLoginService;
import com.qs.mp.system.domain.SysUser;
import com.qs.mp.system.domain.vo.WxLoginParams;
import com.qs.mp.system.service.ISysUserService;
import com.qs.mp.utils.SecurityUtils;
import com.qs.mp.web.controller.common.BaseApiController;
import io.swagger.annotations.Api;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * @auther duota
 * @create 2021 2021/9/6 6:16 下午
 * @describe
 */

@Api("用户钱包API")
@RestController
@RequestMapping("/api/v1/mp")
@Component
public class UserController extends BaseApiController {

  @Autowired
  private ISysUserService sysUserService;

  @Autowired
  protected HostHolder hostHolder;

  @Autowired
  private IWxAccountService wxAccountService;

  @Autowired
  private IChannelService channelService;

  @Value("${wx-channel.appId}")
  private String channelAppId;
  @Value("${wx-channel.appSecret}")
  private String channelAppSecret;

  @Value("${wx-user.appId}")
  private String userAppId;
  @Value("${wx-user.appSecret}")
  private String userAppSecret;

  /**
   * 文件上传路径
   */
  @Value("${mp.profile}")
  public String filePath;

  /**
   * 公开
   */
  @Value("${cloud.public-bucket-name}")
  private String publicBucketName;

  @Autowired
  private IAppTokenService appTokenService;

  @Autowired
  private SysLoginService sysLoginService;

  private static final String CBC_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
  private static final String KEY_ALGORITHM = "AES";
  private static final String UTF8 = "UTF-8";


  @RequestMapping(value = "/user/wxauth", method = RequestMethod.POST)
  @ResponseBody
  public AjaxResult wxauth(@RequestBody WxLoginParams wxLoginParams) {
    String appId = channelAppId;
    String appSecret = channelAppSecret;
    if (wxLoginParams.getIdentity() == UserIdentityEnum.USER.ordinal()) {
      appId = userAppId;
      appSecret = userAppSecret;
    }
    //调用微信后台接口获取openId
    String res = wxLoginCheck(wxLoginParams.getCode(), appId, appSecret);
    JSONObject jsonObject = JSONObject.parseObject(res);
    String openId = jsonObject.getString("openid");
    String sessionKey = jsonObject.getString("session_key");
    String unionId = jsonObject.getString("unionid");

    if (StringUtils.isBlank(openId) || StringUtils.isBlank(sessionKey)) {
      LogUtil.error(logger, "微信登录失败:" + jsonObject.toJSONString());
      return error("登录失败");
    }
    //更新数据库用户授权信息
    LoginUser loginUser = SecurityUtils.getLoginUser();
    SysUser user = new SysUser();
    user.setUserId(loginUser.getUserId());
    if (wxLoginParams.getIdentity() == UserIdentityEnum.USER.ordinal()) {
      user.setOpenId(openId);
    } else {
      user.setChannelOpenId(openId);
    }

    user.setSessionKey(sessionKey);
    user.setUnionId(unionId);
    user.setLoginDate(new Date());
    user.setNickName(wxLoginParams.getNickName());
    //如果用户头像字段为空，则默认获取用户微信头像
    String avatarUrl = wxLoginParams.getAvatarUrl();

    //用户先关联过公众号，那么授权后进行自动绑定
    LambdaQueryWrapper<WxAccount> wrapper = new LambdaQueryWrapper();
    wrapper.eq(WxAccount::getUnionid,unionId);
    wrapper.eq(WxAccount::getActType, WxActTypeEnum.GZH.getValue());
    List<WxAccount> accountList = wxAccountService.list(wrapper);
    if(accountList.size() == 1){
      user.setGzhOpenId(accountList.get(0).getOpenid());
    }
    if(StringUtils.isBlank(loginUser.getUser().getAvatar()) && !StringUtils.isBlank(avatarUrl)){
      try {
        //下载并且重新上传头像
        JSONObject result = saveHeadImage(avatarUrl,String.valueOf(user.getUserId()));
        if(result != null){
          user.setAvatar(result.getString("fileName"));
        }
      } catch (Exception e) {
        LogUtil.error(logger,"头像上传异常",e.getMessage());
      }
    }
    logger.info("update user data:"+JSONObject.toJSONString(user));
    int rows = sysUserService.updateUserProfile(user);
    if (rows != 1) {
      LogUtil.error(logger,"用户微信授权异常");
      return error("用户微信授权异常");
    }
    JSONObject rst = new JSONObject();
    rst.put("openId", openId);
    return success(rst);
  }


  @RequestMapping(value = "/user/wxauth/mobile", method = RequestMethod.POST)
  @ResponseBody
  public AjaxResult wxauthMobile(@RequestBody WxLoginParams wxLoginParams) {
    String appId = userAppId;
    String appSecret = userAppSecret;
    /*if (UserIdentityEnum.isChannel(wxLoginParams.getIdentity())) {
      appId = channelAppId;
      appSecret = channelAppSecret;
    }*/
    JSONObject phoneInfo;
    if (StringUtils.isNotBlank(wxLoginParams.getCode())) {
      //调用微信后台接口获取openId
      String res = getWxMobile(wxLoginParams, appId);
      JSONObject jsonObject = JSONObject.parseObject(res);
      int errcode = jsonObject.getIntValue("errcode");
      if (errcode != 0) {
        LogUtil.error(logger, "手机号获取失败:" + res);
        return error("手机号获取失败");
      }
      phoneInfo = jsonObject.getJSONObject("phone_info");
    } else {
      //调用微信后台接口获取openId
      String rtn = wxLoginCheck(wxLoginParams.getLoginCode(), appId, appSecret);
      JSONObject jsonObject = JSONObject.parseObject(rtn);
      String openId = jsonObject.getString("openid");
      String sessionKey = jsonObject.getString("session_key");

      if (StringUtils.isBlank(openId) || StringUtils.isBlank(sessionKey)) {
        LogUtil.error(logger, "微信登录失败:" + jsonObject.toJSONString());
        return error("登录失败");
      }

      String res = weixinDecrypt(wxLoginParams.getEncryptedData(), sessionKey, wxLoginParams.getIv());
      if (StringUtils.isBlank(res)) {
        return error("手机号获取失败");
      }
      /**
       * {
       *     "phoneNumber": "13580006666",
       *     "purePhoneNumber": "13580006666",
       *     "countryCode": "86",
       *     "watermark":
       *     {
       *         "appid":"APPID",
       *         "timestamp": TIMESTAMP
       *     }
       * }
       */
      phoneInfo = JSON.parseObject(res);
      JSONObject watermark = phoneInfo.getJSONObject("watermark");
      if (!appId.equals(watermark.getString("appid"))) {
        LogUtil.error(logger, "手机号解密appid不符，appid:{0}",  watermark.getString("appid"));
        return error("手机号获取失败");
      }
    }
    String phoneNumber = phoneInfo.getString("phoneNumber");

    if (StringUtils.isBlank(phoneNumber)) {
      LogUtil.error(logger, "微信手机号不存在:" + phoneInfo);
      return error("手机号获取失败");
    }
    AjaxResult ajax = AjaxResult.success();

    SysUser sysUser = sysUserService.selectUserByUserName(phoneNumber);
    if (null == sysUser) {
      if (wxLoginParams.getIdentity() != UserIdentityEnum.USER.ordinal()) {
        return error("用户不存在，请联系客服");
      }
      // C端直接注册新用户
      sysUser = new SysUser();
      sysUser.setUserName(phoneNumber);
      sysUser.setNickName(phoneNumber);
      sysUser.setPhonenumber(phoneNumber);
      sysUserService.registerUser(sysUser);
    }
    Map<String, String> result = sysLoginService.wxAuthLogin(phoneNumber, wxLoginParams.getIdentity());
    for (String key : result.keySet()) {
      ajax.put(key, result.get(key));
    }
    return ajax;
  }

  /**
   * 重置密码
   */
  @RequestMapping(value = "/user/resetPwd", method = RequestMethod.POST)
  public AjaxResult resetPwd(@RequestBody JSONObject user) {
    //验证短信验证码
    ValidSMSResult res = JSMSUtils.sendValidSMSCode(
        user.getString("messageId"), user.getString("code"));
    if (res == null || !res.getIsValid()) {
      return error("短信验证码错误");
    }

    int rows = sysUserService.resetUserPwd(user.getString("userName"),
        SecurityUtils.encryptPassword(user.getString("password")));
    if (rows == 1) {
      return success("密码修改成功.");
    } else {
      return error("密码修改失败");
    }
  }

  /**
   * 查询当前登录用户信息
   */
  @RequestMapping(value = "/user/getLoginUserinfo", method = RequestMethod.POST)
  public AjaxResult getLoginUserinfo(@RequestBody JSONObject params) {
    LoginUser loginUser = SecurityUtils.getLoginUser();
    SysUser sysUser = sysUserService.selectUserById(SecurityUtils.getUserId());

    JSONObject jsonObject = new JSONObject();
    jsonObject.put("userName", sysUser.getUserName());
    jsonObject.put("avatar", sysUser.getAvatar());
    if(loginUser.getIdentity() == UserIdentityEnum.CHANNEL) {
      Channel channel = channelService.getById(loginUser.getChannelId());
      jsonObject.put("nickName", channel.getName());
    } else {
      jsonObject.put("nickName", sysUser.getNickName());
    }
    jsonObject.put("openId", sysUser.getOpenId());
    jsonObject.put("channelOpenId", sysUser.getChannelOpenId());
    JSONArray roleKeys = new JSONArray();
    JSONArray roles = new JSONArray();
    loginUser.getUser().getRoles().forEach(sysRole -> roles.add(sysRole.getRoleName()));
    jsonObject.put("roleKeys", roleKeys);
    jsonObject.put("roles", roles);
    jsonObject.put("userId", SecurityUtils.getUserId());

    return success(jsonObject);
  }



  /**
   * code 换取 session_key
   *
   * @param
   * @return
   */
  private String wxLoginCheck(String code, String appId, String appSecret) {
    String param = "appid=" + appId + "&secret=" + appSecret + "&js_code=" + code
        + "&grant_type=authorization_code";
    String res = HttpUtils.sendGet("https://api.weixin.qq.com/sns/jscode2session", param);
    return res;
  }

  /**
   * code 换取 手机号
   *{
   *     "errcode":0,
   *     "errmsg":"ok",
   *     "phone_info": {
   *         "phoneNumber":"xxxxxx",
   *         "purePhoneNumber": "xxxxxx",
   *         "countryCode": 86,
   *         "watermark": {
   *             "timestamp": 1637744274,
   *             "appid": "xxxx"
   *         }
   *     }
   * }
   * @param
   * @return
   */
  private String getWxMobile(WxLoginParams params, String appId) {
    String accessToken = appTokenService.getAccessToken(appId);
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("code", params.getCode());
    LogUtil.info(logger, JSONObject.toJSONString(params));
    String res = HttpUtils.sendPost("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken, jsonObject.toJSONString());
    return res;
  }

  /**
   * { "subscribe": 1, "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", "nickname": "Band", "sex": 1,
   * "language": "zh_CN", "city": "广州", "province": "广东", "country": "中国", "headimgurl":
   * "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
   * "subscribe_time": 1382694957, "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" }
   *
   * @param accessToken
   * @param openId
   * @return
   */
  private String getWxUserInfo(String accessToken, String openId) {
    String url = "https://api.weixin.qq.com/cgi-bin/user/info";
    String params = "access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
    String res = HttpUtils.sendGet(url, params);
    logger.info("weixin user info :" + res);
    return res;
  }

  private JSONObject saveHeadImage(String serverUrl,String userId) throws Exception {
    String outFilePath = filePath+"/headImage";
    File outPath = new File(outFilePath);
    if (!outPath.exists()) {
      if (!outPath.mkdirs()) {
        throw new Exception("makdirs: '" + outFilePath + "'fail");
      }
    }
    URL url = new URL(serverUrl);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setConnectTimeout(3 * 1000);
    //防止屏蔽程序抓取而放回403错误
    conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible;MSIE 5.0;Windows NT;DigExt)");
    Long totalSize = Long.parseLong(conn.getHeaderField("Content-Length"));
    String name = userId+"/"+ UUIDUtils.newId();
    String mimeType = "image/jpg";
    //logger.info("图片开始下载. totalSize" + totalSize);
    if (totalSize > 0) {
      File file = new File(outFilePath+"/"+userId);
      FileUtils.copyURLToFile(url, file);
      //logger.info("图片下载完成.");
      QcloudFileUtils.putFile(file, name, publicBucketName, mimeType );
      FileUtils.delete(file);
    } else {
      throw new Exception("can not find serverUrl :{}" + serverUrl);
    }
    JSONObject result = new JSONObject();
    result.put("fileName",name);
    result.put("fileType",mimeType);
    return result;
  }

  /**
   * 带有初始变量的解密（微信用）
   *
   * @param content     密文
   * @param skey        密钥
   * @param ivParameter 初始向量
   * @return
   * @throws Exception
   */
  public String weixinDecrypt(String content, String skey, String ivParameter) {
    try {
      BASE64Decoder decoder = new BASE64Decoder();
      // 根据微信文档要求需要把 密文、密钥、iv 使用BASE64进行解码
      byte[] keyByte = decoder.decodeBuffer(skey);
      byte[] contentByte = decoder.decodeBuffer(content);
      byte[] ivByte = decoder.decodeBuffer(ivParameter);
      // 生成密码
      SecretKeySpec keySpec = new SecretKeySpec(keyByte, KEY_ALGORITHM);
      // 生成IvParameterSpec
      IvParameterSpec iv = new IvParameterSpec(ivByte);
      // 初始化解密 指定模式 AES/CBC/PKCS5Padding
      Cipher cipher = Cipher.getInstance(CBC_CIPHER_ALGORITHM);
      // 指定解密模式 传入密码 iv
      cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
      // 解密
      byte[] result = cipher.doFinal(contentByte);
      return new String(result, UTF8);
    } catch (Exception e) {
      LogUtil.error(logger, e, "【解密错误】content:{0},ivParameter:{1}", new Object[]{content, ivParameter});
      return null;
    }
  }

  public static void main(String[] args) {
    UserController userController = new UserController();
    System.out.println(userController.weixinDecrypt("qvNE+SHdiP2GTbmNaqahrhhLw7EZetcMhUMV9yPXHxBbWOMcZ5gFxOGliXC9uojyC0NDUXCUi1xvFVDKMYREQ7rLXXrZIkB1jEleJNuUb9kO3LCDRucvbGAbVwm2EsTNsd5VbqI3fKdu0IOxmmSAWCLZNGXZBlki4ke62lI+ASg6dPohQmmCux8jwD2Js3ZnDQN2mb0JPRT68Qj716gYvA==", "QJ/WdeFANTIfIErOCJ2jNg==", "60YJmIckq4kaXp88wAGGHA=="));
  }

}
