package cn.exploring.engine.server.wx.web;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.exploring.engine.server.core.notify.NotifyService;
import cn.exploring.engine.server.core.util.CharUtil;
import cn.exploring.engine.server.core.util.JacksonUtil;
import cn.exploring.engine.server.core.util.ResponseUtil;
import cn.exploring.engine.server.core.util.bcrypt.BCryptPasswordEncoder;
import cn.exploring.engine.server.db.domain.WxUser;
import cn.exploring.engine.server.db.service.WxUserService;
import cn.exploring.engine.server.db.util.LogRecordUtil;
import cn.exploring.engine.server.db.util.RegexUtil;
import cn.exploring.engine.server.wx.annotation.LoginUser;
import cn.exploring.engine.server.wx.dao.UserInfo;
import cn.exploring.engine.server.wx.dao.UserToken;
import cn.exploring.engine.server.wx.dao.WxLoginInfo;
import cn.exploring.engine.server.wx.service.CaptchaCodeManager;
import cn.exploring.engine.server.wx.service.UserTokenManager;
import cn.exploring.engine.server.wx.util.IpUtil;
import cn.exploring.engine.server.wx.util.SmsUtil;
import cn.exploring.engine.server.wx.util.WxResponseCode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
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 javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 鉴权服务
 */
@RestController
@RequestMapping("/wx/auth")
@Validated
public class WxAuthController {
    // 登录相关的log
    public final Log loginLogger = LogFactory.getLog(WxUser.class);


    @Autowired
    private WxUserService userService;

    @Autowired
    private WxMaService wxService;

    @Autowired
    private NotifyService notifyService;

    /**
     * 账号登录
     *
     * @param body    请求内容，{ username: xxx, password: xxx }
     * @param request 请求对象
     * @return 登录结果
     */
    @PostMapping("login")
    public Object login(@RequestBody String body, HttpServletRequest request) {
        String username = JacksonUtil.parseString(body, "username");
        String password = JacksonUtil.parseString(body, "password");
        if (username == null || password == null) {
            return ResponseUtil.badArgument();
        }

        List<WxUser> userList = userService.queryByUsername(username);
        WxUser user = null;
        if (userList.size() > 1) {
            return ResponseUtil.serious();
        } else if (userList.size() == 0) {
            return ResponseUtil.badArgumentValue();
        } else {
            user = userList.get(0);
        }

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        if (!encoder.matches(password, user.getPassword())) {
            return ResponseUtil.fail(WxResponseCode.AUTH_INVALID_ACCOUNT, "账号密码不对");
        }

        // userInfo
        UserInfo userInfo = new UserInfo();
        userInfo.setNickName(username);
        userInfo.setAvatarUrl(user.getAvatar());

        // token
        UserToken userToken = UserTokenManager.generateToken(user.getId(), "login");

        Map<Object, Object> result = new HashMap<Object, Object>();
        result.put("token", userToken.getToken());
        //result.put("tokenExpire", userToken.getExpireTime().toString());
        result.put("userInfo", userInfo);
        return ResponseUtil.ok(result);
    }

