package com.iformall.controller;


import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.alibaba.fastjson.JSONObject;
import com.iformall.annotation.AuthIgnore;
import com.iformall.common.ErrorCode;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.po.*;
import com.iformall.enums.EnumMallUserStatus;
import com.iformall.exception.MallinkException;
import com.iformall.service.*;
import com.iformall.utils.Constant;
import com.iformall.utils.IPUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author gongbiao
 */
@RestController
@Api(description = "微信相关")
@RequestMapping("/api/wx")
public class WxInfoController extends BaseController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private boolean isFmOpen;

    @Autowired
    WxAppinfoService wxAppinfoService;

    @Autowired
    MallUserInfoService mallUserInfoService;

    @Autowired
    MallUserRoleService mallUserRoleService;

    @Autowired
    MallRolePermissionService mallRolePermissionService;

    @Autowired
    WxBuserService wxBuserService;

    @Autowired
    WxAuthorizerInfoService wxAuthorizerInfoService;

    @Autowired
    WxBuserTokenService wxBuserTokenService;

    @AuthIgnore
    @ApiOperation("获取OPENID")
    @PostMapping("getOpenId")
    public ResultData getOpenId(@RequestBody Map<String,String> param) {
        String appId  = param.get("appId");
        String code  = param.get("code");
        String userId = param.get("bUserId");
        if (StringUtils.isBlank(appId)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "appId不能为空");
        }
        if (StringUtils.isBlank(code)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "code不能为空");
        }
        if (StringUtils.isBlank(userId)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "bUserId不能为空");
        }
        String scene = param.get("scene");
        WxAppinfo wxAppinfo = getAppInfo(appId);
        // 通过authorizerInfo得到tenant_id
        WxAuthorizerInfo wxAuthorizerInfo = null;
        WxMaService wxMaService = null;

        wxAuthorizerInfo = wxAuthorizerInfoService.getByAppId(appId);
        if (wxAuthorizerInfo == null) {
            return new ResultData(ErrorCode.APP_ID_NOT_FOUND);
        }
        if (isFmOpen) {
            wxMaService = getWeappService(appId);
        } else {
            wxMaService = getWeappServiceByAppInfo(wxAppinfo);
            if (StringUtils.isBlank(wxAppinfo.getAccessToken())) {
                // 如果没有accessToken,主动获取保存到数据库中，下次访问可以拿来使用
                updateAccessToken(wxAppinfo, wxMaService);
                wxMaService = getWeappServiceByAppInfo(wxAppinfo);

            } else {
                // 检查token是否已过期, 小时就重新获取
                Date curDate = new Date();
                if (curDate.getTime() > wxAppinfo.getLastTokenTime().getTime() + Constant.H_EXPIRE) {
                    updateAccessToken(wxAppinfo, wxMaService);
                    wxMaService = getWeappServiceByAppInfo(wxAppinfo);
                }
            }
        }

        String token = null;
        String session_key = null;
        String openId = null;
        String unionId = null;
        boolean boolHaveUnionId = false;
        Map<String, String> resultMap = new HashMap<>();
        try {
            WxMaJscode2SessionResult session = wxMaService.jsCode2SessionInfo(code);
            //获取会话密钥（session_key）
            session_key = session.getSessionKey();
            openId = session.getOpenid();
            unionId = session.getUnionid();
            if (StringUtils.isNotBlank(unionId)) {
                boolHaveUnionId = true;
            }
            resultMap.put("openId", openId);
            resultMap.put("session_key", session_key);
        } catch (WxErrorException e) {
            logger.error(e.getMessage(), e);
            throw new MallinkException(ErrorCode.SESSION_KEY_DECODE_ERR.getCode(), e.getMessage());
        }
        logger.info("登录账号ID(bUserId):" + userId + "openId:" + openId);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ipaddress = IPUtil.getIpAddr(request);

        WxBuser newUser = new WxBuser();
        newUser.setTenantId(wxAuthorizerInfo.getTenantId());
        newUser.setAppId(appId);
        newUser.setOpenId(openId);
        if (boolHaveUnionId) {
            newUser.setUnionId(unionId);
        }

        WxBuser oldUser = null;
        if (boolHaveUnionId) {
            // 优先根据unionId查询
            WxBuser userQ = new WxBuser();
            userQ.setTenantId(wxAuthorizerInfo.getTenantId());
            userQ.setUnionId(unionId);
            oldUser = wxBuserService.getByObject(userQ);
        }
        if (oldUser == null) {
            oldUser = wxBuserService.getByOpenId(newUser);
        }
        if (oldUser != null) {
            // 已有用户
            token = oldUser.createToken(new Date());
            resultMap.put("token", token);
            request.setAttribute(Constant.LOGIN_USER_KEY, oldUser.getId());
            request.setAttribute(Constant.TENANT_ID, oldUser.getTenantId());

            oldUser.setRegisterIp(ipaddress);
            oldUser.setSessionKey(session_key);
            oldUser.setAppId(appId);
            oldUser.setOpenId(openId);
            if (boolHaveUnionId) {
                oldUser.setUnionId(unionId);
            }
            if (StringUtils.isNotBlank(wxAuthorizerInfo.getOpenAppid())) {
                oldUser.setOpenAppId(wxAuthorizerInfo.getOpenAppid());
            }
            if (StringUtils.isBlank(oldUser.getScene()) ||
                    oldUser.getScene().equals("undefined")) {                                // from pages/index/index onLoad.options.scene
                oldUser.setScene(scene);
            }
            oldUser.setLoginCount(oldUser.getLoginCount() + 1);
            oldUser.setUserId(Long.parseLong(userId));
            wxBuserTokenService.saveOrUpdate(oldUser);
        } else {
            // 新用户
            newUser.setTenantId(wxAuthorizerInfo.getTenantId());
            token = newUser.createToken(new Date());
            newUser.setRegisterIp(ipaddress);
            newUser.setSessionKey(session_key);

            if (StringUtils.isBlank(newUser.getScene()) ||
                    newUser.getScene().equals("undefined")) {                              // from pages/index/index onLoad.options.scene
                newUser.setScene(scene);
            }
            newUser.setLoginCount(0);
            newUser.setUserId(Long.parseLong(userId));
            wxBuserTokenService.saveOrUpdate(newUser);
            resultMap.put("token", token);
            request.setAttribute(Constant.LOGIN_USER_KEY, newUser.getId());
            request.setAttribute(Constant.TENANT_ID, newUser.getTenantId());
        }
        return new ResultData(resultMap);

    }

    @AuthIgnore
    @ApiOperation("获取超管OPENID")
    @PostMapping("getSuperOpenId")
    public ResultData getSuperOpenId(@RequestBody Map<String,String> param) {
        String appId  = param.get("appId");
        String code  = param.get("code");
        if (StringUtils.isBlank(appId)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "appId不能为空");
        }
        if (StringUtils.isBlank(code)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "code不能为空");
        }

        WxAppinfo wxAppinfo = getAppInfo(appId);
        try {
            Map<String, String> retMap = getOpenIdInfo(appId, code);
            String openId = retMap.get("openId");
            MallUserInfo user = mallUserInfoService.getByBOpenId(openId, wxAppinfo.getTenantId());
            if(user == null) {
                return new ResultData(ErrorCode.USER_NO_PERMISSION, retMap);
            }
            if(user.getStatus().equals(EnumMallUserStatus.NOT_VALID.getCode())) {
                logger.error(ErrorCode.USER_IS_LOCKED.getMessage());
                return new ResultData(ErrorCode.USER_IS_LOCKED);
            }

            if(!mallUserRoleService.checkIsMobileAdmin(user)) {
                return new ResultData(ErrorCode.USER_NO_PERMISSION, retMap);
            }

            retMap.put("phone", user.getPhone());
            retMap.put("msg", "领导来了,欢迎");

            return new ResultData(retMap);
        } catch (Exception e) {
            return new ResultData(Result.ERROR, "解密异常");
        }
    }

    private Map<String,String> getOpenIdInfo(String appId, String code) {
        WxAppinfo wxAppinfo = getAppInfo(appId);
        WxMaService wxMaService = null;
        if(isFmOpen) {
            wxMaService = getWeappService(appId);
        } else {
            wxMaService = getWeappServiceByAppInfo(wxAppinfo);
            if(StringUtils.isBlank(wxAppinfo.getAccessToken())) {
                // 如果没有accessToken,主动获取保存到数据库中，下次访问可以拿来使用
                updateAccessToken(wxAppinfo, wxMaService);
                wxMaService = getWeappServiceByAppInfo(wxAppinfo);
            } else {
                // 检查token是否已过期, 小时就重新获取
                Date curDate = new Date();
                if(curDate.getTime() > wxAppinfo.getLastTokenTime().getTime() + Constant.H_EXPIRE) {
                    updateAccessToken(wxAppinfo, wxMaService);
                    wxMaService = getWeappServiceByAppInfo(wxAppinfo);
                }
            }
        }
        try {
            WxMaJscode2SessionResult session = wxMaService.jsCode2SessionInfo(code);
            //获取会话密钥（session_key）
            String session_key = session.getSessionKey();
            String openId = session.getOpenid();
            logger.info("openId: " + openId);
            Map<String,String> resultMap=new HashMap<>();
            resultMap.put("openId",openId);
            resultMap.put("session_key", session_key);
            return resultMap;
        } catch (WxErrorException e) {
            logger.error(e.getMessage(), e);
            throw new MallinkException(ErrorCode.SESSION_KEY_DECODE_ERR.getCode(), e.getMessage());
        }

    }

    private void updateAccessToken(WxAppinfo wxAppinfo, WxMaService wxMaService) {
        try {
            String accessToken = wxMaService.getAccessToken(true);
            wxAppinfo.setAccessToken(accessToken);
            wxAppinfo.setLastTokenTime(new Date());
            wxAppinfo.setExpiresIn(7200);
            wxAppinfoService.saveOrUpdate(wxAppinfo);
        } catch (WxErrorException e) {
            logger.error(e.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    /**
     * 授权后获取用户的手机号等信息
     * @param map
     * @return
     */
    @AuthIgnore
    @PostMapping("/getUserPhone")
    @ApiOperation(value = "授权后获取用户的手机号", notes="{\"appId\":\"string\",\"encryptedData\":\"string\",\"iv\":\"string\",\"openId\":\"string\",\"session_key\":\"string\"}")
    public ResultData getUserPhone(@RequestBody Map<String, String> map) {
        logger.debug(map.toString());
        Map resultMap = new HashMap();
        String appId = map.get("appId");
        String encryptedData = map.get("encryptedData");
        String iv = map.get("iv");
        String openId = map.get("openId");
        String session_key = map.get("session_key");

        //登录凭证不能为空
        if (StringUtils.isBlank(encryptedData)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "encryptedData 不能为空");
        }
        if (StringUtils.isBlank(iv)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "iv 不能为空");
        }
        if (StringUtils.isBlank(openId)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "openId 不能为空");
        }

        WxMaService wxMaService = getWeappService(appId);

        WxAppinfo wxAppinfo = getAppInfo(appId);
        try {
            // 解密
            WxMaPhoneNumberInfo phoneNoInfo =  wxMaService.getUserService().getPhoneNoInfo(session_key, encryptedData, iv);
            if (null != phoneNoInfo) {
                logger.info(phoneNoInfo.toString());
                String phone = phoneNoInfo.getPhoneNumber();
                resultMap.put("phone", phone);

                MallUserInfo user = mallUserInfoService.getByPhone(phone, wxAppinfo.getTenantId());
                if(user == null) {
                    return new ResultData(ErrorCode.USER_NO_PERMISSION, resultMap);
                }
                if (mallUserRoleService.checkIsMobileAdmin(user)) {
                    resultMap.put("phone", user.getPhone());
                    resultMap.put("admin", "1");

                    user.setBopenId(openId);
                    mallUserInfoService.saveOrUpdate(user);

                    return new ResultData(resultMap);
                } else {
                    return new ResultData(ErrorCode.USER_NO_PERMISSION, resultMap);
                }
            } else {
                return new ResultData(ErrorCode.PHONE_DECODE_ERR, resultMap);
            }
        } catch (Exception e) {
            this.logger.error(e.getMessage(), e);
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "解密并保存出错", resultMap);
        }
    }


    /**
     * 授权后获取用户的手机号等信息
     * @param map
     * @return
     */
    @PostMapping("/getUserPhoneForBuser")
    @ApiOperation(value = "登录后授权获取用户的手机号", notes="{\"appId\":\"string\",\"encryptedData\":\"string\",\"iv\":\"string\",\"openId\":\"string\",\"session_key\":\"string\"}")
    public ResultData getUserPhoneForBuser(@RequestBody Map<String, String> map) {
        logger.debug(map.toString());
        Map resultMap = new HashMap();
        String appId = map.get("appId");
        String encryptedData = map.get("encryptedData");
        String iv = map.get("iv");
        String openId = map.get("openId");
        String session_key = map.get("session_key");

        //登录凭证不能为空
        if (StringUtils.isBlank(encryptedData)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "encryptedData 不能为空");
        }
        if (StringUtils.isBlank(iv)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "iv 不能为空");
        }
        if (StringUtils.isBlank(openId)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "openId 不能为空");
        }

        WxMaService wxMaService = getWeappService(appId);
        WxBuser buser = getBuser();
        try {
            // 解密
            WxMaPhoneNumberInfo phoneNoInfo =  wxMaService.getUserService().getPhoneNoInfo(session_key, encryptedData, iv);
            if (null != phoneNoInfo) {
                logger.info(phoneNoInfo.toString());
                buser.setPhone(phoneNoInfo.getPhoneNumber());
                buser.setPurePhone(phoneNoInfo.getPurePhoneNumber());
                buser.setCountryCode(phoneNoInfo.getCountryCode());
                wxBuserService.saveOrUpdate(buser);
                resultMap.put("msg", "授权手机成功！");
                resultMap.put("phone", phoneNoInfo.getPhoneNumber());
                return new ResultData(resultMap);
            } else {
                return new ResultData(ErrorCode.PHONE_DECODE_ERR, resultMap);
            }
        } catch (Exception e) {
            this.logger.error(e.getMessage(), e);
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "解密并保存出错", resultMap);
        }
    }


    /**
     * 检查微信用户状态
     *
     * @return
     */
    @RequestMapping("/checkWechatStatus")
    @ApiOperation(value = "判断用户是否完成授权", notes = "")
    public ResultData checkWechatStatus() {
        Map resultMap = new HashMap();
        WxBuser user = getBuser();
        if (StringUtils.isNotBlank(user.getNickName())) {
            resultMap.put("nickName", user.getNickName());
        } else {
            logger.warn("用户昵称未授权，跳转到用户授权页！");
            return new ResultData(ErrorCode.NICK_NAME_NOT_FOUND.getCode(), "用户昵称未授权，请跳转到用户昵称授权页！", resultMap);
        }
        if (StringUtils.isNotBlank(user.getPhone())) {
            resultMap.put("phone", user.getPhone());
        } else {
            if (StringUtils.isBlank(user.getPhone())) {
                logger.warn("用户手机号未授权，跳转到授权手机号页！");
                return new ResultData(ErrorCode.PHONE_NOT_FOUND.getCode(), "用户手机号未授权，请跳转到授权手机号页！", resultMap);
            }
            if (user.getPhone().contains("*")) {
                logger.warn("用户手机号已加密，跳转到手工输入手机号页！");
                return new ResultData(ErrorCode.PHONE_IS_ENCRYPTED.getCode(), "用户手机号已加密，手工输入手机号页！", resultMap);
            }
        }
        return new ResultData(Result.SUCCESS, "是老用户，已完成用户昵称/手机号授权", resultMap);
    }

    /**
     * 检查用户状态
     *
     * @return
     */
    @RequestMapping("/checkUserStatus")
    @ApiOperation(value = "判断用户是否完成昵称授权", notes = "")
    public ResultData checkUserStatus() {
        Map resultMap = new HashMap();
        WxBuser user = getBuser();
        if (StringUtils.isNotBlank(user.getNickName())) {
            resultMap.put("nickName", user.getNickName());
        } else {
            logger.warn("用户昵称未授权，跳转到用户授权页！");
            return new ResultData(ErrorCode.NICK_NAME_NOT_FOUND.getCode(), "用户昵称未授权，请跳转到用户昵称授权页！", resultMap);
        }
        return new ResultData(Result.SUCCESS, "是老用户，已完成用户昵称授权", resultMap);
    }

    /**
     * 检查用户状态
     *
     * @return
     */
    @RequestMapping("/checkPhoneStatus")
    @ApiOperation(value = "判断用户是否完成手机授权", notes = "")
    public ResultData checkPhoneStatus() {
        Map resultMap = new HashMap();
        WxBuser user = getBuser();
        if (StringUtils.isNotBlank(user.getPhone())) {
            resultMap.put("phone", user.getPhone());
        } else {
            if (StringUtils.isBlank(user.getPhone())) {
                logger.warn("用户手机号未授权，跳转到授权手机号页！");
                return new ResultData(ErrorCode.PHONE_NOT_FOUND.getCode(), "用户手机号未授权，请跳转到授权手机号页！", resultMap);
            }
            if (user.getPhone().contains("*")) {
                logger.warn("用户手机号已加密，跳转到手工输入手机号页！");
                return new ResultData(ErrorCode.PHONE_IS_ENCRYPTED.getCode(), "用户手机号已加密，手工输入手机号页！", resultMap);
            }
        }
        return new ResultData(Result.SUCCESS, "是老用户，已完成手机号授权", resultMap);
    }

    /**
     * 获取用户的昵称等信息
     *
     * @param map
     * @return
     */
    @PostMapping("/getUserInfo")
    @ApiOperation(value = "授权后获取用户的昵称,unionId等信息", notes = "{\"encryptedData\":\"string\",\"iv\":\"string\"}")
    public ResultData getUserInfo(@RequestBody Map<String, String> map) {
        logger.debug(map.toString());
        Map resultMap = new HashMap();

        String encryptedData = map.get("encryptedData");
        String iv = map.get("iv");

        if (StringUtils.isBlank(encryptedData)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "encryptedData不能为空");
        }
        if (StringUtils.isBlank(iv)) {
            return new ResultData(ErrorCode.SYS_PARAMETER_NOT_NULL.getCode(), "iv不能为空");
        }

        WxBuser user = getBuser();

        if (user != null) {
            logger.debug(user.toString());
            WxMaService wxMaService = getWeappService(user.getAppId());
            String session_key = user.getSessionKey();
            try {
                // 解密用户信息
                WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(session_key, encryptedData, iv);
                if (userInfo != null) {
                    logger.debug(userInfo.toString());
                    user.setUnionId(userInfo.getUnionId());
                    user.setNickName(userInfo.getNickName());
                    user.setGender(Integer.parseInt(userInfo.getGender()));
                    user.setAvatarUrl(userInfo.getAvatarUrl());
                    user.setProvince(userInfo.getProvince());
                    user.setCity(userInfo.getCity());
                    user.setLanguage(userInfo.getLanguage());
                    wxBuserService.saveOrUpdate(user);
                    resultMap.put("openId", user.getOpenId());
                    resultMap.put("unionId", user.getUnionId());
                    resultMap.put("msg", "获取用户信息成功！");
                    return new ResultData(resultMap);
                } else {
                    return new ResultData(ErrorCode.NICK_NAME_DECODE_ERR, resultMap);
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return new ResultData(ErrorCode.DB_FAIL.getCode(), "解密并保存出错", resultMap);
            }
        } else {
            return new ResultData(ErrorCode.USER_LOGIN_FAILED);
        }
    }

    @PostMapping("/updateExtInfo")
    @ApiOperation(value = "用户更新扩展信息(手机)", notes = "{\"systemInfo\":\"string\"}")
    public ResultData updateExtInfo(@RequestBody Map<String, String> map) {
        logger.debug(map.toString());

        String systemInfo = map.get("systemInfo");

        WxBuser user = getBuser();
        if (StringUtils.isNotBlank(systemInfo)) {
            JSONObject extraInfo = new JSONObject();
            extraInfo.put("systemInfo", JSONObject.parseObject(systemInfo));
            user.setExtraInfo(extraInfo.toJSONString());
        }
        wxBuserService.updateExtInfo(user);
        return new ResultData();
    }

    @PostMapping("/updateLBS")
    @ApiOperation(value = "用户更新位置信息", notes = "{\"latitude\":\"string\",\"longitude\":\"string\"}")
    public ResultData updateLBS(@RequestBody Map<String, String> map) {
        logger.debug(map.toString());

        String longitude = map.get("longitude");
        String latitude = map.get("latitude");

        WxBuser user = getBuser();
        if (!StringUtils.isBlank(longitude)) {
            user.setLongitude(BigDecimal.valueOf(Double.valueOf(longitude)));
        }
        if (!StringUtils.isBlank(latitude)) {
            user.setLatitude(BigDecimal.valueOf(Double.valueOf(latitude)));
        }
        wxBuserService.updateLBS(user);
        return new ResultData();
    }

    @PostMapping("/updateScene")
    @ApiOperation(value = "用户更新scene", notes = "{\"scene\":\"string\"}")
    public ResultData updateScene(@RequestBody Map<String, String> map) {
        logger.debug(map.toString());

        String scene = map.get("scene");

        WxBuser user = getBuser();
        if (StringUtils.isBlank(user.getScene())) {
            if (StringUtils.isNotBlank(scene)) {
                user.setScene(scene);
                wxBuserService.updateScene(user);
            }
        }
        return new ResultData();
    }
}
