package com.qmth.sop.business.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.sop.business.bean.dto.DataPermissionDto;
import com.qmth.sop.business.bean.dto.DingSubmitExportDto;
import com.qmth.sop.business.bean.params.DingSubmitParam;
import com.qmth.sop.business.bean.result.DingSubmitResult;
import com.qmth.sop.business.bean.result.DingSubmitSubTotalResult;
import com.qmth.sop.business.entity.SysUser;
import com.qmth.sop.business.entity.TBDingSubmit;
import com.qmth.sop.business.mapper.TBDingSubmitMapper;
import com.qmth.sop.business.service.SysUserService;
import com.qmth.sop.business.service.TBDingSubmitService;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.DingSubmitStatusEnum;
import com.qmth.sop.common.enums.ExceptionResultEnum;
import com.qmth.sop.common.enums.RoleTypeEnum;
import com.qmth.sop.common.util.DateDisposeUtils;
import com.qmth.sop.common.util.ServletUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description: 考勤提交服务实现类
 * @Author: CaoZixuan
 * @Date: 2023/08/25
 */
@Service
public class TBDingSubmitServiceImpl extends ServiceImpl<TBDingSubmitMapper, TBDingSubmit> implements TBDingSubmitService {
    @Resource
    private SysUserService sysUserService;
    @Resource
    private TBDingServiceImpl tbDingService;

    @Override
    public IPage<DingSubmitResult> findDingSubmitPage(Long serviceUnitId, String dingSubmitStatusDesc, Long submitUserId,
                                                      String archivesName, Long supplierId, String customName, String sopNo,
                                                      Integer violationHoursLimit, Integer dingExceptionLimit, Integer remainLimit, Integer exceptionLimit,
                                                      Integer pageNumber, Integer pageSize) {

        List<DingSubmitStatusEnum> dingSubmitStatusEnumList = new ArrayList<>();
        if (dingSubmitStatusDesc != null && dingSubmitStatusDesc.length() > 0) {
            dingSubmitStatusEnumList = Arrays.stream(DingSubmitStatusEnum.values())
                    .filter(e -> e.getSubmitDesc().equals(dingSubmitStatusDesc))
                    .collect(Collectors.toList());
        }
        archivesName = SystemConstant.translateSpecificSign(archivesName);
        customName = SystemConstant.translateSpecificSign(customName);

        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        if (dpr.getHasThirdPmo()) {
            // 乙方PMO
            Long userSupplierId = requestUser.getSupplierId();
            if (userSupplierId == null || userSupplierId == 0) {
                throw ExceptionResultEnum.ERROR.exception(String.format("请先在用户管理中绑定[%s]对应的供应商", RoleTypeEnum.THIRD_PMO.getDesc()));
            }
            supplierId = userSupplierId;
        }
        IPage<DingSubmitResult> page = this.baseMapper.findDingSubmitPage(new Page<>(pageNumber, pageSize), serviceUnitId, dingSubmitStatusEnumList,
                submitUserId, archivesName, supplierId, customName, sopNo, violationHoursLimit, dingExceptionLimit, remainLimit, exceptionLimit, dpr);
        for (DingSubmitResult record : page.getRecords()) {
            record.setRemainCount(tbDingService.findRemainCount(record.getUserId(), record.getSopNo()));
        }
        return page;
    }

