package com.hz.pm.api.workbench.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Sets;
import com.hz.pm.api.common.helper.UserInfoHelper;
import com.hz.pm.api.common.model.constant.BizConst;
import com.hz.pm.api.projectdeclared.manage.DefaultDeclaredProjectManage;
import com.hz.pm.api.projectlib.manage.ProjectLibManage;
import com.hz.pm.api.projectlib.model.entity.Project;
import com.hz.pm.api.projectlib.model.req.ProjectListReq;
import com.hz.pm.api.projectlib.service.IProjectService;
import com.hz.pm.api.sys.enumeration.ProjectEarlyWarningStatusEnum;
import com.hz.pm.api.sys.manage.EarlyWarningManage;
import com.hz.pm.api.sys.manage.NoticeManage;
import com.hz.pm.api.sys.model.entity.ProjectEarlyWarning;
import com.hz.pm.api.sys.model.entity.WflowEarlyWarningRecords;
import com.hz.pm.api.sys.model.req.NoticeListReq;
import com.hz.pm.api.sys.model.vo.ProjectEarlyWarningVO;
import com.hz.pm.api.sys.service.IEarlyWarningRecordsService;
import com.hz.pm.api.sys.service.IProjectEarlyWarningService;
import com.hz.pm.api.sys.utils.EarlyWarningUtil;
import com.hz.pm.api.todocenter.manage.TodoCenterManage;
import com.hz.pm.api.todocenter.model.req.ToBeProcessedReq;
import com.hz.pm.api.todocenter.model.vo.TodoCenterStatisticsVO;
import com.hz.pm.api.user.security.model.UserFullInfoDTO;
import com.hz.pm.api.user.util.LoginUserUtil;
import com.hz.pm.api.workbench.converter.WorkbenchConverter;
import com.hz.pm.api.workbench.model.vo.WorkbenchVO;
import com.ningdatech.basic.function.VUtils;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.StrPool;
import com.wflow.bean.entity.WflowEarlyWarning;
import com.wflow.enums.WarningNoticeTypeEnum;
import com.wflow.service.IEarlyWarningService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;

/**
 * @Classname WorkbenchManage
 * @Description
 * @Date 2023/3/20 10:56
 * @Author PoffyZhang
 */
@Component
@AllArgsConstructor
@Slf4j
public class WorkbenchManage {

    private final TodoCenterManage todoCenterManage;

    private final DefaultDeclaredProjectManage defaultDeclaredProjectManage;

    private final IEarlyWarningRecordsService earlyWarningRecordsService;

    private final IEarlyWarningService earlyWarningService;

    private final IProjectEarlyWarningService projectEarlyWarningService;

    private final NoticeManage noticeManage;

    private final UserInfoHelper userInfoHelper;

    private final ProjectLibManage projectLibManage;

    private final EarlyWarningManage earlyWarningManage;

    private final IProjectService projectService;

