package com.m2pool.auth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.A;
import com.m2pool.auth.entity.*;
import com.m2pool.common.core.RedisTransKey;
import com.m2pool.common.core.Result.R;
import com.m2pool.common.core.constant.Constants;
import com.m2pool.common.core.constant.SecurityConstants;
import com.m2pool.common.core.constant.UserConstants;
import com.m2pool.common.core.enums.UserStatus;
import com.m2pool.common.core.exception.ServiceException;
import com.m2pool.common.core.text.Convert;
import com.m2pool.common.core.utils.DateUtils;
import com.m2pool.common.core.utils.ServletUtils;
import com.m2pool.common.core.utils.StringUtils;
import com.m2pool.common.core.utils.ip.IpUtils;
import com.m2pool.common.core.utils.sign.RsaUtils;
import com.m2pool.common.core.web.Result.AjaxResult;
import com.m2pool.common.redis.service.RedisService;
import com.m2pool.common.security.service.TokenService;
import com.m2pool.common.security.utils.SecurityUtils;
import com.m2pool.system.api.RemoteLogService;
import com.m2pool.system.api.RemoteUserService;
import com.m2pool.system.api.entity.SysLogininfor;
import com.m2pool.system.api.entity.SysUser;
import com.m2pool.system.api.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Update;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import javax.validation.Valid;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * @Description 登录校验方法
 * @Date 2024/6/12 16:19
 * @Author dy
 */
@Slf4j
@Component
public class SysLoginService {

    @Autowired
    private RemoteLogService remoteLogService;

    @Autowired
    private RemoteUserService remoteUserService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisService redisService;

    //public static String PWD_REGEX="^(?![A-Za-z0-9]+$)(?![a-z0-9\\W]+$)(?![A-Za-z\\W]+$)(?![A-Z0-9\\W]+$)[a-zA-Z0-9\\W]{8,32}$";
    public static String PWD_REGEX="^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*()./_])[A-Za-z\\d!@#$%^&*()./_]{8,32}$";

