package cn.iocoder.yudao.module.smartfactory.service.handlinggroupuseramount;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.bpm.api.oa.BpmOALoanApi;
import cn.iocoder.yudao.module.bpm.api.oa.vo.loan.BpmOALoanSumDTO;
import cn.iocoder.yudao.module.smartfactory.controller.admin.handlinggroupuseramount.dto.StaffWagesPageDTO;
import cn.iocoder.yudao.module.smartfactory.controller.admin.handlinggroupuseramount.vo.*;
import cn.iocoder.yudao.module.smartfactory.controller.admin.staffsalary.vo.DeductionItemsVO;
import cn.iocoder.yudao.module.smartfactory.controller.admin.staffsalary.vo.SalarySubjectItemsVO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.handlinggroupamountspecifications.HandlingGroupAmountSpecificationsDO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.handlinggroupuseramount.HandlingGroupUserAmountDO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.staff.StaffDO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.staffsalary.StaffSalaryDO;
import cn.iocoder.yudao.module.smartfactory.dal.mysql.handlinggroupamountspecifications.HandlingGroupAmountSpecificationsMapper;
import cn.iocoder.yudao.module.smartfactory.dal.mysql.handlinggroupuseramount.HandlingGroupUserAmountMapper;
import cn.iocoder.yudao.module.smartfactory.dal.mysql.staffsalary.StaffSalaryMapper;
import cn.iocoder.yudao.module.smartfactory.service.staff.StaffService;
import cn.iocoder.yudao.module.system.api.loan.LoanApi;
import cn.iocoder.yudao.module.system.api.loan.dto.LoanDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.smartfactory.enums.ErrorCodeConstants.SPECIFICATION_USER_AMOUNT_NOT_EXISTS;

/**
 * 搬运组每日个人工资记录 Service 实现类
 *
 * @author 艾楷
 */
@Service
@Validated
public class HandlingGroupUserAmountServiceImpl extends ServiceImpl<HandlingGroupUserAmountMapper,HandlingGroupUserAmountDO> implements HandlingGroupUserAmountService {

    @Resource
    private HandlingGroupUserAmountMapper handlingGroupUserAmountMapper;
    @Resource
    private HandlingGroupAmountSpecificationsMapper handlingGroupAmountSpecificationsMapper;

    @Resource
    private BpmOALoanApi bpmOALoanApi;

    @Resource
    private LoanApi loanApi;

    @Resource
    private StaffService staffService;

    @Resource
    private StaffSalaryMapper staffSalaryMapper;

    private static final String NAME = "姓名";
    private static final String TOTAL_AMOUNT = "总金额";
    // 提取常量
    private static final String USER_ID = "userId";
    private static final String NICK_NAME = "nickName";
    private static final String TOTAL_AMOUNT_EN = "totalAmount";

    @Override
    public Long createHandlingGroupUserAmount(HandlingGroupUserAmountSaveReqVO createReqVO) {
        // 插入
        HandlingGroupUserAmountDO handlingGroupUserAmount = BeanUtils.toBean(createReqVO, HandlingGroupUserAmountDO.class);
        handlingGroupUserAmountMapper.insert(handlingGroupUserAmount);
        // 返回
        return handlingGroupUserAmount.getId();
    }

    @Override
    public void updateHandlingGroupUserAmount(HandlingGroupUserAmountSaveReqVO updateReqVO) {
        // 更新
        HandlingGroupUserAmountDO updateObj = BeanUtils.toBean(updateReqVO, HandlingGroupUserAmountDO.class);
        handlingGroupUserAmountMapper.updateById(updateObj);
    }

    @Override
    public void deleteHandlingGroupUserAmount(Long id) {
        // 删除
        handlingGroupUserAmountMapper.deleteById(id);
    }

    @Override
    public HandlingGroupUserAmountDO getHandlingGroupUserAmount(Long id) {
        return handlingGroupUserAmountMapper.selectById(id);
    }

