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

import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.employeeyearcostbudget.vo.*;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.employee.EmployeeDO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.employeeyearcostbudget.EmployeeYearCostBudgetDO;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.employee.EmployeeMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.employeeyearcostbudget.EmployeeYearCostBudgetMapper;
import cn.iocoder.lyzsys.module.tjt.service.employee.EmployeeService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

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

@Service
@Validated
public class EmployeeYearCostBudgetServiceImpl implements EmployeeYearCostBudgetService {

    @Resource
    private EmployeeYearCostBudgetMapper employeeYearCostBudgetMapper;
    @Resource
    private EmployeeMapper employeeMapper;
    @Resource
    private EmployeeService employeeService;

    @Override
    public Long createEmployeeYearCostBudget(EmployeeYearCostBudgetSaveReqVO createReqVO) {
        EmployeeDO employee = employeeService.validateEmployeeExists(createReqVO.getEmployeeId());
        validateDuplicate(null, createReqVO.getEmployeeId(), createReqVO.getBudgetYear());
        EmployeeYearCostBudgetDO budget = BeanUtils.toBean(createReqVO, EmployeeYearCostBudgetDO.class);
        budget.setEmployeeName(employee.getEmployeeName());
        if (budget.getEnabledFlag() == null) {
            budget.setEnabledFlag(Boolean.TRUE);
        }
        employeeYearCostBudgetMapper.insert(budget);
        return budget.getId();
    }

    @Override
    public EmployeeYearCostBudgetGenerateRespVO generateEmployeeYearCostBudget(EmployeeYearCostBudgetGenerateReqVO reqVO) {
        BigDecimal expectedCostAmount = amount(reqVO.getExpectedCostAmount());
        List<EmployeeDO> enabledEmployees = employeeMapper.selectEnabledList();
        Set<Long> existingEmployeeIds = employeeYearCostBudgetMapper.selectListByBudgetYear(reqVO.getBudgetYear())
                .stream()
                .map(EmployeeYearCostBudgetDO::getEmployeeId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        List<EmployeeYearCostBudgetDO> createList = enabledEmployees.stream()
                .filter(employee -> !existingEmployeeIds.contains(employee.getId()))
                .map(employee -> buildInitialBudget(employee, reqVO.getBudgetYear(), expectedCostAmount))
                .collect(Collectors.toList());
        if (!createList.isEmpty()) {
            employeeYearCostBudgetMapper.insertBatch(createList);
        }

        EmployeeYearCostBudgetGenerateRespVO respVO = new EmployeeYearCostBudgetGenerateRespVO();
        respVO.setBudgetYear(reqVO.getBudgetYear());
        respVO.setTotalEnabledEmployeeCount(enabledEmployees.size());
        respVO.setCreatedCount(createList.size());
        respVO.setSkippedCount(enabledEmployees.size() - createList.size());
        return respVO;
    }

    @Override
    public void updateEmployeeYearCostBudget(EmployeeYearCostBudgetSaveReqVO updateReqVO) {
        validateExists(updateReqVO.getId());
        EmployeeDO employee = employeeService.validateEmployeeExists(updateReqVO.getEmployeeId());
        validateDuplicate(updateReqVO.getId(), updateReqVO.getEmployeeId(), updateReqVO.getBudgetYear());
        EmployeeYearCostBudgetDO updateObj = BeanUtils.toBean(updateReqVO, EmployeeYearCostBudgetDO.class);
        updateObj.setEmployeeName(employee.getEmployeeName());
        employeeYearCostBudgetMapper.updateById(updateObj);
    }

    @Override
    public void deleteEmployeeYearCostBudget(Long id) {
        validateExists(id);
        employeeYearCostBudgetMapper.deleteById(id);
    }

    @Override
    public EmployeeYearCostBudgetRespVO getEmployeeYearCostBudget(Long id) {
        return BeanUtils.toBean(validateExists(id), EmployeeYearCostBudgetRespVO.class);
    }

    @Override
    public PageResult<EmployeeYearCostBudgetRespVO> getEmployeeYearCostBudgetPage(EmployeeYearCostBudgetPageReqVO pageReqVO) {
        return BeanUtils.toBean(employeeYearCostBudgetMapper.selectPage(pageReqVO), EmployeeYearCostBudgetRespVO.class);
    }

    private EmployeeYearCostBudgetDO validateExists(Long id) {
        EmployeeYearCostBudgetDO budget = employeeYearCostBudgetMapper.selectById(id);
        if (budget == null) {
            throw exception(EMPLOYEE_YEAR_COST_BUDGET_NOT_EXISTS);
        }
        return budget;
    }

    private void validateDuplicate(Long id, Long employeeId, Integer budgetYear) {
        EmployeeYearCostBudgetDO duplicate = employeeYearCostBudgetMapper.selectByEmployeeIdAndBudgetYear(employeeId, budgetYear);
        if (duplicate != null && !Objects.equals(duplicate.getId(), id)) {
            throw exception(EMPLOYEE_YEAR_COST_BUDGET_DUPLICATE);
        }
    }

    private EmployeeYearCostBudgetDO buildInitialBudget(EmployeeDO employee, Integer budgetYear, BigDecimal expectedCostAmount) {
        EmployeeYearCostBudgetDO budget = new EmployeeYearCostBudgetDO();
        budget.setEmployeeId(employee.getId());
        budget.setEmployeeName(employee.getEmployeeName());
        budget.setBudgetYear(budgetYear);
        budget.setExpectedCostAmount(expectedCostAmount);
        budget.setSortNo(employee.getSortNo() == null ? 0 : employee.getSortNo());
        budget.setEnabledFlag(Boolean.TRUE);
        budget.setRemark("");
        return budget;
    }

    private BigDecimal amount(BigDecimal value) {
        return value == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
                : value.setScale(2, RoundingMode.HALF_UP);
    }

}