    /**
     * 微信登录
     *
     * @param wxLoginInfo 请求内容，{ code: xxx, userInfo: xxx }
     * @param request     请求对象
     * @return 登录结果
     */
    @PostMapping("login_by_weixin")
    public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {
        String code = wxLoginInfo.getCode();
        UserInfo userInfo = wxLoginInfo.getUserInfo();
        if (code == null || userInfo == null) {
            return ResponseUtil.badArgument();
        }

        String sessionKey = null;
        String openId = null;
        try {
            WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
            sessionKey = result.getSessionKey();
            openId = result.getOpenid();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (sessionKey == null || openId == null) {
            return ResponseUtil.fail();
        }

        Map<Object, Object> result = new HashMap<>();

        WxUser user = userService.queryByOid(openId);
        if (user == null) {
            user = new WxUser();
            user.setUsername(userInfo.getNickName());
            user.setPassword(openId);
            user.setWeixinOpenid(openId);
            user.setAvatar(userInfo.getAvatarUrl());
            user.setNickname(userInfo.getNickName());
            user.setGender(userInfo.getGender());
            user.setUserLevel((byte) 0);
            user.setStatus((byte) 0);
            user.setLastLoginTime(LocalDateTime.now());
            user.setLastLoginIp(IpUtil.client(request));
            userService.add(user);
        } else {
            if (StringUtils.isEmpty(user.getUsername())) {
                user.setUsername(userInfo.getNickName());
            }
            user.setLastLoginTime(LocalDateTime.now());
            user.setLastLoginIp(IpUtil.client(request));

            user.setDeleted(false);

            if (userService.updateById(user) == 0) {
                return ResponseUtil.updatedDataFailed();
            }

            if (!StringUtils.isEmpty(user.getMobile())) {
                userInfo.setMobilePhone(user.getMobile());
                userInfo.setMobile(user.getMobile());

                UserToken userToken = UserTokenManager.generateToken(user.getId(), sessionKey);
                result.put("token", userToken.getToken());
            }
        }

        userInfo.setUserId(user.getId());
        userInfo.setSessionKey(sessionKey);

        if (!StringUtils.isEmpty(user.getAvatar())) {
            userInfo.setAvatarUrl(user.getAvatar());
        }

        result.put("userInfo", userInfo);

        String logInfoData = LogRecordUtil.commonBuildLog("login succeed", user.getId(), "login in EMBA succeed",user);
        loginLogger.info(logInfoData);
        return ResponseUtil.ok(result);
    }


    /**
     * 请求验证码
     *
     * @param body 手机号码{mobile}
     * @return
     */
    @PostMapping("regCaptcha")
    public Object registerCaptcha(@RequestBody String body) {
        String phoneNumber = JacksonUtil.parseString(body, "mobile");
        if (StringUtils.isEmpty(phoneNumber)) {
            return ResponseUtil.badArgument();
        }
        if (!RegexUtil.isMobileExact(phoneNumber)) {
            return ResponseUtil.badArgumentValue();
        }

        String code = CharUtil.getRandomNum(6);
        /*if (!notifyService.isSmsEnable()) {
            return ResponseUtil.fail(AUTH_CAPTCHA_UNSUPPORT, "小程序后台验证码服务不支持");
        }
        notifyService.notifySmsTemplate(phoneNumber, NotifyType.CAPTCHA, new String[]{code});*/

        boolean successful = CaptchaCodeManager.addToCache(phoneNumber, code,1);
        if (!successful) {
            return ResponseUtil.fail(WxResponseCode.AUTH_CAPTCHA_FREQUENCY, "验证码未超时1分钟，不能发送");
        }

        try {
            SmsUtil.sendSms(phoneNumber,Integer.valueOf(code));
        }catch(Exception  e){
            e.printStackTrace();
        }


        return ResponseUtil.ok();
    }


    /**
     * 请求邮箱  验证码
     *
     * @param body 邮箱{email}
     * @return
     */
    @PostMapping("send_email_code")
    public Object sendEmailCode(@RequestBody String body) {
       /* String email = JacksonUtil.parseString(body, "email");
        if (StringUtils.isEmpty(email)) {
            return ResponseUtil.badArgument();
        }

       *//* String code = CharUtil.getRandomNum(6);
        WxNotifyG15 emailNotify = new WxNotifyG15();
        emailNotify.setType((byte)3);
        emailNotify.setSubType((byte)0);
        emailNotify.setNotifyKey(email);
        emailNotify.setNotifyValue(code);
        wxNotifyService.add(emailNotify);*//*

        //添加到缓存中
        boolean successful = CaptchaCodeManager.addToCacheDeleteExist(email, code,5);*/
        return ResponseUtil.ok();
    }


    /**
     *
     * 仅适用于 H5 戈壁挑战赛 报名
     * 邮箱注册
     *
     * @param body    请求内容
     *                {
     *                email: xxx,
     *                code: xxx
     *                }
     *                其中code是邮箱验证码
     * @param request 请求对象
     * @return 登录结果
     * 成功则
     * {
     * errno: 0,
     * errmsg: '成功',
     * data:
     * {
     * token: xxx,
     * tokenExpire: xxx,
     * userInfo: xxx
     * }
     * }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    @PostMapping("email_register")
    public Object email_register(@RequestBody String body, HttpServletRequest request) {
        String email = JacksonUtil.parseString(body, "email");
        String code = JacksonUtil.parseString(body, "code");
        String organizeId = JacksonUtil.parseString(body, "organizeId");

        if (StringUtils.isEmpty(email) || StringUtils.isEmpty(code)|| StringUtils.isEmpty(organizeId)) {
            return ResponseUtil.badArgument();
        }

        //判断验证码是否正确
       String cacheCode = CaptchaCodeManager.getCachedCaptcha(email);
        if (cacheCode == null || cacheCode.isEmpty() || !cacheCode.equals(code)) {
            return ResponseUtil.fail(WxResponseCode.AUTH_CAPTCHA_UNMATCH, "验证码错误");
        }

        WxUser user = null;
        List<WxUser> userList = userService.queryByEmail(email);
        if (userList == null || userList.size() == 0) {  //数据库没有，则添加一条数据
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            String encodedPassword = encoder.encode("gyh");
            user = new WxUser();
            user.setUsername("H5挑战者");
            user.setPassword(encodedPassword);
            user.setMobile("15111555511");
            user.setAvatar("https://yanxuan.nosdn.127.net/80841d741d7fa3073e0ae27bf487339f.jpg?imageView&quality=90&thumbnail=64x64");
            user.setNickname("H5挑战者");
            user.setGender((byte) 0);
            user.setUserLevel((byte) 0);
            user.setStatus((byte) 0);
            user.setLastLoginTime(LocalDateTime.now());
            user.setLastLoginIp(IpUtil.client(request));
            user.setIsH5Register(true);
            user.setEmail(email);
            //保存用户信息
            user = userService.add(user);
        }else{
            user = userList.get(0);
        }

        try {
            user.setDeleted(false);
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("H5登录/注册,给用户中心发送数据更新失败");
        }

        // userInfo
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(user.getId());
        userInfo.setNickName("H5挑战者");
        userInfo.setAvatarUrl(user.getAvatar());
        userInfo.setMobilePhone("15111555511");
        userInfo.setEmail(email);
        // token
        UserToken userToken = UserTokenManager.generateToken(user.getId(), "h5zhuce");
        Map<Object, Object> result = new HashMap<Object, Object>();
        result.put("token", userToken.getToken());
        //result.put("tokenExpire", userToken.getExpireTime().toString());
        result.put("userInfo", userInfo);
        return ResponseUtil.ok(result);
    }




    /**
     * 账号注册
     *
     * @param body    请求内容
     *                {
     *                username: xxx,
     *                password: xxx,
     *                mobile: xxx
     *                code: xxx
     *                }
     *                其中code是手机验证码，目前还不支持手机短信验证码
     * @param request 请求对象
     * @return 登录结果
     * 成功则
     * {
     * errno: 0,
     * errmsg: '成功',
     * data:
     * {
     * token: xxx,
     * tokenExpire: xxx,
     * userInfo: xxx
     * }
     * }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    @PostMapping("register")
    public Object register(@RequestBody String body, HttpServletRequest request) {
        String username = JacksonUtil.parseString(body, "username");
        String password = JacksonUtil.parseString(body, "password");
        String mobile = JacksonUtil.parseString(body, "mobile");
        String code = JacksonUtil.parseString(body, "code");
        String wxCode = JacksonUtil.parseString(body, "wxCode");

        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password) || StringUtils.isEmpty(mobile)
                || StringUtils.isEmpty(wxCode) || StringUtils.isEmpty(code)) {
            return ResponseUtil.badArgument();
        }

       /* List<WxUser> userList = userService.queryByUsername(username);
        if (userList.size() > 0) {
            return ResponseUtil.fail(WxResponseCode.AUTH_NAME_REGISTERED, "用户名已注册");
        }*/

        List<WxUser> userList = userService.queryByMobile(mobile);
        if (userList.size() > 0) {
            return ResponseUtil.fail(WxResponseCode.AUTH_MOBILE_REGISTERED, "手机号已注册");
        }
        if (!RegexUtil.isMobileExact(mobile)) {
            return ResponseUtil.fail(WxResponseCode.AUTH_INVALID_MOBILE, "手机号格式不正确");
        }
        //判断验证码是否正确
        String cacheCode = CaptchaCodeManager.getCachedCaptcha(mobile);
        if (cacheCode == null || cacheCode.isEmpty() || !cacheCode.equals(code)) {
            return ResponseUtil.fail(WxResponseCode.AUTH_CAPTCHA_UNMATCH, "验证码错误");
        }

        String openId = null;
        try {
            WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(wxCode);
            openId = result.getOpenid();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseUtil.fail(WxResponseCode.AUTH_OPENID_UNACCESS, "openid 获取失败");
        }
        userList = userService.queryByOpenid(openId);
        if (userList.size() > 1) {
            return ResponseUtil.serious();
        }
        if (userList.size() == 1) {
            WxUser checkUser = userList.get(0);
            String checkUsername = checkUser.getUsername();
            String checkPassword = checkUser.getPassword();
            if (!checkUsername.equals(openId) || !checkPassword.equals(openId)) {
                return ResponseUtil.fail(WxResponseCode.AUTH_OPENID_BINDED, "openid已绑定账号");
            }
        }

        WxUser user = null;
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encodedPassword = encoder.encode(password);
        user = new WxUser();
        user.setUsername(username);
        user.setPassword(encodedPassword);
        user.setMobile(mobile);
        user.setWeixinOpenid(openId);
        user.setAvatar("https://yanxuan.nosdn.127.net/80841d741d7fa3073e0ae27bf487339f.jpg?imageView&quality=90&thumbnail=64x64");
        user.setNickname(username);
        user.setGender((byte) 0);
        user.setUserLevel((byte) 0);
        user.setStatus((byte) 0);
        user.setLastLoginTime(LocalDateTime.now());
        user.setLastLoginIp(IpUtil.client(request));

        /*// 给新用户发送积分，调用crm判断用户是否是戈友
        RestTemplate restTemplate = new RestTemplate();
        String callCrmResult = restTemplate.getForObject(callCrmCenter+"/getByTel?tel="+mobile, String.class, String.class);
        boolean isGy = JacksonUtil.parseBoolean(callCrmResult,"isGy");
        // String gyProductName = JacksonUtil.parseString(callCrmResult, "gyProductName");
        if(isGy){  //如果是戈友，注册送xxx积分
            user.setScores(gySendScores);
            user.setIsGy(1);
            user.setGyId(JacksonUtil.parseString(callCrmResult, "gyId"));
            user.setGyProductName(JacksonUtil.parseString(callCrmResult, "gyProductName"));
        }else{ //如果不是戈友，注册送xxx积分
            user.setScores(sendScores);
        }
        user.setIsSendScores(1); //以赠送 戈币*/

        //保存用户信息
        userService.add(user);

        // userInfo
        UserInfo userInfo = new UserInfo();
        userInfo.setNickName(username);
        userInfo.setAvatarUrl(user.getAvatar());

        // token
        UserToken userToken = UserTokenManager.generateToken(user.getId(), "zhuce");

        Map<Object, Object> result = new HashMap<Object, Object>();
        result.put("token", userToken.getToken());
        //result.put("tokenExpire", userToken.getExpireTime().toString());
        result.put("userInfo", userInfo);
        return ResponseUtil.ok(result);
    }

