package com.gxwebsoft.shop.controller;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.security.JwtSubject;
import com.gxwebsoft.common.core.security.JwtUtil;
import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.LoginRecord;
import com.gxwebsoft.common.system.entity.Role;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.entity.UserRole;
import com.gxwebsoft.common.system.param.UserParam;
import com.gxwebsoft.common.system.result.LoginResult;
import com.gxwebsoft.common.system.service.*;
import com.gxwebsoft.shop.config.WxMaProperties;
import com.gxwebsoft.shop.entity.UserOauth;
import com.gxwebsoft.shop.service.UserOauthService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

import static com.gxwebsoft.common.core.constants.RedisConstants.ACCESS_TOKEN_KEY;

@RestController
@RequestMapping("/api/shop/wx-login")
@Api(tags = "微信小程序登录API")
public class WxLoginController extends BaseController {
  @Resource
  private SettingService settingService;
  @Resource
  private UserService userService;
  @Resource
  private ConfigProperties configProperties;
  @Resource
  private UserRoleService userRoleService;
  @Resource
  private UserOauthService userOauthService;
  @Resource
  private LoginRecordService loginRecordService;
  @Resource
  private RoleService roleService;
  @Resource
  private WxMaProperties wxMaProperties;

  @Autowired
  private StringRedisTemplate redisTemplate;


