package tech.glinfo.enbao.modules.appuser.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestClientException;
import tech.glinfo.enbao.common.annotation.ApiLog;
import tech.glinfo.enbao.common.annotation.Login;
import tech.glinfo.enbao.common.annotation.LoginUser;
import tech.glinfo.enbao.common.controller.AbstractController;
import tech.glinfo.enbao.common.exception.RRException;
import tech.glinfo.enbao.common.utils.*;
import tech.glinfo.enbao.common.validator.Assert;
import tech.glinfo.enbao.common.validator.ValidatorUtils;
import tech.glinfo.enbao.modules.appuser.dto.AppUserDto;
import tech.glinfo.enbao.modules.appuser.entity.AppUserEntity;
import tech.glinfo.enbao.modules.appuser.form.*;
import tech.glinfo.enbao.modules.appuser.service.OtherAppUserService;
import tech.glinfo.enbao.modules.sh.entity.ShFamilyEntity;
import tech.glinfo.enbao.modules.sh.entity.ShFamilyMemberEntity;
import tech.glinfo.enbao.modules.sh.service.OtherShFamilyService;
import tech.glinfo.enbao.modules.sh.service.ShFamilyMemberService;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * APP用户
 *
 * @author linzhenjie
 * @email linzhenjie@gltech.com
 * @date 2019-11-14 16:06:47
 */
@RestController
@RequestMapping("/user")
@Api(value = "APP用户接口",description="APP用户相关信息接口")
public class AppUserController extends AbstractController {

    @Autowired
    private OtherAppUserService otherAppUserService;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private OtherShFamilyService otherShFamilyService;

    @Autowired
    private ShFamilyMemberService shFamilyMemberService;

    @Value("${wechat.appId}")
    private String appId;

    @Value("${wechat.appSecret}")
    private String appSecret;


    @PostMapping("register")
    @ApiOperation("用户注册")
    @ApiLog("用户注册")
    public R register(@RequestBody WxRegisterForm form){

        //表单校验
        ValidatorUtils.validateEntity(form);
        AppUserEntity userEntity = otherAppUserService.queryByPhone(form.getPhone());
        if (userEntity != null) {
            return R.error("手机号码已经注册过了");
        }

       /* String vcode = redisUtils.get(Constant.VCODE_KEY + Constant.VcodeType.REG.getValue() + "_" + form.getPhone());
        if(!form.getVcode().equals(vcode)) {
            throw new RRException("验证码输入不正确");
        }*/

        AppUserEntity user = new AppUserEntity();
        user.setPhone(form.getPhone());
        user.setNickname(form.getNickname());
        user.setOpenId(form.getOpenId());
        user.setHeadImgUrl(form.getHeadImgUrl());
        user.setPassword(DigestUtils.sha256Hex("123456"));
        otherAppUserService.save(user);
        ShFamilyEntity family = new ShFamilyEntity();
        family.setName("我的家");
        family.setUserId(user.getId());
        otherShFamilyService.save(family);
        //新增家庭成员
        ShFamilyMemberEntity member = new ShFamilyMemberEntity();
        member.setFamilyId(family.getId());
        member.setUserId(user.getId());
        member.setIsAdmin(1);//管理员
        member.setStatus(2);
        shFamilyMemberService.save(member);
        String token = jwtUtils.generateToken(user.getId() +"#"+user.getPhone());
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("expire", jwtUtils.getExpire());
        map.put("user", user);
        return R.ok(map);
    }

