package com.lideeyunji.service.system.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.lideeyunji.service.system.config.convert.auth.AuthConvert;
import com.lideeyunji.service.system.constant.ErrorCodeConstants;
import com.lideeyunji.service.system.controller.vo.auth.*;
import com.lideeyunji.service.system.controller.vo.auth.*;
import com.lideeyunji.service.system.controller.vo.user.user.UserRespVO;
import com.lideeyunji.service.system.entity.AdminUserDO;
import com.lideeyunji.service.system.entity.MenuDO;
import com.lideeyunji.service.system.entity.RoleDO;
import com.lideeyunji.service.system.entity.UserRoleDO;
import com.lideeyunji.service.system.enums.LoginLogTypeEnum;
import com.lideeyunji.service.system.service.*;
import com.lideeyunji.tool.framework.common.enums.CommonStatusEnum;
import com.lideeyunji.tool.framework.common.enums.UserTypeEnum;
import com.lideeyunji.tool.framework.common.pojo.CommonResult;
import com.lideeyunji.tool.framework.common.util.collection.CollectionUtils;
import com.lideeyunji.tool.framework.common.util.string.StrUtils;
import com.lideeyunji.tool.framework.operatelog.core.annotations.OperateLog;
import com.lideeyunji.tool.framework.security.config.SecurityProperties;
import com.lideeyunji.tool.framework.security.core.util.SecurityFrameworkUtils;
import com.lideeyunji.tool.framework.tenant.core.context.TenantContextHolder;
import com.lideeyunji.service.system.service.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

import static com.lideeyunji.tool.framework.common.pojo.CommonResult.success;
import static com.lideeyunji.tool.framework.common.util.collection.CollectionUtils.convertSet;
import static com.lideeyunji.tool.framework.security.core.util.SecurityFrameworkUtils.*;

@Tag(name = "管理后台 - 认证")
@RestController
@RequestMapping("/system/auth")
@Validated
@Slf4j
public class AuthController {

    @Resource
    private IAdminAuthService authService;
    @Resource
    private IAdminUserService userService;
    @Resource
    private IRoleService roleService;
    @Resource
    private IMenuService menuService;
    @Resource
    private IPermissionService permissionService;
    @Resource
    private ISocialClientService socialClientService;