  @ApiOperation("获取微信openId")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/getOpenId")
  public ApiResult<LoginResult> getOpenId(@RequestBody UserParam userParam, HttpServletRequest request) {
    // 1.获取openid
    JSONObject result = getOpenIdByCode(userParam);
    String openid = result.getString("openid");
    String unionid = result.getString("unionid");
    if (openid == null) {
      return fail("获取openid失败", null);
    }
    // 2.通过openid查询用户是否已存在
    User user = userOauthService.getByOauthId(openid);
    // 3.存在则签发token并返回登录成功,不存在则注册新用户
    if (user == null) {
      user = addUser(userParam);
      // 添加第三方用户信息
      UserOauth uo = new UserOauth();
      uo.setUserId(user.getUserId());
      uo.setTenantId(user.getTenantId());
      uo.setOauthType("MP-WEIXIN");
      uo.setOauthId(openid);
      uo.setUnionid(unionid);
      userOauthService.save(uo);
    }
    // 4.签发token
    loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_LOGIN, null, user.getTenantId(), request);
    String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
      configProperties.getTokenExpireTime(), configProperties.getTokenKey());
    return success("登录成功", new LoginResult(access_token, user));
  }

  @ApiOperation("微信授权手机号码并登录")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/loginByMpWxPhone")
  public ApiResult<LoginResult> loginByMpWxPhone(@RequestBody UserParam userParam, HttpServletRequest request) {
    // 获取手机号码
    String phone = getPhoneByCode(userParam);
    // 查询是否存在
    User user = userService.getByPhone(phone);
    // 不存在
    if (user == null) {
      return fail("该用户不存在", new LoginResult());
    }
    // 签发token
    String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
      configProperties.getTokenExpireTime(), configProperties.getTokenKey());
    loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_LOGIN, null, user.getTenantId(), request);
    return success("登录成功", new LoginResult(access_token, user));
  }

  @ApiOperation("微信授权手机号码并更新")
  @Transactional(rollbackFor = {Exception.class})
  @PostMapping("/updatePhoneByMpWx")
  public ApiResult<?> updatePhoneByMpWx(@RequestBody UserParam userParam) {
    // 获取微信授权手机号
    String phone = getPhoneByCode(userParam);
    // 查询当前用户
    User user = userService.getById(userParam.getUserId());
    if (user != null && phone != null) {
      user.setPhone(phone);
      userService.updateUser(user);
      return success("更新成功",phone);
    }
    return fail("更新失败");
  }

  /**
   * 新用户注册
   */
  private User addUser(UserParam userParam) {
    User addUser = new User();
    // 注册用户
    addUser.setStatus(0);
    addUser.setUsername(createUsername("wx_"));
    addUser.setNickname("微信用户");
    addUser.setPassword(userService.encodePassword(CommonUtil.randomUUID16()));
    addUser.setTenantId(getTenantId());
    Role role = roleService.getOne(new QueryWrapper<Role>().eq("role_code", "user"), false);
    addUser.setRoleId(role.getRoleId());
    if (userService.saveUser(addUser)) {
      // 添加用户角色
      final UserRole userRole = new UserRole();
      userRole.setUserId(addUser.getUserId());
      userRole.setTenantId(addUser.getTenantId());
      userRole.setRoleId(addUser.getRoleId());
      userRoleService.save(userRole);
    }
    return addUser;
  }

  // 获取openid
  private JSONObject getOpenIdByCode(UserParam userParam) {
    // 获取微信小程序配置信息
    JSONObject setting = settingService.getBySettingKey("mp-weixin");
    // 获取openId
    String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + setting.getString("appId") + "&secret=" + setting.getString("appSecret") + "&js_code=" + userParam.getCode() + "&grant_type=authorization_code";
    // 执行get请求
    String result = HttpUtil.get(apiUrl);
    System.out.println("result = " + result);
    // 解析access_token
    return JSON.parseObject(result);
  }

  /**
   * 获取微信手机号码
   *
   * @param userParam 需要传微信凭证code
   */
  private String getPhoneByCode(UserParam userParam) {
    // 获取手机号码
    String apiUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken();
    HashMap<String, Object> paramMap = new HashMap<>();
    paramMap.put("code", userParam.getCode());
    // 执行post请求
    String post = HttpUtil.post(apiUrl, JSON.toJSONString(paramMap));
    JSONObject json = JSON.parseObject(post);
    if (json.get("errcode").equals(0)) {
      JSONObject phoneInfo = JSON.parseObject(json.getString("phone_info"));
      // 微信用户的手机号码
      return phoneInfo.getString("phoneNumber");
    }
    return null;
  }

  /**
   * 生成随机账号
   *
   * @return username
   */
  private String createUsername(String type) {
    return type.concat(RandomUtil.randomString(12));
  }

  /**
   * 获取接口调用凭据AccessToken
   * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
   */
  public String getAccessToken() {
    // 获取微信小程序配置信息
    JSONObject setting = settingService.getBySettingKey("mp-weixin");
    // 从缓存获取access_token
    String value = redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY.concat(getTenantId().toString()));
    if (value != null) {
      // 解析access_token
      JSONObject response = JSON.parseObject(value);
      return response.getString("access_token");
    }
    // 微信获取凭证接口
    String apiUrl = "https://api.weixin.qq.com/cgi-bin/token";
    // 组装url参数
    String url = apiUrl.concat("?grant_type=client_credential").concat("&appid=").concat(setting.getString("appId")).concat("&secret=").concat(setting.getString("appSecret"));
    // 执行get请求
    String result = HttpUtil.get(url);
    // 解析access_token
    JSONObject response = JSON.parseObject(result);
    if (response.getString("access_token") != null) {
      // 存入缓存
      redisTemplate.opsForValue().set(ACCESS_TOKEN_KEY.concat(getTenantId().toString()), result);
      return response.getString("access_token");
    }
    return null;
  }

  public WxMaService wxMaService() {
    List<WxMaProperties.Config> configs = wxMaProperties.getConfigs();
    if (configs == null) {
      throw new WxRuntimeException("大哥，拜托先看下项目首页的说明（readme文件），添加下相关配置，注意别配错了！");
    }
    WxMaService maService = new WxMaServiceImpl();
    maService.setMultiConfigs(
            configs.stream()
                    .map(a -> {
                      WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
                      config.setAppid(a.getAppid());
                      config.setSecret(a.getSecret());
                      config.setToken(a.getToken());
                      config.setAesKey(a.getAesKey());
                      config.setMsgDataFormat(a.getMsgDataFormat());
                      return config;
                    }).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
    return maService;
  }


}