    @Override
    public DingSubmitSubTotalResult findDingSubmitSubTotal(Long serviceUnitId, String dingSubmitStatusDesc, Long submitUserId,
                                                           String archivesName, Long supplierId, String customName, String sopNo,
                                                           Integer violationHoursLimit, Integer dingExceptionLimit,
                                                           Integer remainLimit, Integer exceptionLimit) {
        List<DingSubmitStatusEnum> dingSubmitStatusEnumList = new ArrayList<>();
        if (dingSubmitStatusDesc != null && dingSubmitStatusDesc.length() > 0) {
            dingSubmitStatusEnumList = Arrays.stream(DingSubmitStatusEnum.values())
                    .filter(e -> e.getSubmitDesc().equals(dingSubmitStatusDesc))
                    .collect(Collectors.toList());
        }
        archivesName = SystemConstant.translateSpecificSign(archivesName);
        customName = SystemConstant.translateSpecificSign(customName);

        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        if (dpr.getHasThirdPmo()) {
            // 乙方PMO
            Long userSupplierId = requestUser.getSupplierId();
            if (userSupplierId == null || userSupplierId == 0) {
                throw ExceptionResultEnum.ERROR.exception(String.format("请先在用户管理中绑定[%s]对应的供应商", RoleTypeEnum.THIRD_PMO.getDesc()));
            }
            supplierId = userSupplierId;
        }

        List<DingSubmitResult> dingSubmitResultList = this.baseMapper.findDingSubmitList(serviceUnitId, dingSubmitStatusEnumList,
                submitUserId, null, archivesName, supplierId, customName, sopNo, violationHoursLimit, dingExceptionLimit, remainLimit, exceptionLimit, dpr);

        Integer dingCount = dingSubmitResultList.size();
        Integer willSubmitCount = Math.toIntExact(dingSubmitResultList.stream().filter(e -> e.getSubmitStatus().getSubmitDesc().equals("待提交")).count());
        Integer submitCount = Math.toIntExact(dingSubmitResultList.stream().filter(e -> e.getSubmitStatus().getSubmitDesc().equals("已提交")).count());
        Integer totalActualDays = dingSubmitResultList.stream().mapToInt(DingSubmitResult::getActualDays).sum();
        Double totalWorkHours = dingSubmitResultList.stream()
                .map(e -> new BigDecimal(String.valueOf(e.getWorkHours())))
                .reduce(BigDecimal::add)
                .orElse(BigDecimal.ZERO)
                .doubleValue();
        DingSubmitSubTotalResult dingSubmitSubTotalResult = new DingSubmitSubTotalResult();
        dingSubmitSubTotalResult.setDingCount(dingCount);
        dingSubmitSubTotalResult.setWillSubmitCount(willSubmitCount);
        dingSubmitSubTotalResult.setSubmitCount(submitCount);
        dingSubmitSubTotalResult.setTotalActualDays(totalActualDays);
        dingSubmitSubTotalResult.setTotalWorkHours(totalWorkHours);
        return dingSubmitSubTotalResult;
    }

    @Transactional
    @Override
    public void submitDingBatch(List<DingSubmitParam> dingSubmitParamList) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();

        List<DingSubmitStatusEnum> dingSubmitStatusEnumList = new ArrayList<>();
        dingSubmitStatusEnumList.add(DingSubmitStatusEnum.WILL_SUBMIT);
        dingSubmitStatusEnumList.add(DingSubmitStatusEnum.AGREE_WITHDRAW);
        List<TBDingSubmit> tbDingSubmitList = new ArrayList<>();