    @Login
    @PostMapping("update")
    @ApiOperation("用户信息修改")
//    @ApiLog("用户信息修改")
    public R update(@RequestBody UpdateForm form){

        AppUserEntity user = new AppUserEntity();
//        AppUserEntity user1 = otherAppUserService.getById(form.getId());

        //修改手机号码
        if(StringUtils.isNotEmpty(form.getPhone())) {
            AppUserEntity userEntity = otherAppUserService.queryByPhone(form.getPhone());
            if (userEntity != null) {
                throw new RRException("手机号码已经注册过了");
            }
            String vcode1 = redisUtils.get(Constant.VCODE_KEY + Constant.VcodeType.OTHERS.getValue() + "_" + form.getOphone());
            if(!form.getVcode1().equals(vcode1)) {
                throw new RRException("原手机号码验证码输入不正确");
            }
            String vcode2 = redisUtils.get(Constant.VCODE_KEY + Constant.VcodeType.OTHERS.getValue() + "_" + form.getPhone());
            if(!form.getVcode2().equals(vcode2)) {
                throw new RRException("新手机号码验证码输入不正确");
            }

        }

        //修改密码走忘记密码接口
        try {
            BeanUtils.copyProperties(user, form);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        if(otherAppUserService.updateById(user)) {
            return R.ok().put("user", otherAppUserService.getById(user.getId()));
        }
        return R.error("更新用户信息出错了");
    }

    @PostMapping("login")
    @ApiOperation("用户登录")
//    @ApiLog("用户登录")
    public R login(@RequestBody Map<String, Object> params) {
        String openid = (String) params.get("openid");
        String code = (String) params.get("code");
        String unionid = null;
        if(StringUtils.isEmpty(openid) && StringUtils.isNotEmpty(code)) {
            String info = getWechatInfo(code);
            if(info != null) {
                JSONObject json = JSONObject.parseObject(info);
                openid = json.getString("openid");
                unionid = json.getString("unionid");
                if(StringUtils.isNotEmpty(unionid)) {
                    openid = unionid;
                }
            } else {
                return R.error("查找用户openid失败！");
            }
        }
        AppUserEntity  member = otherAppUserService.getOne(new QueryWrapper<AppUserEntity>().eq("open_id", openid));
        if (member != null) {
            String token = jwtUtils.generateToken(member.getId() +"#"+member.getPhone());
            Map<String, Object> map = new HashMap<>();
            map.put("token", token);
//            map.put("expire", jwtUtils.getExpire());
            map.put("user", member);
            return R.ok(map);
        }else {
            return R.error("当前微信暂未绑定任何账号！");
        }
    }
    /**
     * 忘记密码
     */
    @PostMapping("forget")
    @ApiOperation("忘记密码/修改密码")
    @ApiLog("忘记密码/修改密码")
    public R forget(@RequestBody ForgetForm form){
        //表单校验
        ValidatorUtils.validateEntity(form);

        String vcode = redisUtils.get(Constant.VCODE_KEY + Constant.VcodeType.FORGET.getValue() + "_" + form.getPhone());
        if(!form.getVcode().equals(vcode)) {
            throw new RRException("验证码输入不正确");
        }

        //用户登录
        AppUserEntity user = otherAppUserService.queryByPhone(form.getPhone());

        Assert.isNull(user, "手机号码不存在");

        AppUserEntity user1 = new AppUserEntity();
        user1.setId(user.getId());
        user1.setPassword(DigestUtils.sha256Hex(form.getPassword()));

        boolean result = otherAppUserService.updateById(user1);

        return result?R.ok():R.error("操作失败");
    }
    /**
     * 快捷登录
     */
    @PostMapping("quick_login")
    @ApiOperation("快捷登录")
    @ApiLog("用户快捷登录")
    public R quickLogin(@RequestBody QuickLoginForm form) throws IOException {
        //表单校验
        ValidatorUtils.validateEntity(form);

        String vcode = redisUtils.get(Constant.VCODE_KEY + Constant.VcodeType.LOGIN.getValue() + "_" + form.getPhone());
        if(!form.getVcode().equals(vcode)) {
            throw new RRException("验证码输入不正确");
        }

        //用户登录
        AppUserEntity user = otherAppUserService.quickLogin(form);
        if(user == null) {
            AppUserEntity user1 = new AppUserEntity();
            logger.info("手机号码不存在，直接注册：{}", form.getPhone());
            user1.setPhone(form.getPhone());
            String password = StringUtils.random(6, StringUtils.RandomType.INT);
            user1.setNickname("暂未设置");
            user1.setPassword(DigestUtils.sha256Hex(password));
            otherAppUserService.save(user1);
            return R.ok(genTokenAndGetData(user1, form.getClientId(), form.getRole()));
        }
        /*String openId = null;
        if (StringUtils.isNotBlank(form.getCode())) {
            if (StringUtils.isBlank(form.getKey())) {
                return R.error("登录失败！key为空");
            }
            openId = getOpenid(form.getCode(), form.getKey());
        }*/
        Map<String, Object> map = genTokenAndGetData(user, form.getClientId(), form.getRole());
//        map.put("openId", openId);
        return R.ok(map);
    }
    /**
     * 快捷登录
     */
    @PostMapping("wxOrAppleLogin")
    @ApiOperation("微信登录")
    @ApiLog("用户微信登录")
    public R wxOrAppleLogin(@RequestBody Map<String, Object> params) {

        String unionid = (String) params.get("unionid");
        String openid = (String) params.get("openid");
        String clientId = (String) params.get("clientId");
        Integer role = (Integer) params.get("role");

        if (StringUtils.isNotEmpty(unionid)) {//苹果登录
            //open_id 存unionID
            AppUserEntity user = otherAppUserService.getOne(new QueryWrapper<AppUserEntity>().eq("unionid", unionid));
            if (user != null) {
//                map.put("openId", openId);
                return R.ok(genTokenAndGetData(user, clientId, role));
            } else {
                return R.error("当前Apple暂未绑定任何账号！");

            }
        } else if (StringUtils.isNotEmpty(openid)) {//微信登录
            //open_id 存unionID
            AppUserEntity user = otherAppUserService.getOne(new QueryWrapper<AppUserEntity>().eq("open_id", openid));
            if (user != null) {
                String token = jwtUtils.generateToken(user.getId() +"#"+user.getPhone());
                Map<String, Object> map = new HashMap<>();
                map.put("token", token);
                map.put("expire", jwtUtils.getExpire());
                map.put("user", user);
                return R.ok(map);
            } else {
                return R.ok("当前微信暂未绑定任何账号！");
            }
        } else {
            return R.error("缺少参数，请核查！");
        }
    }
    /**
     * 快捷登录
     */
    @PostMapping("bindPhone")
    @ApiOperation("微信绑定账户")
    @ApiLog("微信绑定账户")
    public R bindPhone(@RequestBody Map<String, Object> params) {

        String unionid = (String) params.get("unionid");
        String openid = (String) params.get("openid");
        String clientId = (String) params.get("clientId");
        String phone = (String) params.get("phone");
        String vcode = (String) params.get("vcode");
        Integer role = (Integer) params.get("role");

        if (StringUtils.isBlank(phone, vcode)) {
            return R.error("缺少参数，请核查！");
        }
        if(StringUtils.isEmpty(unionid) && StringUtils.isEmpty(openid)) {
            return R.error("缺少参数，请核查！");
        }

        String code = redisUtils.get(Constant.VCODE_KEY + Constant.VcodeType.OTHERS.getValue() + "_" + phone);
        if(!vcode.equals(code)) {
            throw new RRException("验证码输入不正确");
        }

        AppUserEntity _user = otherAppUserService.queryByPhone(phone);
        if (_user == null) {
            logger.info("手机号码不存在，直接注册：{}", phone);
            AppUserEntity user = new AppUserEntity();
            user.setPhone(phone);
            user.setNickname(StringUtils.isNotEmpty(unionid)?"微信用户":"苹果用户");
            if(StringUtils.isNotEmpty(unionid)) {
                user.setUnionId(unionid);
            }
            if(StringUtils.isNotEmpty(openid)) {
                user.setOpenId(openid);
            }
            //随机密码
            user.setPassword(DigestUtils.sha256Hex(StringUtils.random(6, StringUtils.RandomType.INT)));
            otherAppUserService.save(user);
            return R.ok(genTokenAndGetData(user, clientId, role));

        } else {
            AppUserEntity update = new AppUserEntity();
            update.setId(_user.getId());
            if(StringUtils.isNotEmpty(unionid)) {
                update.setUnionId(unionid);
            }
            if(StringUtils.isNotEmpty(openid)) {
                update.setOpenId(openid);
            }
            otherAppUserService.updateById(update);
        }

        return R.ok(genTokenAndGetData(_user, clientId, role));
    }

    private Map<String, Object> genTokenAndGetData(AppUserEntity user, String clientId, Integer role) {
        String token = jwtUtils.generateToken(user.getId() +"#"+user.getPhone());
        //重新取数据
        if(StringUtils.isEmpty(user.getHeadImgUrl())) {
            user = otherAppUserService.getById(user.getId());
        }
        String activeProfile = SpringContextUtils.getActiveProfile();
        logger.info("当前环境：{}", activeProfile);
        /*if (!"dev".equals(activeProfile) && user.getToken() != null&&!redisUtils.hasKey(AppContants.APP_NOTOKEN_EXPIRED+user.getPhone())) {
            redisUtils.set(AppContants.APP_TOKEN_EXPIRED+user.getToken(), "TokenExpired1", 60 * 60 * 24 * 7);
        }*/
        updateUser(null, clientId, user.getId(), token);
//        Map<String, Object> map = getLoginData(user, token);

        return null;
    }

    /**
     * 更新用户信息
     * @param openId
     * @param clientId
     * @param id
     */
    private void updateUser(String openId, String clientId, Integer id, String token) {
        AppUserEntity entity = new AppUserEntity();
        entity.setId(id);
        if(StringUtils.isNotBlank(clientId)) {
            entity.setThirdId(clientId);//客户端ID，用于消息推送
        }
        if(StringUtils.isNotBlank(openId)) {
            entity.setOpenId(openId);//微信openID
        }
        entity.setLastLoginTime(new Date());
//        entity.setToken(token);
        otherAppUserService.updateById(entity);

    }

    /**
     * 获取登录数据
     * @param user
     * @return
     */
    private Map<String, Object> getLoginData(AppUserEntity user, String token) {

        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("expire", jwtUtils.getExpire());
        AppUserDto data = new AppUserDto();
        try {
            BeanUtils.copyProperties(data, user);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        map.put("data", data);
        return map;
    }

    @Login
    @GetMapping("/info")
    @ApiOperation("APP用户信息")
    @ApiLog("APP用户信息")
    public R info(@LoginUser AppUserEntity user) {
        AppUserDto data = new AppUserDto();
        try {
            BeanUtils.copyProperties(data, user);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return R.ok().put("data", data);
    }

    @Login
    @GetMapping("/getInfoById/{id}")
    @ApiOperation("通过ID获取APP用户信息")
    @ApiLog("通过ID获取APP用户信息")
    public R info(@PathVariable("id") Integer id) {
        AppUserEntity user = otherAppUserService.getById(id);
        if(user == null) {
            throw new RRException("用户不存在");
        }
        Map<String, Object> data = new HashMap<>();
        data.put("uid", user.getId());
        data.put("nickname", user.getNickname());
        data.put("headPortrait", user.getHeadImgUrl());
        return R.ok().put("data", data);
    }

    @Login
    @PostMapping("/closeAccount")
    @ApiOperation("注销账号")
    @ApiLog("注销账号")
    public R closeAccount(@LoginUser AppUserEntity user) {
        user.setPhone("0" + user.getPhone());
        user.setStatus(2);
        otherAppUserService.updateById(user);
        return R.ok();
    }

    /**
     * 校验用户是否存在，存在直接登录
     * @param params
     * @return
     */
    @PostMapping("getMiniInfo")
    public R getMiniInfo(@RequestBody Map<String, Object> params)  {
        String code = (String) params.get("code");
        //wx接口路径
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
        //使用HttpClient发送请求
        CloseableHttpClient httpclient = HttpClients.createDefault();
        //发送Get请求
        HttpGet request = new HttpGet(url);
        request.addHeader("Content-Type", "application/json");
        //获得响应
        String result = getWechatInfo(code);// 转成string
        if(result != null) {
            return R.ok(JSONObject.parseObject(result));
//            String openid = jsonObject.get("openid").toString();
//            logger.info("openid" + openid);
        }
        return R.error();
    }

    private String getWechatInfo(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
        //使用HttpClient发送请求
        CloseableHttpClient httpclient = HttpClients.createDefault();
        //发送Get请求
        HttpGet request = new HttpGet(url);
        request.addHeader("Content-Type", "application/json");
        //获得响应
        String result = null;// 转成string
        try {
            CloseableHttpResponse response = httpclient.execute(request);
            //拿到响应体
            HttpEntity httpEntity = response.getEntity();
            //使用工具转换
            result = EntityUtils.toString(httpEntity, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        logger.info("result:{}", result);
        return result;
    }

    /**
     * 解密
     * @param sessionKey
     * @param encryptedData
     * @param iv
     * @return
     */
    private JSONObject decode(String sessionKey,String encryptedData,String iv) {
        byte[] encrypData = Base64.decodeBase64(encryptedData);
        byte[] ivData = Base64.decodeBase64(iv);
        byte[] sessionKeyB = Base64.decodeBase64(sessionKey);
        Security.addProvider(new BouncyCastleProvider());
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
        byte[] doFinal = new byte[0];
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyB, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            doFinal = cipher.doFinal(encrypData);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        if(doFinal != null && doFinal.length > 0) {
            String result = new String(doFinal);
            return JSONObject.parseObject(result);
        }

        return new JSONObject();
    }

    @PostMapping("getUserPhoneAndRegister")
    public R getUserPhoneAndRegister(@RequestBody Map<String, Object> params)  {

        String sessionKey = (String) params.get("sessionKey");
        String encryptedData = (String) params.get("encryptedData");
        String iv = (String) params.get("iv");
        String unionid = (String) params.get("unionid");

        JSONObject userInfo = decode(sessionKey, encryptedData, iv);
        String phoneNumber = userInfo.getString("phoneNumber");
        Map<String, Object> result = new HashMap<>();
        if(StringUtils.isNotEmpty(phoneNumber)) {
            AppUserEntity _user = otherAppUserService.queryByPhone(phoneNumber);
            if(_user != null) {//已经注册过了，直接登录即可
                String token = jwtUtils.generateToken(_user.getId() +"#"+_user.getPhone());
                result.put("token", token);
                _user.setOpenId(unionid);
                result.put("user", _user);
                AppUserEntity userEntity = new AppUserEntity();
                userEntity.setId(_user.getId());
                userEntity.setOpenId(unionid);
                userEntity.setLastLoginTime(new Date());
                otherAppUserService.updateById(userEntity);
            } else {//注册用户
                AppUserEntity user = new AppUserEntity();
                user.setPhone(phoneNumber);
                user.setNickname("暂未设置");
                user.setOpenId(unionid);
                user.setPassword(DigestUtils.sha256Hex(StringUtils.random(6, StringUtils.RandomType.INT)));
                otherAppUserService.save(user);

                ShFamilyEntity family = new ShFamilyEntity();
                family.setName("我的家");
                family.setUserId(user.getId());
                otherShFamilyService.save(family);
                //新增家庭成员
                ShFamilyMemberEntity member = new ShFamilyMemberEntity();
                member.setFamilyId(family.getId());
                member.setUserId(user.getId());
                member.setIsAdmin(1);//管理员
                member.setStatus(2);
                shFamilyMemberService.save(member);

                String token = jwtUtils.generateToken(user.getId() +"#"+phoneNumber);
                result.put("token", token);

                result.put("user", otherAppUserService.getById(user.getId()));
            }
        } else {
            return R.error("获取手机号码错误");
        }

        return R.ok(result);
    }

}