    @Override
    public PageResult<HandlingGroupUserAmountDO> getHandlingGroupUserAmountPage(HandlingGroupUserAmountPageReqVO pageReqVO) {
        IPage<HandlingGroupUserAmountDO> page = handlingGroupUserAmountMapper.getHandlingGroupUserAmountPage(MyBatisUtils.buildPage(pageReqVO), pageReqVO);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createOrEdit(HandlingGroupUserAmountSaveReqVO createReqVO) {
        // 插入
        HandlingGroupUserAmountDO handlingGroupUserAmount = BeanUtils.toBean(createReqVO, HandlingGroupUserAmountDO.class);
        handlingGroupUserAmountMapper.insertOrUpdate(handlingGroupUserAmount);
    }

    @Override
    public PageResult<StaffWagesPageVO> staffWagesPage(StaffWagesPageDTO dto) {
        IPage<StaffWagesPageVO> page = handlingGroupUserAmountMapper.staffWagesPage(MyBatisUtils.buildPage(dto), dto);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public HandlingGroupUserAmountMonthVO getListByMonth(Long id, Long factoryId, Integer type, String time, String name) {
        HandlingGroupUserAmountMonthVO vo = new HandlingGroupUserAmountMonthVO();
        Map<Integer, String> headers = new HashMap<>();
        // -- 根据yyyy-MM获取到该月第一天时间 - 再获取到最后一天 - 完了后组装表头
        String[] times = time.split("-");
        List<String> dateList;
        if (type == 1) {
            LocalDateTime startTime = LocalDateTime.of(Integer.parseInt(times[0]), Integer.parseInt(times[1]), 1, 0, 0, 0);
            LocalDateTime endTime = startTime.plusMonths(1).minusDays(1);
            dateList = DateUtils.betweenDayList(startTime, endTime);
        } else {
            LocalDateTime startTime = LocalDateTime.of(Integer.parseInt(times[0]), Integer.parseInt(times[1]), 26, 0, 0, 0).minusMonths(1);
            LocalDateTime endTime = LocalDateTime.of(Integer.parseInt(times[0]), Integer.parseInt(times[1]), 25, 0, 0, 0);
            dateList = DateUtils.betweenDayList(startTime, endTime);
        }
        headers.put(1, NAME);
        int num = 1;
        for (String date : dateList) {
            num++;
            headers.put(num, date);
        }
        headers.put(num + 1, TOTAL_AMOUNT);
        // - 插入表头
        vo.setHeaders(headers);
        List<Map<String, Object>> map = handlingGroupUserAmountMapper.getListByMonth(id, factoryId, dateList, name);
        List<HandlingGroupUserAmountMonthItemVO> list = new ArrayList<>();
        for (Map<String, Object> item : map) {
            HandlingGroupUserAmountMonthItemVO itemVO = new HandlingGroupUserAmountMonthItemVO();
            itemVO.setUserId((Long) item.get(USER_ID));
            Map<Integer, String> itemMap = new HashMap<>();
            itemMap.put(1, item.get(NICK_NAME).toString());
            num = 1;
            for (String date : dateList) {
                num++;
                itemMap.put(num, item.get(date).toString());
            }
            itemMap.put(num + 1, item.get(TOTAL_AMOUNT_EN).toString());
            itemVO.setContentList(itemMap);
            list.add(itemVO);
        }
        vo.setList(list);
        return vo;
    }

    @Override
    public List<HandlingGroupUserAmountDO> getHandlingGroupUserAmountList(HandlingGroupUserAmountPageReqVO pageReqVO) {
        return handlingGroupUserAmountMapper.getHandlingGroupUserAmountList(pageReqVO);
    }

    @Override
    public void confirmHandlingGroupUserAmount(Long factoryId, String month) {

        // 查询搬运工 月工资总额
        List<HandlingGroupUserAmountDO> userAmountSums = handlingGroupUserAmountMapper.selectAmountSum(factoryId, month);
        if (CollUtil.isEmpty(userAmountSums)) {
            throw  exception(SPECIFICATION_USER_AMOUNT_NOT_EXISTS);
        }

        // 校验 工厂当前月份是否存在未分配搬运工数据
        Long count = handlingGroupUserAmountMapper.selectIsExists(factoryId, month);
        if (count > 0L) {
            throw  exception(SPECIFICATION_USER_AMOUNT_NOT_EXISTS);
        }

        // 更新搬运组薪资 状态
        handlingGroupUserAmountMapper.update(null, new LambdaUpdateWrapper<HandlingGroupUserAmountDO>()
                .set(HandlingGroupUserAmountDO::getStatus, 1)
                .eq(HandlingGroupUserAmountDO::getFactoryId, factoryId)
                .like(HandlingGroupUserAmountDO::getDateStr, month));

        // 获取厂区得员工信息
        Set<Long> staffIds = convertSet(userAmountSums, HandlingGroupUserAmountDO::getUserId);
        List<StaffDO> staffDOS = staffService.getList(staffIds);
        Map<Long, StaffDO> staffMap = convertMap(staffDOS, StaffDO::getId);

        // 获取员工借支记录
        List<BpmOALoanSumDTO> loanDOS = bpmOALoanApi.getListByStaffId(staffIds, month).getCheckedData();
        Map<Long, BigDecimal> loanMap = loanDOS.stream()
                .collect(Collectors.toMap(
                        BpmOALoanSumDTO::getSfUserId,
                        BpmOALoanSumDTO::getTotalAmount,
                        BigDecimal::add
                ));

        // 获取员工剩余的 工资借支金额
        List<LoanDTO> sysLoanDOS = loanApi.getListByMonth(staffIds, month).getCheckedData();
        Map<Long, BigDecimal> sysLoanMap = new HashMap<>();
        if (CollUtil.isNotEmpty(sysLoanDOS)) {
            sysLoanMap = sysLoanDOS.stream()
                    .collect(Collectors.toMap(LoanDTO::getUserId, LoanDTO::getRemainingAmount));
        }

        Map<Long, BigDecimal> finalSysLoanMap = sysLoanMap;
        List<StaffSalaryDO> salaryDOS = userAmountSums.stream().map(item -> {
            StaffSalaryDO salaryDO = new StaffSalaryDO()
                    .setStaffId(item.getUserId())
                    .setFactoryId(item.getFactoryId())
                    .setMonth(month)
                    .setPayableAmount(item.getAmount())
                    .setReturnAmount(loanMap.getOrDefault(item.getUserId(), BigDecimal.ZERO)
                            .add(finalSysLoanMap.getOrDefault(item.getUserId(), BigDecimal.ZERO)))
                    .setDeductionItems(Collections.singletonList(
                            new DeductionItemsVO()
                                    .setKey("insurance")
                                    .setValue("50")));
            // 设置实发工资
            salaryDO.setRealAmount(salaryDO.getPayableAmount().subtract(salaryDO.getReturnAmount()));
            // 设置薪资主体数据
            if (staffMap.get(item.getUserId()).getSalarySubject() != null) {
                salaryDO.setSalarySubjectItems(Collections.singletonList(
                        new SalarySubjectItemsVO()
                                .setSalarySubjectId(staffMap.get(item.getUserId()).getSalarySubject())
                                .setAmount(salaryDO.getPayableAmount())
                ));
            }
            return salaryDO;
        }).collect(Collectors.toList());

        // 插入工资记录
        staffSalaryMapper.insertBatch(salaryDOS);
    }

    @Override
    public List<HandlingGroupUserAmountDO> getByHandlingGroupIds(List<Long> handlingGroupIds) {
        return handlingGroupUserAmountMapper.getByHandlingGroupIds(handlingGroupIds);
    }

    @Override
    public List<HandlingGroupUserAmountDO> getByFactorIdAndDateStrAndHandlingGroupId(List<Long> factoryIds, List<String> dateStrList, List<Long> handlingGroupIds) {
        return handlingGroupUserAmountMapper.getByFactorIdAndDateStrAndHandlingGroupId(factoryIds, dateStrList, handlingGroupIds);
    }

}
