package com.njcn.rdms.module.system.service.dept;

import cn.hutool.core.collection.CollUtil;
import com.google.common.annotations.VisibleForTesting;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.system.controller.admin.dept.vo.orgleader.OrgLeaderRelationSaveReqVO;
import com.njcn.rdms.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.rdms.module.system.dal.dataobject.dept.OrgLeaderRelationDO;
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.rdms.module.system.dal.mysql.dept.OrgLeaderRelationMapper;
import com.njcn.rdms.module.system.service.user.AdminUserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static com.njcn.rdms.framework.common.util.collection.CollectionUtils.convertSet;

import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton;

/**
 * 组织负责人关系 Service 实现类
 */
@Service
@Validated
public class OrgLeaderRelationServiceImpl implements OrgLeaderRelationService {

    @Resource
    private OrgLeaderRelationMapper orgLeaderRelationMapper;
    @Resource
    private DeptService deptService;
    @Resource
    private AdminUserService adminUserService;

    @Override
    public Long createOrgLeaderRelation(OrgLeaderRelationSaveReqVO createReqVO) {
        validateOrgLeaderRelationForCreateOrUpdate(null, createReqVO);
        OrgLeaderRelationDO relation = BeanUtils.toBean(createReqVO, OrgLeaderRelationDO.class);
        orgLeaderRelationMapper.insert(relation);
        return relation.getId();
    }

    @Override
    public void updateOrgLeaderRelation(OrgLeaderRelationSaveReqVO updateReqVO) {
        if (updateReqVO.getId() == null) {
            throw exception(ORG_LEADER_RELATION_NOT_FOUND);
        }
        validateOrgLeaderRelationForCreateOrUpdate(updateReqVO.getId(), updateReqVO);
        OrgLeaderRelationDO relation = BeanUtils.toBean(updateReqVO, OrgLeaderRelationDO.class);
        orgLeaderRelationMapper.updateById(relation);
    }

    @Override
    public void deleteOrgLeaderRelation(Long id) {
        validateOrgLeaderRelationExists(id);
        orgLeaderRelationMapper.deleteById(id);
    }

    @Override
    public List<OrgLeaderRelationDO> getOrgLeaderRelationListByDeptId(Long deptId) {
        validateDeptExists(deptId);
        return orgLeaderRelationMapper.selectListByDeptId(deptId);
    }

    @Override
    public Set<Long> listEffectiveDeptIdsByUserId(Long userId) {
        List<OrgLeaderRelationDO> relations = orgLeaderRelationMapper.selectEffectiveListByUserId(userId, LocalDateTime.now());
        return convertSet(relations, OrgLeaderRelationDO::getDeptId);
    }

    @Override
    public List<AdminUserDO> getCandidateUsersByDeptId(Long deptId) {
        validateDeptExists(deptId);
        Set<Long> deptScope = new HashSet<>(deptService.getChildDeptIdListFromCache(deptId));
        deptScope.add(deptId);
        List<AdminUserDO> users = adminUserService.getUserListByDeptIds(deptScope);
        users.removeIf(user -> !adminUserService.isUserAvailable(user));
        users.sort(Comparator.comparing(AdminUserDO::getDeptId)
                .thenComparing(AdminUserDO::getNickname)
                .thenComparing(AdminUserDO::getId));
        return users;
    }

    private void validateOrgLeaderRelationForCreateOrUpdate(Long id, OrgLeaderRelationSaveReqVO reqVO) {
        validateOrgLeaderRelationExists(id);
        deptService.validateDeptList(singleton(reqVO.getDeptId()));
        adminUserService.validateUserList(singleton(reqVO.getUserId()));
        validateEffectiveRange(reqVO.getEffectiveFrom(), reqVO.getEffectiveUntil());
        validateUserInDeptScope(reqVO.getDeptId(), reqVO.getUserId());
        validateRelationOverlap(id, reqVO.getDeptId(), reqVO.getUserId(),
                reqVO.getEffectiveFrom(), reqVO.getEffectiveUntil());
    }

    @VisibleForTesting
    void validateEffectiveRange(LocalDateTime effectiveFrom, LocalDateTime effectiveUntil) {
        if (effectiveFrom != null && effectiveUntil != null && effectiveFrom.isAfter(effectiveUntil)) {
            throw exception(ORG_LEADER_EFFECTIVE_RANGE_INVALID);
        }
    }

    @VisibleForTesting
    void validateUserInDeptScope(Long deptId, Long userId) {
        AdminUserDO user = adminUserService.getUser(userId);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        Set<Long> deptScope = new HashSet<>(deptService.getChildDeptIdListFromCache(deptId));
        deptScope.add(deptId);
        if (!deptScope.contains(user.getDeptId())) {
            throw exception(ORG_LEADER_USER_NOT_IN_DEPT, user.getNickname());
        }
    }

    @VisibleForTesting
    void validateRelationOverlap(Long id, Long deptId, Long userId,
                                 LocalDateTime effectiveFrom, LocalDateTime effectiveUntil) {
        List<OrgLeaderRelationDO> relations = orgLeaderRelationMapper.selectListByDeptIdAndUserId(deptId, userId);
        if (CollUtil.isEmpty(relations)) {
            return;
        }
        for (OrgLeaderRelationDO relation : relations) {
            if (Objects.equals(relation.getId(), id)) {
                continue;
            }
            if (isTimeRangeOverlap(effectiveFrom, effectiveUntil,
                    relation.getEffectiveFrom(), relation.getEffectiveUntil())) {
                throw exception(ORG_LEADER_RELATION_OVERLAP);
            }
        }
    }

    private boolean isTimeRangeOverlap(LocalDateTime start1, LocalDateTime end1,
                                       LocalDateTime start2, LocalDateTime end2) {
        return !isAfter(start1, end2) && !isAfter(start2, end1);
    }

    private boolean isAfter(LocalDateTime start, LocalDateTime end) {
        return start != null && end != null && start.isAfter(end);
    }

    private OrgLeaderRelationDO validateOrgLeaderRelationExists(Long id) {
        if (id == null) {
            return null;
        }
        OrgLeaderRelationDO relation = orgLeaderRelationMapper.selectById(id);
        if (relation == null) {
            throw exception(ORG_LEADER_RELATION_NOT_FOUND);
        }
        return relation;
    }

    private DeptDO validateDeptExists(Long deptId) {
        DeptDO dept = deptService.getDept(deptId);
        if (dept == null) {
            throw exception(DEPT_NOT_FOUND);
        }
        return dept;
    }

}
