package com.ho.user.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.ho.common.tools.annotation.HzPermission;
import com.ho.common.tools.annotation.LogAnnotation;
import com.ho.common.tools.annotation.TokenIgnore;
import com.ho.common.tools.constant.CommonConstant;
import com.ho.common.tools.constant.ContextConstant;
import com.ho.common.tools.constant.PermissionConstant;
import com.ho.common.tools.constant.RedisKeyConstant;
import com.ho.common.tools.entity.SimpleUser;
import com.ho.common.tools.entity.SysRole;
import com.ho.common.tools.entity.UserDetailRespVO;
import com.ho.common.tools.exception.BaseResponseCode;
import com.ho.common.tools.exception.BusinessException;
import com.ho.common.tools.exception.DataResult;
import com.ho.common.tools.service.RedisService;
import com.ho.common.tools.util.AESEncryptUtil;
import com.ho.common.tools.util.FastUtils;
import com.ho.user.api.entity.SysDept;
import com.ho.user.api.entity.SysUser;
import com.ho.user.api.entity.WorkOrderUser;
import com.ho.user.entity.SysTheme;
import com.ho.user.service.*;
import com.ho.user.vo.req.*;
import com.ho.user.vo.resp.SysUserThemeRespVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.IOException;
import java.util.*;

/**
 * @author fancl
 * @desc: 用户入口
 * @date 2022/8/10
 */