        for (DingSubmitParam dingSubmitParam : dingSubmitParamList) {
            Long serviceUnitId = dingSubmitParam.getServiceUnitId();
            String sopNo = dingSubmitParam.getSopNo();
            Long userArchivesId = dingSubmitParam.getUserArchivesId();
            List<DingSubmitResult> dingSubmitResultList = this.baseMapper.findDingSubmitList(serviceUnitId, null,
                    null, userArchivesId, null, null, null, sopNo,
                    null, null, null, null, null);

            if (dingSubmitResultList.size() != 1) {
                throw ExceptionResultEnum.ERROR.exception(String.format("未找到服务单元id为[%s],sop流水号为[%s],用户档案号为[%s]的考勤信息", serviceUnitId, sopNo, userArchivesId));
            }
            DingSubmitResult dingSubmitResult = dingSubmitResultList.get(0);
            String serviceUnitName = dingSubmitResult.getServiceUnitName();
            String userArchivesName = dingSubmitResult.getArchivesName();


            // 条件1 - 提交必须是待提交状态
            DingSubmitStatusEnum submitStatus = dingSubmitResult.getSubmitStatus();
            if (!dingSubmitStatusEnumList.contains(submitStatus)) {
                throw ExceptionResultEnum.ERROR.exception(String.format("服务单元为[%s],sop流水号为[%s],用户档案为[%s]的考勤的提交状态为[%s],不能提交",
                        serviceUnitName, sopNo, userArchivesName, submitStatus.getSubmitDesc()));
            }
            // 条件2 - 待处理的异常数为0
            Integer exceptionCount = dingSubmitResult.getExceptionCount();
            if (exceptionCount != 0) {
                throw ExceptionResultEnum.ERROR.exception(String.format("服务单元为[%s],sop流水号为[%s],用户档案为[%s]的考勤待处理异常数为[%s],不能提交",
                        serviceUnitName, sopNo, userArchivesName, exceptionCount));
            }

            Long submitId = dingSubmitResult.getSubmitId();
            TBDingSubmit tbDingSubmit = new TBDingSubmit();
            tbDingSubmit.setServiceId(serviceUnitId);
            tbDingSubmit.setSopNo(sopNo);
            tbDingSubmit.setCrmNo(dingSubmitResult.getCrmNo());
            tbDingSubmit.setUserArchivesId(userArchivesId);
            tbDingSubmit.setApproachTime(dingSubmitResult.getApproachTime());
            tbDingSubmit.setDepartureTime(dingSubmitResult.getDepartureTime());
            tbDingSubmit.setRoleType(dingSubmitResult.getRoleType());
            tbDingSubmit.setActualDays(dingSubmitResult.getActualDays());
            tbDingSubmit.setWeekdays(dingSubmitResult.getWeekdays());
            tbDingSubmit.setWeekends(dingSubmitResult.getWeekends());
            tbDingSubmit.setLegalHolidays(dingSubmitResult.getLegalHolidays());
            tbDingSubmit.setWorkHours(dingSubmitResult.getWorkHours());
            tbDingSubmit.setViolationDays(dingSubmitResult.getViolationDays());
            tbDingSubmit.setSubmitUserId(requestUser.getId());
            tbDingSubmit.setSubmitTime(System.currentTimeMillis());
            tbDingSubmit.setSubmitStatus(DingSubmitStatusEnum.ALREADY_SUBMIT);
            if (submitId != null && submitId > 0) {
                tbDingSubmit.setId(submitId);
            }
            tbDingSubmitList.add(tbDingSubmit);
        }
        this.saveOrUpdateBatch(tbDingSubmitList);
    }

    @Transactional
    @Override
    public void applyWithdraw(DingSubmitParam dingSubmitParam) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();

        Long serviceUnitId = dingSubmitParam.getServiceUnitId();
        String sopNo = dingSubmitParam.getSopNo();
        Long userArchivesId = dingSubmitParam.getUserArchivesId();
        List<DingSubmitResult> dingSubmitResultList = this.baseMapper.findDingSubmitList(serviceUnitId, null,
                null, userArchivesId, null, null, null, sopNo,
                null, null, null, null, null);

        if (dingSubmitResultList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception(String.format("未找到服务单元id为[%s],sop流水号为[%s],用户档案号为[%s]的考勤信息", serviceUnitId, sopNo, userArchivesId));
        }
        DingSubmitResult dingSubmitResult = dingSubmitResultList.get(0);
        String serviceUnitName = dingSubmitResult.getServiceUnitName();
        String userArchivesName = dingSubmitResult.getArchivesName();

        DingSubmitStatusEnum submitStatus = dingSubmitResult.getSubmitStatus();
        if (!DingSubmitStatusEnum.ALREADY_SUBMIT.equals(submitStatus)) {
            throw ExceptionResultEnum.ERROR.exception(String.format("服务单元为[%s],sop流水号为[%s],用户档案为[%s]的考勤的提交状态为[%s],不能申请撤回",
                    serviceUnitName, sopNo, userArchivesName, submitStatus.getSubmitDesc()));
        }

        Long submitId = dingSubmitResult.getSubmitId();
        if (submitId == null || submitId == 0) {
            throw ExceptionResultEnum.ERROR.exception(String.format("服务单元为[%s],sop流水号为[%s],用户档案为[%s]的考勤数据异常", serviceUnitName, sopNo, userArchivesName));
        }
        TBDingSubmit tbDingSubmit = new TBDingSubmit();
        tbDingSubmit.setId(submitId);
        tbDingSubmit.setServiceId(serviceUnitId);
        tbDingSubmit.setSopNo(sopNo);
        tbDingSubmit.setCrmNo(dingSubmitResult.getCrmNo());
        tbDingSubmit.setUserArchivesId(userArchivesId);
        tbDingSubmit.setApproachTime(dingSubmitResult.getApproachTime());
        tbDingSubmit.setDepartureTime(dingSubmitResult.getDepartureTime());
        tbDingSubmit.setRoleType(dingSubmitResult.getRoleType());
        tbDingSubmit.setActualDays(dingSubmitResult.getActualDays());
        tbDingSubmit.setWeekdays(dingSubmitResult.getWeekdays());
        tbDingSubmit.setWeekends(dingSubmitResult.getWeekends());
        tbDingSubmit.setLegalHolidays(dingSubmitResult.getLegalHolidays());
        tbDingSubmit.setWorkHours(dingSubmitResult.getWorkHours());
        tbDingSubmit.setViolationDays(dingSubmitResult.getViolationDays());
        tbDingSubmit.setSubmitUserId(requestUser.getId());
        tbDingSubmit.setSubmitTime(System.currentTimeMillis());
        tbDingSubmit.setSubmitStatus(DingSubmitStatusEnum.APPLY_WITHDRAW);
        this.updateById(tbDingSubmit);
    }

    @Transactional
    @Override
    public void cancelWithdraw(DingSubmitParam dingSubmitParam) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();

        Long serviceUnitId = dingSubmitParam.getServiceUnitId();
        String sopNo = dingSubmitParam.getSopNo();
        Long userArchivesId = dingSubmitParam.getUserArchivesId();
        List<DingSubmitResult> dingSubmitResultList = this.baseMapper.findDingSubmitList(serviceUnitId, null,
                null, userArchivesId, null, null, null, sopNo,
                null, null, null, null, null);

        if (dingSubmitResultList.size() != 1) {
            throw ExceptionResultEnum.ERROR.exception(String.format("未找到服务单元id为[%s],sop流水号为[%s],用户档案号为[%s]的考勤信息", serviceUnitId, sopNo, userArchivesId));
        }
        DingSubmitResult dingSubmitResult = dingSubmitResultList.get(0);
        String serviceUnitName = dingSubmitResult.getServiceUnitName();
        String userArchivesName = dingSubmitResult.getArchivesName();

        DingSubmitStatusEnum submitStatus = dingSubmitResult.getSubmitStatus();
        if (!DingSubmitStatusEnum.APPLY_WITHDRAW.equals(submitStatus)) {
            throw ExceptionResultEnum.ERROR.exception(String.format("服务单元为[%s],sop流水号为[%s],用户档案为[%s]的考勤的提交状态为[%s],不能申请撤回",
                    serviceUnitName, sopNo, userArchivesName, submitStatus.getSubmitDesc()));
        }

        Long submitId = dingSubmitResult.getSubmitId();
        if (submitId == null || submitId == 0) {
            throw ExceptionResultEnum.ERROR.exception(String.format("服务单元为[%s],sop流水号为[%s],用户档案为[%s]的考勤数据异常", serviceUnitName, sopNo, userArchivesName));
        }
        TBDingSubmit tbDingSubmit = new TBDingSubmit();
        tbDingSubmit.setId(submitId);
        tbDingSubmit.setServiceId(serviceUnitId);
        tbDingSubmit.setSopNo(sopNo);
        tbDingSubmit.setCrmNo(dingSubmitResult.getCrmNo());
        tbDingSubmit.setUserArchivesId(userArchivesId);
        tbDingSubmit.setApproachTime(dingSubmitResult.getApproachTime());
        tbDingSubmit.setDepartureTime(dingSubmitResult.getDepartureTime());
        tbDingSubmit.setRoleType(dingSubmitResult.getRoleType());
        tbDingSubmit.setActualDays(dingSubmitResult.getActualDays());
        tbDingSubmit.setWeekdays(dingSubmitResult.getWeekdays());
        tbDingSubmit.setWeekends(dingSubmitResult.getWeekends());
        tbDingSubmit.setLegalHolidays(dingSubmitResult.getLegalHolidays());
        tbDingSubmit.setWorkHours(dingSubmitResult.getWorkHours());
        tbDingSubmit.setViolationDays(dingSubmitResult.getViolationDays());
        tbDingSubmit.setSubmitUserId(requestUser.getId());
        tbDingSubmit.setSubmitTime(System.currentTimeMillis());
        tbDingSubmit.setSubmitStatus(DingSubmitStatusEnum.ALREADY_SUBMIT);
        this.updateById(tbDingSubmit);
    }

    @Transactional
    @Override
    public void agreeWithdraw(Long dingSubmitId) {
        TBDingSubmit tbDingSubmit = this.getById(dingSubmitId);
        if (Objects.isNull(tbDingSubmit)) {
            throw ExceptionResultEnum.ERROR.exception("未找到工时统计记录");
        }
        DingSubmitStatusEnum status = tbDingSubmit.getSubmitStatus();
        if (!DingSubmitStatusEnum.APPLY_WITHDRAW.equals(status)) {
            throw ExceptionResultEnum.ERROR.exception("只有[申请撤回]状态的可以同意");
        }
        tbDingSubmit.setSubmitStatus(DingSubmitStatusEnum.AGREE_WITHDRAW);
        this.updateById(tbDingSubmit);
    }

    @Override
    public List<DingSubmitExportDto> findDingSubmitExportList(Long serviceUnitId, String dingSubmitStatusDesc, Long submitUserId, String archivesName, Long supplierId, String customName, String sopNo, Integer violationHoursLimit, Integer dingExceptionLimit, Integer remainLimit, Integer exceptionLimit, Long requestUserId) {
        List<DingSubmitStatusEnum> dingSubmitStatusEnumList = new ArrayList<>();
        if (dingSubmitStatusDesc != null && dingSubmitStatusDesc.length() > 0) {
            dingSubmitStatusEnumList = Arrays.stream(DingSubmitStatusEnum.values())
                    .filter(e -> e.getSubmitDesc().equals(dingSubmitStatusDesc))
                    .collect(Collectors.toList());
        }
        archivesName = SystemConstant.translateSpecificSign(archivesName);
        customName = SystemConstant.translateSpecificSign(customName);

        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUserId);
        if (dpr.getHasThirdPmo()) {
            SysUser requestUser = sysUserService.getById(requestUserId);
            // 乙方PMO
            Long userSupplierId = requestUser.getSupplierId();
            if (userSupplierId == null || userSupplierId == 0) {
                throw ExceptionResultEnum.ERROR.exception(String.format("请先在用户管理中绑定[%s]对应的供应商", RoleTypeEnum.THIRD_PMO.getDesc()));
            }
            supplierId = userSupplierId;
        }

        List<DingSubmitResult> dingSubmitResultList = this.baseMapper.findDingSubmitList(serviceUnitId, dingSubmitStatusEnumList,
                submitUserId, null, archivesName, supplierId, customName, sopNo, violationHoursLimit, dingExceptionLimit,
                remainLimit, exceptionLimit, dpr);

        return dingSubmitResultList.stream().flatMap(e -> {
            DingSubmitExportDto dto = new DingSubmitExportDto();
            dto.setServiceUnitName(e.getServiceUnitName());
            dto.setSopNo(e.getSopNo());
            dto.setCustomName(e.getCustomName());
            dto.setProvince(e.getProvince());
            dto.setCity(e.getCity());
            if (e.getApproachTime() != null && e.getApproachTime() > 0){
                dto.setApproachTime(DateDisposeUtils.getDateStr(e.getApproachTime()));
            }
            if (e.getDepartureTime() != null && e.getDepartureTime() > 0){
                dto.setDepartureTime(DateDisposeUtils.getDateStr(e.getDepartureTime()));
            }
            dto.setArchivesInfo(e.getArchivesInfo());
            dto.setRoleType(e.getRoleType().getDesc());
            dto.setSupplierName(e.getSupplierName());
            dto.setActualDays(e.getActualDays());
            dto.setWeekdays(e.getWeekdays());
            dto.setWeekends(e.getWeekends());
            dto.setLegalHolidays(e.getLegalHolidays());
            dto.setWorkHours(e.getWorkHours());
            dto.setViolationDays(e.getViolationDays());
            dto.setDingExceptionCount(e.getDingExceptionCount());
            dto.setRemainCount(e.getRemainCount());
            dto.setExceptionCount(e.getExceptionCount());
            dto.setSubmitStatus(e.getSubmitStatus().getSubmitDesc());
            dto.setSubmitUserName(e.getSubmitUserName());
            if (e.getSubmitTime() != null && e.getSubmitTime() > 0){
                dto.setSubmitTime(DateDisposeUtils.getDateStr(e.getSubmitTime()));
            }
            return Stream.of(dto);
        }).collect(Collectors.toList());
    }
}