    public static String EMAIL_REGEX="^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
    /**
     * 登录
     */
    public R<?> login(LoginBody loginBody)
    {
        //邮箱
        String email = loginBody.getUserName();

        //String password= loginBody.getPassword();
        String password="";

        try {
            password = RsaUtils.decryptByPrivateKey(loginBody.getPassword());
            password = StringUtils.clean(password);
        }catch (Exception e){
            return R.fail(401,"加密密码传参有误");
        }

        String emailCode = loginBody.getCode();
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(email, password))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "邮箱/密码必须填写");
            throw new ServiceException("邮箱/密码必须填写");
        }
        if(!StringUtils.isBlank(email)){
            if(!email.matches(EMAIL_REGEX)){
                throw new ServiceException("邮箱格式错误");
            }
        }else {
            throw new ServiceException("邮箱为必填项");
        }

        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "用户密码不在指定范围");
            throw new ServiceException("用户密码不在指定范围");
        }


        //if(!"admin".equals(username)){
        //    if(!password.matches(PWD_REGEX)){
        //        recordLogininfor(username, Constants.LOGIN_FAIL, "密码格式错误");
        //        throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符，8到32位");
        //    }
        //}
        //todo 可以添加校验验证码的功能
        if("1328642438@qq.com".equals(email)){
            //不做处理 进入后续登录流程
        }else if(redisService.hasKey(RedisTransKey.getLoginKey(email))){

            Object o = redisService.getCacheObject(RedisTransKey.getLoginKey(email));//user:emailCode:email


            EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class);
            //验证验证码
            if(emailCode.equals(emailCodeEntity.getEmailCode())){
               //不做处理 进入后续登录流程
            }else {
                throw new ServiceException("验证码错误");
            }
        }else {
            throw new ServiceException("验证码未获取或已过期，请重新获取验证码");
        }

        // 查询用户信息
        R<LoginUser> userResult = remoteUserService.getUserInfo(email, SecurityConstants.INNER);

        if (R.FAIL == userResult.getCode())
        {
            throw new ServiceException(userResult.getMsg());
        }

        if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "登录用户不存在");
            throw new ServiceException("登录用户：" + email + " 不存在");
        }
        LoginUser userInfo = userResult.getData();
        //Long userid = userInfo.getUserid();
        SysUser user = userResult.getData().getSysUser();
        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "对不起，您的账号已被删除");
            throw new ServiceException("对不起，您的账号：" + email + " 已被删除");
        }
        if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "用户已停用，请联系管理员");
            throw new ServiceException("对不起，您的账号：" + email + " 已停用");
        }
        //根据user:userId:lock
        if(redisService.hasKey("user:"+email+":lock")){
            //账户输入密码错误
            throw new ServiceException("该账户因密码错误多次被暂时限制登陆24h！");
        }

        if (!SecurityUtils.matchesPassword(password, user.getPassword()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "用户密码错误");
            //redis 记录用户密码错误次数 大于5时锁定账户24小时
            int count = 0;
            if(redisService.hasKey("user:"+email+":pwdError")){
                //判断key是否过期
                long expire = redisService.getExpire("user:" + email + ":pwdError", TimeUnit.SECONDS);
                if(expire > 0){
                    count = Convert.toInt(redisService.getCacheObject("user:"+email+":pwdError"));
                }
            }
            count++;
            if(count == 1){
                redisService.setCacheObject("user:"+email+":pwdError",count,1L,TimeUnit.HOURS);
            }else if(count < 6){
                long expire = redisService.getExpire("user:" + email + ":pwdError", TimeUnit.SECONDS);
                System.out.println(expire);
                redisService.setCacheObject("user:"+email+":pwdError",count,redisService.getExpire("user:"+email+":pwdError",TimeUnit.SECONDS),TimeUnit.SECONDS);
            }
            if(count >= 5){
                //锁定用户账号 并清除输入错误次数的记录
                redisService.setCacheObject("user:"+email+":lock", email, 24L, TimeUnit.HOURS);
                //清除输入错误次数的记录
                redisService.deleteObject("user:"+email+":pwdError");
            }
            throw new ServiceException("密码错误,一小时内错误五次之后锁定账户24h,当前已失败"+count+"次");
        }

        //todo 判断当前ip和用户上一次登陆ip是否一致
        //if(StringUtils.isNotNull(userResult.getData().getSysUser().getLoginIp()) && StringUtils.isNotBlank(userResult.getData().getSysUser().getLoginIp())){
        //    //上一次ip不为空 判断
        //    if(StringUtils.isBlank(loginBody.getUuid()) || StringUtils.isNull(loginBody.getUuid())){
        //
        //        //没传uuid说明是刚发起的登录请求 需要判断ip是否发生变化
        //        String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
        //        if(!ipAddr.equals(userResult.getData().getSysUser().getLoginIp())){
        //            return R.fail(201,"登录ip发生变化，请通过验证再登录");
        //        }
        //    }else {
        //        //传了uuid说明是验证登录 验证
        //        if (StringUtils.isBlank(loginBody.getCode()))
        //        {
        //            return  R.fail("验证码不能为空");
        //        }
        //
        //        String verifyKey = Constants.CAPTCHA_CODE_KEY + loginBody.getUuid();
        //        String captcha = redisService.getCacheObject(verifyKey);
        //
        //        if(StringUtils.isNull(captcha)){
        //            return  R.fail("验证码已失效");
        //        }
        //        //redisService.deleteObject(verifyKey);
        //
        //        if (!loginBody.getCode().equalsIgnoreCase(captcha))
        //        {
        //            return  R.fail("验证码错误");
        //        }
        //        redisService.deleteObject(verifyKey);
        //    }
        //
        //}


        recordLogininfor(email, Constants.LOGIN_SUCCESS, "登录成功");

        //修改数据库中的用户登录次数和最后一次登录的ip
        user.setLoginCount(user.getLoginCount()+1);
        user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        user.setLoginDate(DateUtils.getNowDate());

        //todo 单独存储用户的登录时间和ip
        remoteUserService.updateUserInfo(user);

        return R.success(tokenService.createToken(userInfo));
    }


    public R<Map<String,Object>> managerLogin(LoginManageBody loginManageBody){
        //邮箱
        String email = loginManageBody.getUserName();

        //String password= loginBody.getPassword();
        String password="";

        try {
            password = RsaUtils.decryptByPrivateKey(loginManageBody.getPassword());
            password = StringUtils.clean(password);
        }catch (Exception e){
            return R.fail(401,"加密密码传参有误");
        }
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(email, password))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "邮箱/密码必须填写");
            throw new ServiceException("邮箱/密码必须填写");
        }
        if(!StringUtils.isBlank(email)){
            if(!email.matches(EMAIL_REGEX)){
                throw new ServiceException("邮箱格式错误");
            }
        }else {
            throw new ServiceException("邮箱为必填项");
        }

        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "用户密码不在指定范围");
            throw new ServiceException("用户密码不在指定范围");
        }
        // 查询用户信息
        R<LoginUser> userResult = remoteUserService.getUserInfo(email, SecurityConstants.INNER);

        if (R.FAIL == userResult.getCode())
        {
            throw new ServiceException(userResult.getMsg());
        }

        if (StringUtils.isNull(userResult.getData()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "登录用户不存在");
            throw new ServiceException("登录用户：" + email + " 不存在");
        }
        LoginUser userInfo = userResult.getData();
        SysUser user = userResult.getData().getSysUser();
        Set<String> roles = userInfo.getRoles();



        if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "对不起，您的账号已被删除");
            throw new ServiceException("对不起，您的账号：" + email + " 已被删除");
        }
        if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            recordLogininfor(email, Constants.LOGIN_FAIL, "用户已停用，请联系管理员");
            throw new ServiceException("对不起，您的账号：" + email + " 已停用");
        }

        if(!SecurityUtils.matchesPassword(password, user.getPassword())){
            throw new ServiceException("密码错误,请重新输入");
        }

        //必须是管理员才能登录
        if(!roles.contains("3")){
            recordLogininfor(email, Constants.LOGIN_FAIL, "对不起，您的账号没有权限不能登录后台管理");
            throw new ServiceException("对不起，您的账号：" + email + " 没有权限不能登录后台管理");
        }

        recordLogininfor(email, Constants.LOGIN_SUCCESS, "后台管理系统用户登录成功");

        return R.success(tokenService.createToken(userInfo));
    }

    public void logout(String loginName)
    {
        recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
    }

    /**
     * 注册
     */
    public void register(RegisterBody registerBody)
    {
        String password = registerBody.getPassword();
        try {
            password = RsaUtils.decryptByPrivateKey(registerBody.getPassword());
            password = StringUtils.clean(password);
        }catch (Exception e){
            throw new ServiceException("加密密码传参有误",401);
        }

        String email = registerBody.getEmail();


        String phone = registerBody.getPhone();
        String emailCode = registerBody.getEmailCode();
        //String key = username+"+"+email;
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(email, password))
        {
            throw new ServiceException("邮箱/密码必须填写");
        }
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            throw new ServiceException("密码长度必须在8到32个字符之间");
        }
        if(!password.matches(PWD_REGEX)){
            throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符，8到32位");
        }

        //todo 手机号验证
        if(!StringUtils.isEmpty(phone)){
            String phoneRex = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$";

            if (!phone.matches(phoneRex))
            {
                throw new ServiceException("手机号格式不正确");
            }
        }
        if(!StringUtils.isBlank(email)){
            if(!email.matches(EMAIL_REGEX)){
                throw new ServiceException("邮箱格式错误");
            }
        }else {
            throw new ServiceException("邮箱为必填项");
        }

        if(redisService.hasKey(RedisTransKey.getEmailKey(email))){

            Object o = redisService.getCacheObject(RedisTransKey.getEmailKey(email));//user:emailCode:email

            EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class);

            //验证验证码
            if(emailCode.equals(emailCodeEntity.getEmailCode())){
                // 注册用户信息
                SysUser sysUser = new SysUser();
                sysUser.setUserName(email);
                sysUser.setNickName(email);
                sysUser.setPhone(phone);
                sysUser.setEmail(email);
                sysUser.setPassword(SecurityUtils.encryptPassword(password));
                R<?> registerResult = remoteUserService.registerUserInfo(sysUser);

                if (R.FAIL == registerResult.getCode())
                {
                    throw new ServiceException(registerResult.getMsg());
                }
                recordLogininfor(email, Constants.REGISTER, "注册成功");
            }else {
                throw new ServiceException("验证码错误");
            }
        }else {
            throw new ServiceException("验证码未获取或已过期，请重新获取验证码");
        }

    }


    /**
     *
     */
    public void resetPwd(ResetPwdBody resetPwdBody,boolean isUpdate)
    {
        log.info("验证码{},重置密码{},重置者邮箱{}",resetPwdBody.getResetPwdCode(),resetPwdBody.getPassword(),resetPwdBody.getEmail());
        String password = resetPwdBody.getPassword();
        try {
            password = RsaUtils.decryptByPrivateKey(resetPwdBody.getPassword());
        }catch (Exception e){
            throw new ServiceException("加密密码传参有误",401);
            //password = resetPwdBody.getPassword();
        }

        String email = resetPwdBody.getEmail();
        String resetPwdCode = resetPwdBody.getResetPwdCode();
        String updatePwdCode = resetPwdBody.getUpdatePwdCode();
        // 邮箱或密码为空 错误
        if (StringUtils.isAnyBlank(email, password))
        {
            throw new ServiceException("邮箱/新密码不能为空");
        }
        if(!StringUtils.isBlank(email)){
            if(!email.matches(EMAIL_REGEX)){
                throw new ServiceException("邮箱格式错误");
            }
        }else {
            throw new ServiceException("邮箱为必填项");
        }

        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            throw new ServiceException("密码长度必须在8到32个字符之间");
        }
        //password格式校验
        if(!password.matches(PWD_REGEX)){
            throw new ServiceException("密码格式错误,密码应包含大写字母、小写字母、数字以及特殊字符，8到32位");
        }
        R<LoginUser> userResult = remoteUserService.getUserInfo(email, SecurityConstants.INNER);

        if (R.FAIL == userResult.getCode())
        {
            throw new ServiceException("服务器请求失败请稍后再试");
        }

        if(SecurityUtils.matchesPassword(password,userResult.getData().getSysUser().getPassword())){
            throw new ServiceException("新密码不能与原密码一致");
        }

        //if(resetPwdBody.getGCode() == 0){
        //    throw new ServiceException("验证码不能为空");
        //}
        //谷歌验证码校验
        //R<?> checkCodeResult = remoteUserService.checkGoogleCode(resetPwdBody.getGCode(),SecurityConstants.INNER);
        //
        //if (R.FAIL == checkCodeResult.getCode())
        //{
        //    System.out.println(checkCodeResult);
        //    throw new ServiceException(checkCodeResult.getMsg());
        //}
        //
        //System.out.println(checkCodeResult);

        log.info("重置密码{},修改验证码{},重置验证码{}",isUpdate,redisService.hasKey(RedisTransKey.getResetPwdKey(email)),redisService.hasKey(RedisTransKey.getUpdatePwdKey(email)));
        // 如果是true代表就是忘记密码    false 就是个人中心重置密码
        if(isUpdate && redisService.hasKey(RedisTransKey.getResetPwdKey(email))){
            Object o = redisService.getCacheObject(RedisTransKey.getResetPwdKey(email));//user:emailCode:username

            EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class);
            if (email.equals(emailCodeEntity.getEmail())) {
                //邮箱必须和刚刚传的一致
                //验证验证码
                if(resetPwdCode.equals(emailCodeEntity.getEmailCode())){
                    // 重置用户密码
                    SysUser sysUser = new SysUser();
                    sysUser.setEmail(email);
                    sysUser.setPassword(SecurityUtils.encryptPassword(password));

                    R<?> resetPwdResult = remoteUserService.resetPwdByEmail(sysUser);

                    if (R.FAIL == resetPwdResult.getCode())
                    {
                        throw new ServiceException(resetPwdResult.getMsg());
                    }
                    //recordLogininfor(username, Constants.REGISTER, "注册成功");
                }else {
                    throw new ServiceException("邮箱验证码错误");
                }
            }else {
                throw new ServiceException("请勿修改已输入的邮箱");
            }
        }else if(!isUpdate && redisService.hasKey(RedisTransKey.getUpdatePwdKey(email))){
            Object o = redisService.getCacheObject(RedisTransKey.getUpdatePwdKey(email));//user:emailCode:username

            EmailCodeEntity emailCodeEntity = JSON.parseObject(JSON.toJSONString(o), EmailCodeEntity.class);
            if (email.equals(emailCodeEntity.getEmail())) {
                //邮箱必须和刚刚传的一致
                //验证验证码
                if(updatePwdCode.equals(emailCodeEntity.getEmailCode())){
                    // 重置用户密码
                    SysUser sysUser = new SysUser();
                    sysUser.setEmail(email);
                    sysUser.setPassword(SecurityUtils.encryptPassword(password));

                    R<?> resetPwdResult = remoteUserService.resetPwdByEmail(sysUser);

                    if (R.FAIL == resetPwdResult.getCode())
                    {
                        throw new ServiceException(resetPwdResult.getMsg());
                    }
                    //recordLogininfor(username, Constants.REGISTER, "注册成功");
                }else {
                    throw new ServiceException("邮箱验证码错误");
                }
            }else {
                throw new ServiceException("请勿修改已输入的邮箱");
            }
        }else {
            ////判断是邮箱不存在还是操作超时
            ////通过邮箱获取用户
            //R<LoginUser> userByEmail = remoteUserService.getUserInfo(email, SecurityConstants.INNER);
            //
            //if(StringUtils.isNull(userByEmail.getData())){
            //    throw new ServiceException("邮箱"+email+"未被注册，请检查邮箱是否正确");
            //}
            throw new ServiceException("验证码校验失败：操作超时，请重新操作");
        }
    }


    /**
     * 记录登录信息
     *
     * @param username 用户名
     * @param status 状态
     * @param message 消息内容
     * @return
     */
    public void recordLogininfor(String username, String status, String message)
    {
        SysLogininfor logininfor = new SysLogininfor();
        logininfor.setUserName(username);
        logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
        logininfor.setMsg(message);
        // 日志状态
        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
        {
            logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
        }
        else if (Constants.LOGIN_FAIL.equals(status))
        {
            logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
        }
        remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
    }


}
