package cn.iocoder.lyzsys.module.tjt.service.employee;

import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.collection.CollectionUtils;
import cn.iocoder.lyzsys.framework.common.util.json.JsonUtils;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.employee.vo.EmployeePageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.employee.vo.EmployeeRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.employee.vo.EmployeeSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.employee.vo.EmployeeSimpleRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRolePersonSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.employee.EmployeeDO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.office.OfficeDO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.specialtyrolesplit.SpecialtyRoleSplitDO;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.employeeyearcostbudget.EmployeeYearCostBudgetMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.employee.EmployeeMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.office.OfficeMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.projectroleperson.ProjectRolePersonMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.specialtyrolesplit.SpecialtyRoleSplitMapper;
import cn.iocoder.lyzsys.module.tjt.service.office.OfficeService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.EMPLOYEE_IN_USE;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.EMPLOYEE_NOT_EXISTS;

@Service
@Validated
public class EmployeeServiceImpl implements EmployeeService {

    @Resource
    private EmployeeMapper employeeMapper;
    @Resource
    private OfficeMapper officeMapper;
    @Resource
    private EmployeeYearCostBudgetMapper employeeYearCostBudgetMapper;
    @Resource
    private ProjectRolePersonMapper projectRolePersonMapper;
    @Resource
    private SpecialtyRoleSplitMapper specialtyRoleSplitMapper;
    @Resource
    private OfficeService officeService;

    @Override
    public Long createEmployee(EmployeeSaveReqVO createReqVO) {
        officeService.validateOfficeExists(createReqVO.getOfficeId());
        EmployeeDO employee = BeanUtils.toBean(createReqVO, EmployeeDO.class);
        if (employee.getEnabledFlag() == null) {
            employee.setEnabledFlag(Boolean.TRUE);
        }
        employeeMapper.insert(employee);
        return employee.getId();
    }

    @Override
    public void updateEmployee(EmployeeSaveReqVO updateReqVO) {
        validateEmployeeExists(updateReqVO.getId());
        officeService.validateOfficeExists(updateReqVO.getOfficeId());
        employeeMapper.updateById(BeanUtils.toBean(updateReqVO, EmployeeDO.class));
    }

    @Override
    public void deleteEmployee(Long id) {
        validateEmployeeExists(id);
        if (employeeYearCostBudgetMapper.selectCountByEmployeeId(id) > 0
                || projectRolePersonMapper.selectCountByEmployeeId(id) > 0
                || existsInSpecialtyRoleSplit(id)) {
            throw exception(EMPLOYEE_IN_USE);
        }
        employeeMapper.deleteById(id);
    }

    @Override
    public EmployeeRespVO getEmployee(Long id) {
        EmployeeDO employee = validateEmployeeExists(id);
        Map<Long, OfficeDO> officeMap = getOfficeMap(Collections.singleton(employee.getOfficeId()));
        return toRespVO(employee, officeMap);
    }

    @Override
    public EmployeeDO validateEmployeeExists(Long id) {
        EmployeeDO employee = employeeMapper.selectById(id);
        if (employee == null) {
            throw exception(EMPLOYEE_NOT_EXISTS);
        }
        return employee;
    }

    @Override
    public PageResult<EmployeeRespVO> getEmployeePage(EmployeePageReqVO pageReqVO) {
        PageResult<EmployeeDO> pageResult = employeeMapper.selectPage(pageReqVO);
        Map<Long, OfficeDO> officeMap = getOfficeMap(CollectionUtils.convertSet(pageResult.getList(), EmployeeDO::getOfficeId));
        return BeanUtils.toBean(pageResult, EmployeeRespVO.class, respVO ->
                respVO.setOfficeName(officeMap.containsKey(respVO.getOfficeId())
                        ? officeMap.get(respVO.getOfficeId()).getOfficeName() : null));
    }

    @Override
    public List<EmployeeSimpleRespVO> getEmployeeSimpleList(String keyword, Long officeId, String status, Boolean enabledFlag) {
        List<EmployeeDO> list = employeeMapper.selectSimpleList(keyword, officeId, status, enabledFlag == null ? Boolean.TRUE : enabledFlag);
        Map<Long, OfficeDO> officeMap = getOfficeMap(CollectionUtils.convertSet(list, EmployeeDO::getOfficeId));
        return BeanUtils.toBean(list, EmployeeSimpleRespVO.class, respVO ->
                respVO.setOfficeName(officeMap.containsKey(respVO.getOfficeId())
                        ? officeMap.get(respVO.getOfficeId()).getOfficeName() : null));
    }

    private boolean existsInSpecialtyRoleSplit(Long employeeId) {
        List<SpecialtyRoleSplitDO> candidateList = specialtyRoleSplitMapper.selectListByPersonsJsonKeyword("\"employeeId\":" + employeeId);
        for (SpecialtyRoleSplitDO item : candidateList) {
            List<SpecialtyRolePersonSaveReqVO> persons = JsonUtils.parseArray(item.getPersonsJson(), SpecialtyRolePersonSaveReqVO.class);
            if (persons == null) {
                continue;
            }
            boolean matched = persons.stream()
                    .filter(Objects::nonNull)
                    .anyMatch(person -> Objects.equals(person.getEmployeeId(), employeeId));
            if (matched) {
                return true;
            }
        }
        return false;
    }

    private Map<Long, OfficeDO> getOfficeMap(Set<Long> officeIds) {
        if (officeIds == null || officeIds.isEmpty()) {
            return Collections.emptyMap();
        }
        officeIds.removeIf(Objects::isNull);
        if (officeIds.isEmpty()) {
            return Collections.emptyMap();
        }
        return CollectionUtils.convertMap(officeMapper.selectBatchIds(officeIds), OfficeDO::getId);
    }

    private EmployeeRespVO toRespVO(EmployeeDO employee, Map<Long, OfficeDO> officeMap) {
        return BeanUtils.toBean(employee, EmployeeRespVO.class, respVO ->
                respVO.setOfficeName(officeMap.containsKey(respVO.getOfficeId())
                        ? officeMap.get(respVO.getOfficeId()).getOfficeName() : null));
    }

}