    /**
     * 账号密码重置
     *
     * @param body    请求内容
     *                {
     *                password: xxx,
     *                mobile: xxx
     *                code: xxx
     *                }
     *                其中code是手机验证码，目前还不支持手机短信验证码
     * @param request 请求对象
     * @return 登录结果
     * 成功则 { errno: 0, errmsg: '成功' }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    @PostMapping("reset")
    public Object reset(@RequestBody String body, HttpServletRequest request) {
        String password = JacksonUtil.parseString(body, "password");
        String mobile = JacksonUtil.parseString(body, "mobile");
        String code = JacksonUtil.parseString(body, "code");

        String nickname = JacksonUtil.parseString(body, "nickname");  //昵称
        String username = JacksonUtil.parseString(body, "username");  //真实姓名
        String firm = JacksonUtil.parseString(body, "firm");      //公司名称
        String dutyTypesId = JacksonUtil.parseString(body, "dutyTypesId");    //职务id
        String dutyTypesName = JacksonUtil.parseString(body, "dutyTypesName");    //职务名称
        String cardTypeId = JacksonUtil.parseString(body, "cardTypeId");      //证件类型
        String cardId = JacksonUtil.parseString(body, "cardId");      //证件号
        String wxId = JacksonUtil.parseString(body, "wxId");          //微信号

        if (mobile == null || code == null || password == null) {
            return ResponseUtil.badArgument();
        }

        //判断验证码是否正确
        String cacheCode = CaptchaCodeManager.getCachedCaptcha(mobile);
        if (cacheCode == null || cacheCode.isEmpty() || !cacheCode.equals(code)) {
            return ResponseUtil.fail(WxResponseCode.AUTH_CAPTCHA_UNMATCH, "验证码错误");
        }

        List<WxUser> userList = userService.queryByMobile(mobile);
        WxUser user = null;
        if (userList.size() > 1) {
            return ResponseUtil.serious();
        } else if (userList.size() == 0) {
            return ResponseUtil.fail(WxResponseCode.AUTH_MOBILE_UNREGISTERED, "手机号未注册");
        } else {
            user = userList.get(0);
        }

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encodedPassword = encoder.encode(password);

        user.setPassword(encodedPassword);
        if(!StringUtils.isEmpty(nickname)) {
            user.setNickname(nickname);
        }
        if(!StringUtils.isEmpty(username)) {
            user.setUsername(username);
        }
        if(!StringUtils.isEmpty(firm)) {
            user.setFirm(firm);
        }
        if(dutyTypesId != null) {
            user.setDutyTypesId(Integer.valueOf(dutyTypesId));
        }
        if(!StringUtils.isEmpty(dutyTypesName)) {
            user.setDutyTypesName(dutyTypesName);
        }
        if(cardTypeId != null) {
            user.setCardTypeId(Integer.valueOf(cardTypeId));
        }
        if(!StringUtils.isEmpty(cardId)) {
            user.setDutyTypesName(cardId);
        }
        user.setWxId(wxId);


        if (userService.updateById(user) == 0) {
            return ResponseUtil.updatedDataFailed();
        }

        return ResponseUtil.ok();
    }

    @PostMapping("bindPhone")
    @Transactional(rollbackFor=Exception.class)
    public Object bindPhone(@RequestBody String body) {
        String encryptedData = JacksonUtil.parseString(body, "encryptedData");
        Integer userId = JacksonUtil.parseInteger(body, "userId");
        String sessionKey = JacksonUtil.parseString(body, "sessionKey");
        String iv = JacksonUtil.parseString(body, "iv");

        String phone = null;
        try {
            WxMaPhoneNumberInfo phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
            phone = phoneNumberInfo.getPhoneNumber();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseUtil.wxError();
        }

        WxUser user = userService.findById(userId);
        user.setMobile(phone);

        if (userService.updateById(user) == 0) {
            return ResponseUtil.updatedDataFailed();
        }

        //token 绑定成功 返回
        UserToken userToken = UserTokenManager.generateToken(user.getId(), sessionKey);
        Map<Object, Object> result = new HashMap<>();
        result.put("token", userToken.getToken());
        //result.put("tokenExpire", userToken.getExpireTime().toString());
        result.put("user", user);

        return ResponseUtil.ok(result);
    }

    @PostMapping("logout")
    public Object logout(@LoginUser Integer userId) {
        if (userId == null) {
            return ResponseUtil.unlogin();
        }
        UserTokenManager.removeToken(userId);
        return ResponseUtil.ok();
    }
}
