package com.cmeeting.ad.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmeeting.ad.entity.RobotSecurityUser;
import com.cmeeting.ad.entity.SysUserSync;
import com.cmeeting.ad.service.ILdapService;
import com.cmeeting.ad.service.UserService;
import com.cmeeting.ad.util.JwtUtil;
import com.cmeeting.ad.vo.ApplicationUserVO;
import com.cmeeting.ad.vo.UserVo;
import com.cmeeting.constant.RecordTemplateConstant;
import com.cmeeting.exception.RobotBaseException;
import com.cmeeting.mapper.primary.AuthMapper;
import com.cmeeting.mapper.primary.SysUserSyncMapper;
import com.cmeeting.pojo.CoreModulePermissions;
import com.cmeeting.pojo.ModulePermission;
import com.cmeeting.pojo.UserAccessRecord;
import com.cmeeting.service.IUserAccessRecordService;
import com.cmeeting.service.ModulePermissionService;
import com.cmeeting.util.*;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Value("${aec.key}")
    public String aesKey;
    @Value("${jwt.expireTime}")
    private String expireTime;
    @Value("${permission.tenantId}")
    public String permissionTenantId;
    @Value("${permission.admin-white_users}")
    public String adminWhiteUsers;

    @Value("${cimc.secret}")
    private String secret;
    @Resource
    private ILdapService iLdapService;
    @Resource
    private UserAdminConfig userAdminConfig;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private SysUserSyncMapper sysUserSysMapper;
    @Resource
    private AuthMapper authMapper;
    @Resource
    private SysUserSyncMapper sysUserSyncMapper;
    @Resource
    private IUserAccessRecordService iUserAccessRecordService;
    @Resource
    private ModulePermissionService iModulePermissionService;

    @Override
    public R login(String agentId, String data, String ip) {
        String mess = AESUtils.decrypt(data, aesKey);
        ApplicationUserVO.Login login = JSONObject.parseObject(mess, ApplicationUserVO.Login.class);
        log.info("登录信息：{}", login);
        return loginByAD(login);
    }

    @Override
    public Object auth(String userId, String nick, String role) {
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("userId", userId);
        stringStringHashMap.put("tenantId", permissionTenantId);
        stringStringHashMap.put("role", role);
        stringStringHashMap.put("nick", nick);
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userId, permissionTenantId);
        usernamePasswordAuthenticationToken.setDetails(stringStringHashMap);
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        RobotSecurityUser userDetail = (RobotSecurityUser) authentication.getPrincipal();
        String token = jwtUtil.generateToken(userDetail);
        JSONObject ret = new JSONObject();
        ret.put("token", token);
        ret.put("name", nick);

        redisUtils.set(token, userDetail, Long.parseLong(expireTime));
        // 记录登录日志
        iUserAccessRecordService.save(UserAccessRecord.builder().userId(userId).createTime(new Date()).build());
        return ret;
    }


    @Override
    public Object tokenAuth(ApplicationUserVO.LoginDecrypt vo) {
        String aigcToken = vo.getData();
        String url = userAdminConfig.getSystemDomain() + "/cloud/info";
        JSONObject object = HttpClientUtils.httpGet(url, aigcToken);
        if ("0".equals(object.get("code"))) {
            return null;
        }
        JSONObject data = object.getJSONObject("data");
        RobotSecurityUser robotSecurityUser = JSON.parseObject(data.toJSONString(), RobotSecurityUser.class);
        String userId = String.format("%08d", robotSecurityUser.getUserId());

        List<ModulePermission> auths = iModulePermissionService.list(new LambdaQueryWrapper<ModulePermission>().eq(ModulePermission::getPurpose, 0));
        boolean userAccess = auths.stream().anyMatch(item -> item.getType().equals(1) && item.getRelId().equals(userId));
        SysUserSync sysUserSync = sysUserSyncMapper.selectOne(new LambdaQueryWrapper<SysUserSync>().eq(SysUserSync::getUserId, userId).select(SysUserSync::getName, SysUserSync::getDeptId));
        //未直接授权到人，判断是否有授权给当前登录人所在组织
        if (!userAccess) {
            Set<String> authDepts = auths.stream().filter(item -> item.getType().equals(0)).map(ModulePermission::getRelId).collect(Collectors.toSet());
            List<String> deptPath = new ArrayList<>();
            getDeptPath(deptPath, sysUserSync.getDeptId());
            if (deptPath.stream().noneMatch(authDepts::contains)) {
                throw new RobotBaseException("您暂无权限");
            }
        }

        return auth(userId, sysUserSync.getName(), RecordTemplateConstant.TEMPLATE_TYPE_CUSTOM);
    }

    @Override
    public Object tokenAuth(String data) {
        String userId = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(data)
                .getBody()
                .getSubject();

        List<ModulePermission> auths = iModulePermissionService.list(new LambdaQueryWrapper<ModulePermission>().eq(ModulePermission::getPurpose, 0));
        boolean userAccess = auths.stream().anyMatch(item -> item.getType().equals(1) && item.getRelId().equals(userId));
        SysUserSync sysUserSync = sysUserSyncMapper.selectOne(new LambdaQueryWrapper<SysUserSync>().eq(SysUserSync::getUserId, userId).select(SysUserSync::getName, SysUserSync::getDeptId).last("limit 1"));
        //未直接授权到人，判断是否有授权给当前登录人所在组织
        if (!userAccess) {
            Set<String> authDepts = auths.stream().filter(item -> item.getType().equals(0)).map(ModulePermission::getRelId).collect(Collectors.toSet());
            List<String> deptPath = new ArrayList<>();
            getDeptPath(deptPath, sysUserSync.getDeptId());
            if (deptPath.stream().noneMatch(authDepts::contains)) {
                throw new RobotBaseException("您暂无权限");
            }
        }

        return auth(userId, sysUserSync.getName(), RecordTemplateConstant.TEMPLATE_TYPE_CUSTOM);
    }

    @Override
    public Object emailAuth(UserVo.Auth vo) {
        String userId = vo.getId();
        String nick = vo.getNick();
        //时间戳
        long expireDateTimestamp = vo.getExpireDate();
        ZonedDateTime expireDate = Instant.ofEpochMilli(expireDateTimestamp).atZone(ZoneId.systemDefault());
        ZonedDateTime now = ZonedDateTime.now();
        //todo 暂时将链接设为长期有效，后续根据业务情况调整（原因是链接如果过期，普通用户没有可以跳转的登录界面，目前的管理端登录界面只能黄飞用）
//        if(expireDate.isBefore(now)){
//            throw new RobotBaseException("认证已过期！", 401);
//        }
        long expireTimeStamp = Duration.between(now, expireDate).getSeconds();
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("userId", userId);
        stringStringHashMap.put("tenantId", permissionTenantId);
        stringStringHashMap.put("role", RecordTemplateConstant.TEMPLATE_TYPE_CUSTOM);
        stringStringHashMap.put("nick", nick);
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userId, permissionTenantId);
        usernamePasswordAuthenticationToken.setDetails(stringStringHashMap);
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        RobotSecurityUser userDetail = (RobotSecurityUser) authentication.getPrincipal();
        String token = jwtUtil.generateToken(userDetail);
        redisUtils.set(token, userDetail, expireTimeStamp);

        JSONObject ret = new JSONObject();
        ret.put("token", token);
        ret.put("name", nick);
        // 记录登录日志
        iUserAccessRecordService.save(UserAccessRecord.builder().userId(userId).createTime(new Date()).build());
        return ret;
    }

    private R loginByAD(ApplicationUserVO.Login login) {
        // AD验证
        String username = login.getUsername().trim();
        boolean auth = iLdapService.authenticate(username, login.getPassword().trim());
        if (auth) {
            SysUserSync sysUserSync = sysUserSysMapper.selectOne(new LambdaQueryWrapper<SysUserSync>()
                    .eq(SysUserSync::getUserId, username).last("limit 1"));
            if(StringUtils.isEmpty(adminWhiteUsers) || !Arrays.asList(adminWhiteUsers.split(",")).contains(username)){
                throw new RobotBaseException("尊敬的用户 ["+sysUserSync.getName()+"]，您的账户"+username+"未被授权访问此系统，请联系系统管理员。");
            }
            return R.ok(auth(sysUserSync.getUserId(), sysUserSync.getName(), RecordTemplateConstant.TEMPLATE_TYPE_SYSTEM));
        } else {
            return R.error("账号/密码错误！");
        }
    }

    /**
     * 获取部门的路径
     *
     * @param deptPath
     * @param deptId
     */
    private void getDeptPath(List<String> deptPath, String deptId) {
        if (!deptPath.contains(deptId)) {
            deptPath.add(deptId);
        }
        String parentId = sysUserSyncMapper.getParDeptId(deptId);
        if (StrUtil.isBlank(parentId)) {
            return;
        }
            //部门id去重
        if (!deptPath.contains(parentId)) {
            deptPath.add(parentId);
            getDeptPath(deptPath, parentId);
        }
    }
}