package com.gxwebsoft.common.system.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.exception.BusinessException;
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.love.entity.UserProfile;
import com.gxwebsoft.love.service.UserProfileService;
import com.gxwebsoft.shop.entity.Merchant;
import com.gxwebsoft.shop.entity.UserOauth;
import com.gxwebsoft.shop.entity.UserReferee;
import com.gxwebsoft.shop.service.MerchantService;
import com.gxwebsoft.shop.service.UserOauthService;
import com.gxwebsoft.shop.service.UserRefereeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

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

@RestController
@RequestMapping("/api/wx-login")
@Api(tags = "微信小程序登录API")
public class WxLoginController extends BaseController {
  private final StringRedisTemplate redisTemplate;
  @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 ConfigProperties config;
  @Resource
  private UserRefereeService userRefereeService;
  @Resource
  private UserProfileService userProfileService;
  @Resource
  private MerchantService merchantService;

  public WxLoginController(StringRedisTemplate redisTemplate) {
    this.redisTemplate = 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 = userService.getByOauthId(userParam);
    // 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);
    if(phone == null){
      String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString());
      redisTemplate.delete(key);
      throw new BusinessException("授权失败，请重试");
    }
    // 查询是否存在
    User user = userService.getByPhone(phone);
    // 不存在则注册
    if (user == null) {
      userParam.setPhone(phone);
      user = addUser(userParam);
      user.setRecommend(1);
    }
    // 签发token
    String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
      configProperties.getTokenExpireTime(), configProperties.getTokenKey());
    loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_REGISTER, 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.setGradeId(2);
    if(userParam.getPhone() != null){
      addUser.setPhone(userParam.getPhone());
    }
    addUser.setPassword(userService.encodePassword(CommonUtil.randomUUID16()));
    addUser.setTenantId(getTenantId());
    addUser.setRecommend(1);
    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);
      // 用户详细资料表
      final UserProfile profile = new UserProfile();
      profile.setUserId(addUser.getUserId());
      profile.setRecommend(1);
      profile.setWhenMarriedMate("想要二年内结婚");
      userProfileService.save(profile);
      // 添加门店
      final Merchant merchant = new Merchant();
      merchant.setUserId(addUser.getUserId());
      merchant.setMerchantOwner(addUser.getUserId());
      merchant.setMerchantCode(addUser.getUserId()+"");
      merchant.setMerchantName(addUser.getNickname());
      merchant.setLogo(addUser.getAvatar());
      merchant.setProvince(addUser.getProvince());
      merchant.setCity(addUser.getCity());
      merchant.setRegion(addUser.getRegion());
      merchant.setMerchantPhone(addUser.getPhone());
      merchant.setMerchantHours("8:30 - 22:30");
      merchant.setSortNumber(100);
      merchant.setStatus(0);
      merchant.setFirstRatio(new BigDecimal("0.1"));
      merchant.setSecondRatio(new BigDecimal("0.1"));
      merchant.setOnlineRatio(new BigDecimal("0.1"));
      merchant.setOfflineRatio(new BigDecimal("0"));
      merchant.setShopRatio(new BigDecimal("0"));
      merchant.setTenantId(addUser.getTenantId());
      merchantService.save(merchant);
    }
    // 绑定推荐关系
    if(userParam.getRefereeId() != null && userParam.getRefereeId() > 0){
      final Integer refereeId = userParam.getRefereeId();
      final UserReferee userReferee = new UserReferee();
      userReferee.setUserId(addUser.getUserId());
      userReferee.setDealerId(refereeId);
      userReferee.setLevel(1);
      userRefereeService.save(userReferee);
    }
    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);
    // 解析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<>();
    if (StrUtil.isBlank(userParam.getCode())) {
      throw new BusinessException("code不能为空");
    }
    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"));
      // 微信用户的手机号码
      final String phoneNumber = phoneInfo.getString("phoneNumber");
      // 验证手机号码
      if(!Validator.isMobile(phoneNumber)){
        String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString());
        redisTemplate.delete(key);
        throw new BusinessException("手机号码格式不正确");
      }
      return 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
   */
  private String getAccessToken() {
    String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString());
    // 获取微信小程序配置信息
    JSONObject setting = settingService.getBySettingKey("mp-weixin");
    // 从缓存获取access_token
    String value = redisTemplate.opsForValue().get(key);
    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(key, result,7000L, TimeUnit.SECONDS);
      return response.getString("access_token");
    }
    return null;
  }

  @ApiOperation("获取微信openId")
  @PostMapping("/getWxOpenId")
  public ApiResult<?> getWxOpenId(@RequestBody UserParam userParam) {
    final User loginUser = getLoginUser();
    if(loginUser.getUsername().equals("www")){
      return fail("游客");
    }
    String apiUrl = "https://api.weixin.qq.com/sns/jscode2session";
    final HashMap<String, Object> map = new HashMap<>();
    final JSONObject setting = settingService.getBySettingKey("mp-weixin");
    final String appId = setting.getString("appId");
    final String appSecret = setting.getString("appSecret");
    map.put("appid",appId);
    map.put("secret",appSecret);
    map.put("js_code",userParam.getCode());
    map.put("grant_type","authorization_code");
    final String response = HttpUtil.get(apiUrl,map);
    System.out.println("response = " + response);
    final JSONObject jsonObject = JSONObject.parseObject(response);

    // 保存openid
    final List<UserOauth> list = userOauthService.list(new LambdaQueryWrapper<UserOauth>().eq(UserOauth::getUserId, loginUser.getUserId()).eq(UserOauth::getOauthType,"MP-WEIXIN"));
    if (CollectionUtils.isEmpty(list)) {
      final UserOauth userOauth = new UserOauth();
      userOauth.setUserId(getLoginUserId());
      userOauth.setOauthId(jsonObject.getString("openid"));
      userOauth.setUnionid(jsonObject.getString("unionid"));
      userOauth.setOauthType("MP-WEIXIN");
      userOauthService.save(userOauth);
      return success("保存openid成功",jsonObject);
    }
    return fail("更新失败",null);
  }

  @ApiOperation("获取微信小程序码")
  @GetMapping("/getQRCode")
  public ApiResult<?> getQRCode() {
    String apiUrl = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + getAccessToken();
    final HashMap<String, Object> map = new HashMap<>();
    map.put("path","pages/flash/index?user_id="+getLoginUserId());
//    map.put("env_version","trial");
    // 获取图片 Buffer
    byte[] qrCode = HttpRequest.post(apiUrl)
      .body(JSON.toJSONString(map))
      .execute().bodyBytes();

    // 保存的文件名称
    final String fileName = CommonUtil.randomUUID8().concat(".png");
    // 保存路径
    String filePath = getUploadDir().concat("qrcode/") + fileName;
    File file = FileUtil.writeBytes(qrCode, filePath);
    if(file != null){
      return success(config.getFileServer().concat("/qrcode/").concat(fileName));
    }
    return fail("获取失败",null);
  }

  /**
   * 文件上传位置(服务器)
   */
  private String getUploadDir() {
    return config.getUploadPath() + "file/";
  }


}