@RestController
@Api(tags = "系统模块-用户管理")
@RequestMapping(ContextConstant.SYS + "/user")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    RoleService roleService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private DeptService deptService;

    @Autowired
    EncryptComponent encryptComponent;

    @Value("${spring.profiles.active}")
    String env;


    @GetMapping("{id}")
    @ApiOperation(value = "查询用户详情接口")
    //@LogAnnotation(title = "用户管理",action = "查询用户详情")
    //@RequiresPermissions("sys:user:detail")
    public DataResult<SysUser> detailInfo(@PathVariable("id") String id) {
        DataResult<SysUser> result = DataResult.success();
        result.setData(userService.detailInfo(id));
        return result;
    }

    /**
     * 查询用户详情接口(新)
     *
     * @param request
     * @return
     */
    @GetMapping("get")
    @ApiOperation(value = "查询用户详情接口(新)")
    //@LogAnnotation(title = "用户管理",action = "查询用户详情")
    public DataResult<UserDetailRespVO> yourSelfInfo(@RequestParam(required = false) String type,HttpServletRequest request) {
        String token = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        String userTokenKey = RedisKeyConstant.User.TOKEN + token;
        String lang =request.getHeader(CommonConstant.LANG);
        //根据用户的登陆方式(账号还是手机号),延长对应缓存
        if (redisService.hasKey(userTokenKey)) {
            //每次获取用户信息,都会进行过期时间的延长
            UserDetailRespVO user = (UserDetailRespVO) redisService.get(userTokenKey);
            SimpleUser simpleUser = (SimpleUser) redisService.getSimpleUserByToken(token);
            //获取到缓存的token中的roleIds，根据roleIds进行菜单查找
            if(null != type){
                user.setConfigType(type);
            }
            UserDetailRespVO vo = userService.yourSelfInfo(user, simpleUser.getPlatSuper(),lang);
            //延长token时间
            //redisCommon.extendKeyTime(userTokenKey, null);
            //String userNameKey = RedisKeyConstant.User.USER_NAME + user.getUsername();
            //登陆方式,账号或手机号,都使用同一个key,此处没必要区分是手机号还是账号登陆的
            //已经有Aspect在延长token时长了,其他地方不用再延长了
            //redisCommon.extendKeyTime(userNameKey, null);
            return DataResult.success(vo);
        }
        //没有就让用户重新登陆
        else {
            log.info("User token has expired ! token :{}", token);
            throw new BusinessException(BaseResponseCode.TOKEN_PARSE_ERROR);
        }
    }

    /**
     * 返回的参数类型和youSelfInfo信息一样,这样前端就可以通过login获取到全部信息,不用再调用用户详情接口了
     * 2022-11-10修改 同一个账号在pc端还是移动端登陆需要分别判断,不能走同一个token
     * 2022-11-23 同一用户不允许重复登陆
     *
     * @param loginReqVO
     * @return
     */
    @PostMapping(value = "login")
    @ApiOperation(value = "用户登录接口")
    @TokenIgnore
    public DataResult<UserDetailRespVO> login(@RequestBody @Valid LoginReqVO loginReqVO) {
        //登陆缓存有两个键值对,一个是Key是 userName:SimpleUser ,另一个是Key是token,value是UserDetailRespVO
        //这个userName可能为账号或者手机号
        //先判断用户是否登陆成功
        //判断是账号还是手机号
        //手机号登陆的
        if (FastUtils.checkPhoneNumber(loginReqVO.getUsername())) {
            loginReqVO.setLoginType(RedisKeyConstant.User.LOGIN_TYPE_PHONE);
        } else {
            loginReqVO.setLoginType(RedisKeyConstant.User.LOGIN_TYPE_ACCOUNT);
        }

        //验证登陆
        SysUser user = null;
        //手机号登陆的
        if (RedisKeyConstant.User.LOGIN_TYPE_PHONE.equals(loginReqVO.getLoginType())) {
            user = userService.getUserByPhone(loginReqVO.getUsername());
        }
        //账号登陆的
        else if (RedisKeyConstant.User.LOGIN_TYPE_ACCOUNT.equals(loginReqVO.getLoginType())) {
            user = userService.getUserByName(loginReqVO.getUsername());
        }
        if (user == null) {
            throw new BusinessException(BaseResponseCode.NOT_ACCOUNT);
        }

        if (CommonConstant.TWO.equals(user.getPeriodOfValidity())){
            Date date = DateUtils.addHours(user.getUpdateTime(), 6);
            Date now = new Date();
            if(DateUtil.compare(now,date)>=0){
                throw new BusinessException(BaseResponseCode.LOSE_EFFICACY);
            }
        }
        //状态0 (锁定状态)
        if (CommonConstant.USER.STATUS_LOCKING.equals(user.getStatus())) {
            throw new BusinessException(BaseResponseCode.USER_LOCK);
        }
        //校验部门状态是否为禁用
        if(!CommonConstant.ZERO.equals(user.getDeptId())){
            SysDept dept = deptService.detailInfo(user.getDeptId());
            if(!CommonConstant.STATUS_FLAG.equals(dept.getStatus())){
                throw new BusinessException(BaseResponseCode.TO_BELONGING_DEPT_DISABLE);
            }
        }
        //执行解密,使用私钥对aesKey和iv分别进行解密,然后AES解密
        //本地环境不加密
//        if (!CommonConstant.Env.DEV.equals(env)) {
            loginReqVO = encryptComponent.decryptSecret(loginReqVO);
//        }
        //即使走了缓存也要再校验一次密码
        //要使用明文密码
        if (!BCrypt.checkpw(loginReqVO.getPassword(), user.getPassword())) {
            throw new BusinessException(BaseResponseCode.PASSWORD_ERROR);
        }
        //清除用户登陆缓存
        //userService.removeUserCache(user.getUsername(), loginReqVO.getType());
        //若是特殊用户，则不更删除缓存，允许多点登录
        Map<String, String> map = userService.checkSpecialUser();
        if(null == map.get(user.getUsername())){
            userService.removeUserCache(user.getUsername(), loginReqVO.getType());
        }


        log.info("Not have user's RedisCache info ,next to find from DB");
        //解密密码并校验
        return DataResult.success(userService.login(loginReqVO, user));
    }

    @PostMapping(value = "getToken")
    @ApiOperation(value = "用户登录接口")
    @TokenIgnore
    public DataResult<UserDetailRespVO> getToken(@RequestBody @Valid LoginReqVO loginReqVO) {
        if (FastUtils.checkPhoneNumber(loginReqVO.getUsername())) {
            loginReqVO.setLoginType(RedisKeyConstant.User.LOGIN_TYPE_PHONE);
        } else {
            loginReqVO.setLoginType(RedisKeyConstant.User.LOGIN_TYPE_ACCOUNT);
        }

        //验证登陆
        SysUser user = null;
        //手机号登陆的
        if (RedisKeyConstant.User.LOGIN_TYPE_PHONE.equals(loginReqVO.getLoginType())) {
            user = userService.getUserByPhone(loginReqVO.getUsername());
        }
        //账号登陆的
        else if (RedisKeyConstant.User.LOGIN_TYPE_ACCOUNT.equals(loginReqVO.getLoginType())) {
            user = userService.getUserByName(loginReqVO.getUsername());
        }
        if (user == null) {
            throw new BusinessException(BaseResponseCode.NOT_ACCOUNT);
        }

        if (CommonConstant.TWO.equals(user.getPeriodOfValidity())){
            Date date = DateUtils.addHours(user.getUpdateTime(), 6);
            Date now = new Date();
            if(DateUtil.compare(now,date)>=0){
                throw new BusinessException(BaseResponseCode.LOSE_EFFICACY);
            }
        }
        //状态0 (锁定状态)
        if (CommonConstant.USER.STATUS_LOCKING.equals(user.getStatus())) {
            throw new BusinessException(BaseResponseCode.USER_LOCK);
        }
        //校验部门状态是否为禁用
        if(!CommonConstant.ZERO.equals(user.getDeptId())){
            SysDept dept = deptService.detailInfo(user.getDeptId());
            if(!CommonConstant.STATUS_FLAG.equals(dept.getStatus())){
                throw new BusinessException(BaseResponseCode.TO_BELONGING_DEPT_DISABLE);
            }
        }
        //即使走了缓存也要再校验一次密码
        //要使用明文密码
        if (!BCrypt.checkpw(loginReqVO.getPassword(), user.getPassword())) {
            throw new BusinessException(BaseResponseCode.PASSWORD_ERROR);
        }
        //清除用户登陆缓存
        //userService.removeUserCache(user.getUsername(), loginReqVO.getType());
        //若是特殊用户，则不更删除缓存，允许多点登录
        Map<String, String> map = userService.checkSpecialUser();
        if(null == map.get(user.getUsername())){
            userService.removeUserCache(user.getUsername(), loginReqVO.getType());
        }


        log.info("Not have user's RedisCache info ,next to find from DB");
        //解密密码并校验
        return DataResult.success(userService.login(loginReqVO, user));
    }
    /**
     * 原登陆接口, 已过期,使用登陆成功就将原有token失效
     * @param loginReqVO
     * @return
     */
    //@PostMapping(value = "login")
    //@ApiOperation(value = "用户登录接口")
    //@TokenIgnore
    public DataResult<UserDetailRespVO> login2(@RequestBody @Valid LoginReqVO loginReqVO) {
        //登陆缓存有两个键值对,一个是Key是 userName:SimpleUser ,另一个是Key是token,value是UserDetailRespVO
        //这个userName可能为账号或者手机号

        String userNameKey = RedisKeyConstant.User.USER_NAME + loginReqVO.getType() + ":" + loginReqVO.getUsername();
        //判断是账号还是手机号
        //手机号登陆的
        if (FastUtils.checkPhoneNumber(loginReqVO.getUsername())) {
            loginReqVO.setLoginType(RedisKeyConstant.User.LOGIN_TYPE_PHONE);
        } else {
            loginReqVO.setLoginType(RedisKeyConstant.User.LOGIN_TYPE_ACCOUNT);
        }
        //执行解密,使用私钥对aesKey和iv分别进行解密,然后AES解密
        //本地环境不加密
        if (!CommonConstant.Env.DEV.equals(env)) {
            loginReqVO = encryptComponent.decryptSecret(loginReqVO);
        }
        //为避免重复登陆,看缓存中是否有用户信息
        if (redisService.hasKey(userNameKey)) {
            SimpleUser simpleUser = (SimpleUser) redisService.get(userNameKey);
            if (simpleUser != null) {
                String token = simpleUser.getToken();
                String userTokenKey = RedisKeyConstant.User.TOKEN + token;
                UserDetailRespVO userDetail = (UserDetailRespVO) redisService.get(userTokenKey);
                //需要判断使用是同一个渠道登陆的
                if (loginReqVO.getType().equals(userDetail.getLoginChannel())) {
                    //相同,走缓存,不相同说明这个渠道没登陆过,重新安排一次登陆
                } else {
                    //没有登陆的情况
                    DataResult.success(userService.login(loginReqVO, null));
                }
                SysUser user = null;
                //手机号登陆的
                if (RedisKeyConstant.User.LOGIN_TYPE_PHONE.equals(userDetail.getLoginType())) {
                    user = userService.getUserByPhone(loginReqVO.getUsername());
                }
                //账号登陆的
                else if (RedisKeyConstant.User.LOGIN_TYPE_ACCOUNT.equals(userDetail.getLoginType())) {
                    user = userService.getUserByName(loginReqVO.getUsername());
                }
                if (user == null) {
                    throw new BusinessException(BaseResponseCode.NOT_ACCOUNT);
                }
                //状态2 (锁定状态) 暂时没用到
                if (user.getStatus() == 2) {
                    throw new BusinessException(BaseResponseCode.USER_LOCK);
                }
                //即使走了缓存也要再校验一次密码
                //要使用明文密码
                if (!BCrypt.checkpw(loginReqVO.getPassword(), user.getPassword())) {
                    throw new BusinessException(BaseResponseCode.PASSWORD_ERROR);
                }
                //解密密码并校验
                return DataResult.success(userDetail);
            }
        }
        log.info("Not have user's RedisCache info ,next to find from DB");
        //没缓存就走DB
        //解密密码并校验

        return DataResult.success(userService.login(loginReqVO ,null));
    }

    //解密演示,不对外提供接口
    @ApiIgnore
    @PostMapping(value = "login1")
    @TokenIgnore
    public DataResult<UserDetailRespVO> login1(@RequestBody @Valid LoginReqVO loginReqVO) {
        //使用私钥对aesKey和iv分别进行解密,然后AES解密
        loginReqVO = encryptComponent.decryptSecret(loginReqVO);
        return DataResult.success();
    }


    @PostMapping("register")
    @ApiOperation(value = "用户注册接口")
    public DataResult<String> register(@RequestBody @Valid RegisterReqVO vo) {
        DataResult<String> result = DataResult.success();
        result.setData(userService.register(vo));
        return result;
    }

    @PostMapping("add")
    @ApiOperation(value = "新增用户接口")
    @RequiresPermissions("sys:user:add")
    @LogAnnotation(title = "用户管理", action = "新增用户")
    public DataResult addUser(@RequestBody @Valid UserAddReqVO vo) {
        userService.addUser(vo);
        return DataResult.success();
    }

    @PutMapping("info")
    @ApiOperation(value = "更新用户信息接口")
    @LogAnnotation(title = "用户管理", action = "更新用户信息")
    @RequiresPermissions("sys:user:update")
    public DataResult updateUserInfoById(@RequestBody @Valid UserUpdateReqVO vo, HttpServletRequest request) {
        String token = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        String userTokenKey = RedisKeyConstant.User.TOKEN + token;
        UserDetailRespVO user = (UserDetailRespVO) redisService.get(userTokenKey);
        String operationId = user.getUserId();
        //判断手机号是否被其他人占用
        SysUser userByPhone = userService.getUserByPhone(vo.getPhone());
        if (userByPhone != null) {
            //这个手机号如果被其他人占用要提示
            if (!userByPhone.getId().equals(vo.getId())) {
                throw new BusinessException(BaseResponseCode.PHONE_IS_USED_BY_OTHER_PERSON);
            }
        }
        //这个token是操作者的token
        userService.updateUserInfo(vo, operationId, token);
        return DataResult.success();
    }


    @GetMapping("logout")
    @ApiOperation(value = "退出接口")
    //@LogAnnotation(title = "用户管理",action = "退出")
    @TokenIgnore
    public DataResult logout(HttpServletRequest request) {
        String accessToken = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        //  String refreshToken = request.getHeader(Constant.REFRESH_TOKEN);
        //暂时不传递 refresh_token
        userService.logout(accessToken);
        return DataResult.success();
    }

    @PutMapping("pwd")
    @ApiOperation(value = "修改密码接口")
    @LogAnnotation(title = "用户管理", action = "更新密码")
    public DataResult updatePwd(@RequestBody UpdatePasswordReqVO vo, HttpServletRequest request) {
        String accessToken = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        SimpleUser simpleUser = redisService.getSimpleUserByToken(accessToken);
        //解密旧密码
        String oldPwdPlain = AESEncryptUtil.decrypt(vo.getOldPwd(), simpleUser.getAesKey(), simpleUser.getIv());
        String newPwdPlain = AESEncryptUtil.decrypt(vo.getNewPwd(), simpleUser.getAesKey(), simpleUser.getIv());
        vo.setOldPwd(oldPwdPlain);
        vo.setNewPwd(newPwdPlain);
        if (simpleUser != null) {
            userService.updatePwd(vo, simpleUser.getUserId(), accessToken);
        }
        return DataResult.success();
    }

    @PutMapping("resetpwd")
    @ApiOperation(value = "重置密码接口")
    @LogAnnotation(title = "用户管理", action = "重置密码")
    @HzPermission(PermissionConstant.SYS_USER_RESETPWD)
    public DataResult resetPwd(@RequestBody ResetPasswordReqVO vo) {
        userService.resetPwd(vo);
        return DataResult.success();
    }


    @DeleteMapping("delete")
    @ApiOperation(value = "删除用户接口")
    @LogAnnotation(title = "用户管理", action = "删除用户")
    @RequiresPermissions("sys:user:deleted")
    public DataResult deletedUser(@RequestBody @ApiParam(value = "用户id集合") List<String> userIds, HttpServletRequest request) {
        String token = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        String userTokenKey = RedisKeyConstant.User.TOKEN + token;
        UserDetailRespVO user = (UserDetailRespVO) redisService.get(userTokenKey);
        String operationId = user.getUserId();
        userService.deletedUsers(userIds, operationId);
        return DataResult.success();
    }

    //原来的查询工单维修人员方法,已改为使用下面的新方法
    /*@PostMapping("workOrder/findWorkers")
    @ApiOperation(value = "工单维修人员列表")
    public DataResult<List<WorkOrderUser>> findWorkers(HttpServletRequest request) {
        String token = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        SimpleUser simpleUser = redisService.getSimpleUserByToken(token);
        //查询工单维修角色下的人员列表
        //先用角色code查角色
        SysRole roleInfo = roleService.findRoleInfoByRoleCode(CommonConstant.ROLE_WORK_ORDER_DO);
        if (roleInfo == null) {
            throw new BusinessException(BaseResponseCode.ROLE_INFO_NOT_FOUND);
        }
        List<Integer> roleIds = new ArrayList<>();
        roleIds.add(roleInfo.getId());
        List<String> userIdsByRoleIds = userRoleService.getUserIdsByRoleIds(roleIds);
        UserPageUserByDeptReqVO userQuery = new UserPageUserByDeptReqVO();
        userQuery.setUserIds(userIdsByRoleIds);
        //人员所属部门
        List<Integer> depts = simpleUser.getDepts();
        //根据部门和对应的角色用户匹配结果
        List<SysUser> userListByDeptIdsAndUserIds = userService.getUserListByDeptIdsAndUserIds(depts, userQuery);
        List<WorkOrderUser> userList = new ArrayList<>();
        if (!userListByDeptIdsAndUserIds.isEmpty()) {
            for (SysUser sysUser : userListByDeptIdsAndUserIds) {
                WorkOrderUser user = new WorkOrderUser();
                user.setUserId(sysUser.getId());
                user.setUsername(sysUser.getUsername());
                user.setRealName(sysUser.getRealName());
                user.setPhone(sysUser.getPhone());
                userList.add(user);
            }
        }
        return DataResult.success(userList);
    }*/

    @PostMapping("workOrder/findWorkers")
    @ApiOperation(value = "工单维修人员列表")
    public DataResult<List<WorkOrderUser>> findWorkers(HttpServletRequest request) {
        String token = request.getHeader(RedisKeyConstant.User.ACCESS_TOKEN);
        SimpleUser simpleUser = redisService.getSimpleUserByToken(token);
        //查询工单维修角色下的人员列表
        //先用角色code查角色
        SysRole roleInfo = roleService.findRoleInfoByRoleCode(CommonConstant.ROLE_WORK_ORDER_DO);
        if (roleInfo == null) {
            throw new BusinessException(BaseResponseCode.ROLE_INFO_NOT_FOUND);
        }
        List<Integer> roleIds = new ArrayList<>();
        roleIds.add(roleInfo.getId());
        List<String> userIdsByRoleIds = userRoleService.getUserIdsByRoleIds(roleIds);
        UserPageUserByDeptReqVO userQuery = new UserPageUserByDeptReqVO();
        userQuery.setUserIds(userIdsByRoleIds);
        //人员所属部门
        List<Integer> depts = simpleUser.getDepts();
        //根据部门和对应的角色用户匹配结果
        List<SysUser> userListByDeptIdsAndUserIds = userService.getUserListByDeptIdsAndUserIds(depts, userQuery,null);
        List<WorkOrderUser> userList = new ArrayList<>();
        if (!userListByDeptIdsAndUserIds.isEmpty()) {
            for (SysUser sysUser : userListByDeptIdsAndUserIds) {
                WorkOrderUser user = new WorkOrderUser();
                user.setUserId(sysUser.getId());
                user.setUsername(sysUser.getUsername());
                user.setRealName(sysUser.getRealName());
                user.setPhone(sysUser.getPhone());
                userList.add(user);
            }
        }
        return DataResult.success(userList);
    }

    @PostMapping("getMac")
    @ApiOperation(value = "获取mac地址")
    @TokenIgnore
    public DataResult getMac() {
        log.info("begin to getMac");
        // -1 失败  0 成功
        try {
            Process proc = Runtime.getRuntime().exec("/bin/bash  /data/so/test.sh");
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("end to getMac");
        return DataResult.success();
    }

    @GetMapping("getUserTheme/{userId}")
    @ApiOperation(value = "获取用户主题信息")
    public DataResult getUserTheme(@PathVariable String userId) {
        SysUserThemeRespVo theme =  userService.getUserTheme(userId);
        return DataResult.success(theme);
    }

    @GetMapping("getSysTheme")
    @ApiOperation(value = "获取用户主题信息")
    public DataResult getSysTheme() {
        List<SysTheme> theme =  userService.getSysTheme();
        return DataResult.success(theme);
    }

    @PostMapping("addUserTheme")
    @ApiOperation(value = "新增用户主题信息")
    public DataResult addUserTheme(@RequestBody SysUserThemeReqVo sysUserThemeReqVo) {
        if(sysUserThemeReqVo.getUserId() == null || sysUserThemeReqVo.getUserId() ==""){
            throw new BusinessException(BaseResponseCode.PARAM_CHECK_FAIL);
        }
        userService.addUserTheme(sysUserThemeReqVo);
        return DataResult.success();
    }

}