    public WorkbenchVO getWorkbenchData(Integer year) {
        WorkbenchVO res = new WorkbenchVO();
        Long userId = LoginUserUtil.getUserId();
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(userId);

        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    StopWatch stopWatch = new StopWatch();
                    stopWatch.start();
                    //1.待办中心数据
                    TodoCenterStatisticsVO statisticsVO = todoCenterManage.todoCenterStatistics(user.getUserId());
                    ToBeProcessedReq toBeProcessedReq = new ToBeProcessedReq();
                    toBeProcessedReq.page(1, 5);
                    toBeProcessedReq.setLoginUserId(userId);
                    toBeProcessedReq.setUserId(user.getUserId());
                    statisticsVO.setTodoList(todoCenterManage.todoProjectList(toBeProcessedReq).getRecords()
                            .stream().map(WorkbenchConverter::convert).collect(Collectors.toList()));
                    res.setTodoCerter(statisticsVO);
                    stopWatch.stop();
                    log.info("待办中心数据 耗时,{} s", stopWatch.getTotalTimeSeconds());
                }, ForkJoinPool.commonPool()),
                CompletableFuture.runAsync(() -> {
                    StopWatch stopWatch = new StopWatch();
                    stopWatch.start();

                    //2.项目统计数据
                    res.setOrgDeclared(WorkbenchConverter.convert(defaultDeclaredProjectManage
                            .declaredProjectOrgStatistics(year, user), defaultDeclaredProjectManage.declaredProjectOrgStatistics(year - 1, user)));
                    if (userInfoHelper.isSuperOrRegionAdmin(user.getUserId())) {
                        res.setRegionDeclared(WorkbenchConverter.convert(defaultDeclaredProjectManage
                                .declaredProjectRegionStatistics(year, user), defaultDeclaredProjectManage.declaredProjectRegionStatistics(year - 1, user)));
                    } else {
                        res.setRegionDeclared(new WorkbenchVO.DeclaredStatistics());
                    }
                    stopWatch.stop();
                    log.info("项目统计数据 耗时,{} s", stopWatch.getTotalTimeSeconds());
                }, ForkJoinPool.commonPool()),
                CompletableFuture.runAsync(() -> {
                    StopWatch stopWatch = new StopWatch();
                    stopWatch.start();

                    //3.所有公告按类型分
                    NoticeListReq noticeListReq = new NoticeListReq();
                    noticeListReq.page(1, 1000);
                    res.setNoticeList(noticeManage.listToMapByManager(noticeListReq, user));
                    stopWatch.stop();
                    log.info("公告 耗时,{} s", stopWatch.getTotalTimeSeconds());
                }, ForkJoinPool.commonPool()),
                CompletableFuture.runAsync(() -> {
                    StopWatch stopWatch = new StopWatch();
                    stopWatch.start();

                    //4.项目预警记录
                    res.setProjectEarlyWarning(getProjectEarlyWarning(user));
                    log.info("项目预警记录 耗时,{} s", stopWatch.getTotalTimeSeconds());
                }, ForkJoinPool.commonPool()),
                CompletableFuture.runAsync(() -> {
                    StopWatch stopWatch = new StopWatch();
                    stopWatch.start();

                    // 项目列表
                    ProjectListReq projectListReq = new ProjectListReq();
                    projectListReq.page(1, 2000);
                    projectListReq.setProjectYear(year);
                    res.setProjects(new ArrayList<>(projectLibManage.listWithPermissionWorkBench(projectListReq, user).getRecords()));
                    log.info("工作台 项目列表 耗时,{} s", stopWatch.getTotalTimeSeconds());
                }, ForkJoinPool.commonPool())
        ).join();

        return res;
    }

    private WorkbenchVO.ProjectEarlyWarning getProjectEarlyWarning(UserFullInfoDTO user) {
        WorkbenchVO.ProjectEarlyWarning projectEarlyWarning = new WorkbenchVO.ProjectEarlyWarning();
        LambdaQueryWrapper<Project> projectWrapper = Wrappers.lambdaQuery(Project.class)
                .eq(Project::getNewest, Boolean.TRUE);
        EarlyWarningUtil.buildPermissionProjectWrapper(projectWrapper, user);
        List<Project> projects = projectService.list(projectWrapper);
        Set<String> projectCodeS = Sets.newHashSet();
        Map<String, String> projectNameMap = projects.stream()
                .filter(p -> projectCodeS.add(p.getProjectCode()))
                .collect(Collectors.toMap(Project::getProjectCode, Project::getProjectName));

        //4.1
        //累积预警统计
        WorkbenchVO.WarningStatistics accumulate = new WorkbenchVO.WarningStatistics();
        LambdaQueryWrapper<WflowEarlyWarningRecords> overWrapper = Wrappers.lambdaQuery(WflowEarlyWarningRecords.class)
                .eq(WflowEarlyWarningRecords::getNoticeType, WarningNoticeTypeEnum.OVER.getCode())
                .or(q2 -> q2.isNull(WflowEarlyWarningRecords::getNoticeType));
        EarlyWarningUtil.buildPermissionFullWrapper(overWrapper, user);
        long overCount = earlyWarningRecordsService.count(overWrapper);
        accumulate.setOver((int) overCount);
        LambdaQueryWrapper<WflowEarlyWarningRecords> adventWrapper = Wrappers.lambdaQuery(WflowEarlyWarningRecords.class)
                .eq(WflowEarlyWarningRecords::getNoticeType, WarningNoticeTypeEnum.ADVENT.getCode());
        EarlyWarningUtil.buildPermissionFullWrapper(adventWrapper, user);
        long adventCount = earlyWarningRecordsService.count(adventWrapper);
        accumulate.setAdvent((int) adventCount);
        projectEarlyWarning.setAccumulate(accumulate);

        //4.2
        //当前
        //4.2.1 超期
        Page<ProjectEarlyWarning> overPage = Page.of(1, 1000);
        Page<ProjectEarlyWarning> adventPage = Page.of(1, 1000);
        Page<ProjectEarlyWarning> normalPage = Page.of(1, 1000);
        LambdaQueryWrapper<ProjectEarlyWarning> overPewWrapper = Wrappers.lambdaQuery(ProjectEarlyWarning.class)
                .eq(ProjectEarlyWarning::getStatus, ProjectEarlyWarningStatusEnum.OVER_TIME.name())
                .eq((user.getIsOrgAdmin() && !user.getSuperAdmin()) || user.notAdmin(), ProjectEarlyWarning::getBuildOrgCode, user.getMhUnitId());
        projectEarlyWarningService.page(overPage, overPewWrapper);
        LambdaQueryWrapper<ProjectEarlyWarning> adventPewWrapper = Wrappers.lambdaQuery(ProjectEarlyWarning.class)
                .eq(ProjectEarlyWarning::getStatus, ProjectEarlyWarningStatusEnum.ADVENT_TIME.name())
                .eq((user.getIsOrgAdmin() && !user.getSuperAdmin()) || user.notAdmin(), ProjectEarlyWarning::getBuildOrgCode, user.getMhUnitId());
        projectEarlyWarningService.page(adventPage, adventPewWrapper);
        LambdaQueryWrapper<ProjectEarlyWarning> normalWrapper = Wrappers.lambdaQuery(ProjectEarlyWarning.class)
                .eq(ProjectEarlyWarning::getStatus, ProjectEarlyWarningStatusEnum.NORMAL.name())
                .eq((user.getIsOrgAdmin() && !user.getSuperAdmin()) || user.notAdmin(), ProjectEarlyWarning::getBuildOrgCode, user.getMhUnitId());
        projectEarlyWarningService.page(normalPage, normalWrapper);

        WorkbenchVO.WarningStatistics cuurent = new WorkbenchVO.WarningStatistics();
        long overTotal = overPage.getTotal();
        long adventTotal = adventPage.getTotal();
        long normalTotal = normalPage.getTotal();
        cuurent.setAdvent((int) adventTotal);
        cuurent.setOver((int) overTotal);
        cuurent.setNormal((int) normalTotal);
        projectEarlyWarning.setCurrent(cuurent);

        List<WflowEarlyWarningRecords> warningRecords = earlyWarningRecordsService.list(Wrappers.lambdaQuery(WflowEarlyWarningRecords.class)
                .eq((user.getIsOrgAdmin() && !user.getSuperAdmin()) || user.notAdmin(), WflowEarlyWarningRecords::getBuildOrgCode, user.getMhUnitId())
                .orderByDesc(WflowEarlyWarningRecords::getWarningTime));
        Set<String> projectCodeSet = Sets.newHashSet();
        Map<String, WflowEarlyWarningRecords> warningMap = warningRecords.stream()
                .filter(w -> projectCodeSet.add(w.getProjectCode()))
                .collect(Collectors.toMap(WflowEarlyWarningRecords::getProjectCode, w -> w));

        if (CollUtil.isNotEmpty(overPage.getRecords())) {
            LocalDateTime now = LocalDateTime.now();
            List<ProjectEarlyWarningVO> overVos = overPage.getRecords().stream().map(o -> {
                        ProjectEarlyWarningVO vo = BeanUtil.copyProperties(o, ProjectEarlyWarningVO.class);
                        //取最新一条预警信息
                        if (warningMap.containsKey(vo.getProjectCode())) {
                            WflowEarlyWarningRecords record = warningMap.get(vo.getProjectCode());
                            vo.setWarningTime(record.getWarningTime());
                            vo.setInstStart(record.getInstStart());
                            vo.setWarningUserId(record.getWarningUserId());
                            vo.setNoticeMethod(record.getNoticeMethod());
                            vo.setNoticeContent(record.getNoticeContent());
                            vo.setPath(record.getPath());
                            vo.setBatchUserIds(record.getBatchUserIds());
                            vo.setRecordId(record.getId());
                        } else {
                            vo.setWarningTime(now);
                        }
                        vo.setProjectName(projectNameMap.get(o.getProjectCode()));
                        return vo;
                    }).sorted(Comparator.comparing(ProjectEarlyWarningVO::getWarningTime).reversed())
                    .collect(Collectors.toList());
            projectEarlyWarning.setOvers(PageVo.of(overVos, overPage.getTotal()));
        } else {
            projectEarlyWarning.setOvers(PageVo.empty());
        }

        if (CollUtil.isNotEmpty(adventPage.getRecords())) {
            LocalDateTime now = LocalDateTime.now();
            List<ProjectEarlyWarningVO> adventVos = adventPage.getRecords().stream().map(o -> {
                        ProjectEarlyWarningVO vo = BeanUtil.copyProperties(o, ProjectEarlyWarningVO.class);
                        //取最新一条预警信息
                        if (warningMap.containsKey(vo.getProjectCode())) {
                            WflowEarlyWarningRecords record = warningMap.get(vo.getProjectCode());
                            vo.setWarningTime(record.getWarningTime());
                            vo.setInstStart(record.getInstStart());
                            vo.setWarningUserId(record.getWarningUserId());
                            vo.setNoticeMethod(record.getNoticeMethod());
                            vo.setNoticeContent(record.getNoticeContent());
                            vo.setPath(record.getPath());
                            vo.setBatchUserIds(record.getBatchUserIds());
                            vo.setRecordId(record.getId());
                        } else {
                            vo.setWarningTime(now);
                        }
                        vo.setProjectName(projectNameMap.get(o.getProjectCode()));
                        return vo;
                    }).sorted(Comparator.comparing(ProjectEarlyWarningVO::getWarningTime).reversed())
                    .collect(Collectors.toList());
            projectEarlyWarning.setAdvents(PageVo.of(adventVos, adventPage.getTotal()));
        } else {
            projectEarlyWarning.setAdvents(PageVo.empty());
        }

        if (CollUtil.isNotEmpty(normalPage.getRecords())) {
            List<ProjectEarlyWarningVO> normalVos = normalPage.getRecords().stream().map(o -> {
                        ProjectEarlyWarningVO vo = BeanUtil.copyProperties(o, ProjectEarlyWarningVO.class);
                        //取最新一条预警信息
                        if (warningMap.containsKey(vo.getProjectCode())) {
                            WflowEarlyWarningRecords record = warningMap.get(vo.getProjectCode());
                            vo.setWarningTime(record.getWarningTime());
                            vo.setInstStart(record.getInstStart());
                            vo.setWarningUserId(record.getWarningUserId());
                            vo.setNoticeMethod(record.getNoticeMethod());
                            vo.setNoticeContent(record.getNoticeContent());
                            vo.setPath(record.getPath());
                            vo.setBatchUserIds(record.getBatchUserIds());
                            vo.setRecordId(record.getId());
                        }
                        vo.setProjectName(projectNameMap.get(o.getProjectCode()));
                        return vo;
                    }).sorted(Comparator.comparing(ProjectEarlyWarningVO::getId).reversed())
                    .collect(Collectors.toList());
            projectEarlyWarning.setNormals(PageVo.of(normalVos, normalPage.getTotal()));
        } else {
            projectEarlyWarning.setNormals(PageVo.empty());
        }
        return projectEarlyWarning;
    }

    /**
     * 催办
     *
     * @param recordId
     * @return
     */
    public String urging(Long recordId) {
        WflowEarlyWarningRecords record = earlyWarningRecordsService.getById(recordId);

        VUtils.isTrue(Objects.isNull(record))
                .throwMessage("催办失败,id不存在!");
        String noticeMethod = record.getNoticeMethod();
        Integer noticeType = record.getNoticeType();
        String projectCode = record.getProjectCode();
        Project project = projectService.getProjectByCode(projectCode);
        String batchEmployees = record.getBatchUserIds();
        Integer ruleType = record.getRuleType();
        String areaCode = record.getAreaCode();
        //如果是临期 可以取到 超期的小时数
        String path = record.getPath();
        LocalDateTime instStart = record.getInstStart();
        Integer biz = record.getBiz();
        VUtils.isTrue(Objects.isNull(project)).throwMessage("催办失败,项目不存在!");
        if (StringUtils.isBlank(batchEmployees)) {
            //如果批次员工号不存在 就取当前
            batchEmployees = record.getWarningUserId();
        }
        String noticeContent = record.getNoticeContent();
        //查出配置的模板
        WflowEarlyWarning config = earlyWarningService.getOne(Wrappers.lambdaQuery(WflowEarlyWarning.class)
                .eq(WflowEarlyWarning::getAreaCode, areaCode)
                .eq(WflowEarlyWarning::getRuleType, ruleType)
                .last(BizConst.LIMIT_1));
        //如果配置能取到 就取模板的
        if (Objects.nonNull(config)) {
            noticeContent = config.getNoticeContent();
        }

        VUtils.isTrue(StringUtils.isBlank(batchEmployees))
                .throwMessage("催办失败,员工为空!");
        String[] employees = batchEmployees.split(StrPool.COMMA);
        //算出 当前和他的 小时差 临期
        long adventBetween = ChronoUnit.HOURS.between(instStart, LocalDateTime.now());
        for (String employee : employees) {
            earlyWarningManage.urging(noticeMethod, noticeContent,
                    (int) adventBetween, (int) adventBetween,
                    biz, instStart, Long.valueOf(employee),
                    project, ruleType, noticeType, path,
                    batchEmployees, record.getNodeId());
        }
        return "催办成功了" + employees.length + "个人";
    }

}