    @Resource
    private SecurityProperties securityProperties;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @PostMapping("/login")
    @PermitAll
    @Operation(tags = "授权管理",summary = "使用账号密码登录")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
        return success(authService.login(reqVO));
    }

    @PostMapping("/logout")
    @PermitAll
    @Operation(tags = "授权管理",summary = "登出系统")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<Boolean> logout(HttpServletRequest request) {
        logoutService(request);
        return success(true);
    }

    @PostMapping("/switch-login")
    @Operation(tags = "授权管理",summary = "切换部门登录")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> switchLogin(@RequestBody SwitchLoginReqVO reqVO, HttpServletRequest request) {
        //先退出登录 ，然后再重新登录
        Long loginUserId = getLoginUserId();
        Long loginDeptId = reqVO.getLoginDeptId();
        Long loginRoleId = reqVO.getLoginRoleId();
        if(loginDeptId==null){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_DEPT_NULL);
        }
        if(loginRoleId==null){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_ROLE_NULL);
        }
        //获取该部门下的角色
        AdminUserDO user = userService.getUser(loginUserId);
        String deptIdsStr = user.getDeptId();
        if (StrUtil.isEmpty(deptIdsStr)) {
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_BIND_DEPT_NULL);
        }
        Set<Long> deptId = StrUtils.str2setLong(user.getDeptId());

        if(CollectionUtil.isEmpty(deptId)){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_BIND_DEPT_NULL);
        }
        if(loginDeptId!=-1 && !deptId.contains(loginDeptId)){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_NOTIN_DEPT);
        }
        //判断该角色
        List<UserRoleDO> userRoleList = userService.getUserRoleList(user.getId(), loginDeptId);
        if(CollectionUtil.isEmpty(userRoleList)){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_NOTIN_ROLE);
        }
        Map<Long, UserRoleDO> userRoleMap = CollectionUtils.convertMap(userRoleList, UserRoleDO::getRoleId);
        if(loginRoleId!=-1 && !userRoleMap.containsKey(loginRoleId)){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_NOTIN_ROLE);
        }
        //校验通过
        //1.先退出旧的
        logoutService(request);
        //2.重新登录
        AuthLoginRespVO vo = authService.switchLogin(user.getId(), loginDeptId, loginRoleId);
        return success(vo);
    }

    @PostMapping("/switch-tenant")
    @Operation(tags = "授权管理",summary = "切换租户登录（只允许超级管理员操作）")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> switchTenant(@RequestBody SwitchTenantReqVO reqVO, HttpServletRequest request) {
        Long tenantId = reqVO.getTenantId();
        boolean supAdminToken = isSupAdmin();
        if(!supAdminToken){
            return CommonResult.error(ErrorCodeConstants.AUTH_SWITCH_LOGIN_USER_ISNOT_SUPADMIN);
        }
        //1.先退出旧的
        logoutService(request);
        //当前是超级管理员 -> 登录到当前租户下的管理
        TenantContextHolder.setTenantId(tenantId);
        AuthLoginRespVO vo = authService.supAdminSwitchTenantLogin(tenantId,true);

        return success(vo);
    }


    @PostMapping("/refresh-token")
    @PermitAll
    @Operation(tags = "授权管理",summary = "刷新令牌")
    @Parameter(name = "refreshToken", description = "刷新令牌", required = true)
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
        return success(authService.refreshToken(refreshToken));
    }

    @GetMapping("/get-permission-info")
    @Operation(tags = "授权管理",summary = "获取登录用户的权限信息")
    public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
        // 1.1 获得用户信息
        AdminUserDO user = userService.getUser(getLoginUserId());
        if (user == null) {
            AuthPermissionInfoRespVO vo = new AuthPermissionInfoRespVO();
            return CommonResult.success(vo);
        }
        boolean supAdminFlag = userService.isSupAdmin(user.getId());//是否是超级管理员
        boolean tenantAdminFlag = userService.isTenantAdmin(user.getId());//是否是租户管理员

        //获取当前登录部门和角色
        Long loginDeptId = getLoginDeptId();
        Long loginRoleId = getLoginRoleId();

        //获取所有按钮权限
        Set<String> allButtonPermissionSets = menuService.getAllButtonPermissionSets();

        // 1.2 获得角色列表
        Set<Long> roleIds =new HashSet<>();
        if(supAdminFlag || tenantAdminFlag){//超级管理员 或者租户管理员，直接查自身的所有权限
            roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
        }else{
            if(loginRoleId!=null){//当前登录有角色
                if(loginRoleId==-1){//该部门下的所有角色
                    List<UserRoleDO> userRoleList = userService.getUserRoleList(user.getId(), loginDeptId);
                    roleIds = userRoleList.stream()
                            .map(UserRoleDO::getRoleId)
                            .collect(Collectors.toSet());
                }else{
                    roleIds.add(loginRoleId);
                }
            }
        }

        if (CollUtil.isEmpty(roleIds)) {
            return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList(),allButtonPermissionSets));
        }
        List<RoleDO> roles = roleService.getRoleList(roleIds);
        roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色

        // 1.3 获得菜单列表
        Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));
        List<MenuDO> menuList = new ArrayList<>();
        if (!CollUtil.isEmpty(menuIds)) {
            menuList = menuService.getMenuList(menuIds);
            menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
        }
        Set<Long> deptId = StrUtils.str2setLong(user.getDeptId());

        //部门角色信息
        List<UserRespVO.DeptInfo> deptInfoList = userService.getDeptInfo(user.getId(), deptId);

        // 2. 拼接结果返回
        AuthPermissionInfoRespVO vo = AuthConvert.INSTANCE.convert(user, roles, menuList, allButtonPermissionSets);
        Map<String, List<String>> lideeYunJiButtonPermissionMap = menuService.getLideeYunJiButtonPermissionMap(vo.getPermissions());
        vo.setLideeYunJipermissions(lideeYunJiButtonPermissionMap);


        vo.setDeptInfoList(deptInfoList);
        AuthPermissionInfoRespVO.UserVO userVo = vo.getUser();
        userVo.setLoginDeptId(loginDeptId);
        userVo.setLoginRoleId(loginRoleId);
        userVo.setSupAdmin(supAdminFlag);//是否是超级管理员
        userVo.setTenantAdmin(tenantAdminFlag);//是否是租户管理员
        userVo.setSupAdminToken(isSupAdmin());
        return success(vo);
    }

    // ========== 短信登录相关 ==========

    @PostMapping("/sms-login")
    @PermitAll
    @Operation(tags = "授权管理",summary = "使用短信验证码登录")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) {
        return success(authService.smsLogin(reqVO));
    }

    @PostMapping("/send-sms-code")
    @PermitAll
    @Operation(tags = "授权管理",summary = "发送手机验证码")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<Boolean> sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) {

        authService.sendSmsCode(reqVO);
        return success(true);
    }

    // ========== 社交登录相关 ==========

    @GetMapping("/social-auth-redirect")
    @PermitAll
    @Operation(tags = "授权管理",summary = "社交授权的跳转")
    @Parameters({
            @Parameter(name = "type", description = "社交类型", required = true),
            @Parameter(name = "redirectUri", description = "回调路径")
    })
    public CommonResult<String> socialLogin(@RequestParam("type") Integer type,
                                            @RequestParam("redirectUri") String redirectUri) {
        return success(socialClientService.getAuthorizeUrl(
                type, UserTypeEnum.ADMIN.getValue(), redirectUri));
    }

    @PostMapping("/social-login")
    @PermitAll
    @Operation(tags = "授权管理",summary = "社交快捷登录，使用 code 授权码", description = "适合未登录的用户，但是社交账号已绑定用户")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
        return success(authService.socialLogin(reqVO));
    }


    //退出登录
    private void logoutService(HttpServletRequest request){
        Long loginUserId = getLoginUserId();

        String token = SecurityFrameworkUtils.obtainAuthorization(request,
                securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
        if (StrUtil.isNotBlank(token)) {
            authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
        }
        //清空个人信息
        String userRedisKey="JEE_LOW_CODE:USER:"+loginUserId+":*";
        Set<String> keys = stringRedisTemplate.keys(userRedisKey);
        if(keys!=null && keys.size()>0){
            keys.forEach(key->{
                stringRedisTemplate.delete(key);
            });
        }
    }

}
