package com.trade.admin.workflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.trade.admin.system.domain.entity.SysUser;
import com.trade.admin.system.security.utils.SecurityUtils;
import com.trade.admin.system.service.ISysUserService;
import com.trade.admin.workflow.domain.FlowActivityDefinition;
import com.trade.admin.workflow.domain.Project;
import com.trade.admin.workflow.domain.BusinessProcessingLog;
import com.trade.admin.workflow.dto.*;
import com.trade.admin.workflow.service.BusinessProcessingLogService;
import com.trade.admin.workflow.service.BusinessProcessingTaskService;
import com.trade.admin.workflow.service.FlowActivityDefinitionService;
import com.trade.admin.workflow.service.FlowableService;
import com.trade.admin.workflow.util.FlowableConstantUtil;
import com.trade.admin.workflow.util.FlowableUtil;
import com.trade.common.constant.HttpStatus;
import com.trade.common.core.page.GridPage;
import com.trade.common.exception.ServiceException;
import com.trade.common.exception.ValidatorException;

import com.trade.common.utils.ConstantUtil;
import com.trade.common.utils.DateUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class BusinessProcessingTaskServiceImpl implements BusinessProcessingTaskService {

    @Autowired
    private FlowableService flowableService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private FlowActivityDefinitionService flowActDefService;
    @Autowired
    private BusinessProcessingLogService processingLogService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public GridPage<BusinessProcessingTaskVo> findMyPendingList(SearchBusinessProcessingTask search) {
        if (null == search || StrUtil.isEmpty(search.getSimpleClassName())) {
            throw new ValidatorException(HttpStatus.ERROR, "列表分页条件不能为空！");
        }
        if (StrUtil.isNotEmpty(search.getSourceBusinessId())) {
            search.setSourceBusinessId(search.getSimpleClassName() + FlowableUtil.splitChar + search.getSourceBusinessId());
        }
        List<Task> myTasks = flowableService.findMyPendingListPage(search, search.getSimpleClassName());
        List<BusinessProcessingTaskVo> rows = new ArrayList<>();
        if (CollUtil.isNotEmpty(myTasks)) {
            for (Task task : myTasks) {
                rows.add(autoPendingVo(task, (Project) flowableService.getBusinessDataByProcessInst(task.getProcessInstanceId())));
            }
            rows.sort(Comparator.comparing(BusinessProcessingTaskVo::getGmtModified).reversed());
        }
        long count = flowableService.countMyPendingListPage(search, search.getSimpleClassName());
        return new GridPage<>(search.getRows(), search.getPage(), count, rows);
    }

    /**
     * 待我处理：生成列表立项视图对象
     *
     * @param task    待我处理的任务
     * @param project 立项对象
     * @return
     */
    private BusinessProcessingTaskVo autoPendingVo(Task task, Project project) {
        if (null != project && null != task) {
            BusinessProcessingLog log = processingLogService.getLatestByTask(project.getId(),
                    project.getClass().getSimpleName(), task.getId(), FlowableConstantUtil.PROCESS_TASK);
            BusinessProcessingTaskVo taskVo = new BusinessProcessingTaskVo();
            taskVo.setId(project.getId());
            taskVo.setBusinessNumber(project.getBusinessNumber());
            taskVo.setModuleNumber(project.getModuleNumber());
            taskVo.setProjectName(project.getProjectName());
            taskVo.setCustomerName(project.getCustomerName());
            taskVo.setSupplierName(project.getSupplierName());
            taskVo.setGmtModified(project.getGmtModified());
            taskVo.setStatus(project.getStatusDictionary());
            taskVo.setTaskId(task.getId());
            taskVo.setTaskDefKey(task.getTaskDefinitionKey());
            taskVo.setTaskName(task.getName());
            if (task.getAssignee() != null) {
                taskVo.setAssignee(sysUserService.selectUserById(Long.parseLong(task.getAssignee())));
            }

            //TODO 根据不同的Project 填充taskVo的值

            FlowActivityDefinition flowActDef = flowActDefService.getByProcActKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
            // 如果已分配，则按照分配的时间
            Date startTime = null;
            // 首先，确定该任务是待处理的任务，故直接获取其最新的处理日志，肯定为该任务办理人开始任务（或被分配任务）的记录。
            if (null != log) {
                startTime = log.getGmtCreate();// 若未分配，那么该时间为任务创建时间，若分配，那么该时间为办理人最新的任务分配时间
            }
            if (null != flowActDef) {
                Float deadline = null;
                if (flowActDef.getWorkingDay()) {
                    deadline = FlowableUtil.getBusinessDeadlineByWorkingDay(startTime, flowActDef.getTimeLimit());// 工作日时限
                } else {
                    deadline = FlowableUtil.getBusinessDeadlineByNatureDay(startTime, flowActDef.getTimeLimit());// 自然日时限
                }
                String deadlineStr = "";
                if (null != deadline) {
                    if (deadline < 0) {
                        deadlineStr = "逾期" + Math.abs(deadline) + (flowActDef.getWorkingDay() ? "个工作日" : "个自然日");
                    } else {
                        deadlineStr = deadline + (flowActDef.getWorkingDay() ? "个工作日" : "个自然日");
                    }
                }
                taskVo.setDeadline(deadlineStr);
            }
            return taskVo;
        }
        return null;
    }

    @Override
    public GridPage<BusinessProcessingTaskVo> findMyCompletedList(SearchBusinessProcessingTask search) {
        if (null == search || StrUtil.isEmpty(search.getSimpleClassName())) {
            throw new ValidatorException(HttpStatus.ERROR, "列表分页条件不能为空！");
        }
        search.setUserId(SecurityUtils.getUserId());
        List<BusinessProcessingTaskVo> rows = new ArrayList<>();
        Page<BusinessProcessingTaskVo> page = PageHelper.startPage(search.getPage(), search.getRows());


        //采购合同
        if (search.getSimpleClassName().equals(ConstantUtil.PURCHASE_CONTRACT)) {
            //rows = purchContractService.findMyCompletedList(search);
        }
        //验收单
        if (search.getSimpleClassName().equals(ConstantUtil.ACCEPTANCE_SHEET)) {
            // rows = acceptanceBasicInfoService.findMyCompletedList(search);
        }
        //结算单
        if (search.getSimpleClassName().equals(ConstantUtil.BILL_BASIC_INFORMATION)) {
            //rows = settlementBasicInfoService.findMyCompletedList(search);
        }
        //底层
        if (search.getSimpleClassName().equals(ConstantUtil.UNDERLYING_MANAGE)) {
            //rows = underlyingBasicInfoService.findMyCompletedList(search);
        }



        return new GridPage<>(search.getRows(), search.getPage(), page.getTotal(), rows);
    }

    @Override
    public GridPage<BusinessProcessingTaskVo> findAll(SearchBusinessProcessingTask search) {
        if (null == search || StrUtil.isEmpty(search.getSimpleClassName())) {
            throw new ValidatorException(HttpStatus.BAD_REQUEST, "列表分页条件不能为空！");
        }
        List<BusinessProcessingTaskVo> rows = new ArrayList<>();
        Page<BusinessProcessingTaskVo> page = PageHelper.startPage(search.getPage(), search.getRows());
        //TODO 根据不同的业务类型查询数据

        return new GridPage<>(search.getRows(), search.getPage(), page.getTotal(), rows);
    }

    @Override
    public List<BusinessHistoricDetailVo> getHistoricBusinessById(Long businessKey, String businessType) {
        List<BusinessHistoricDetailVo> list = new ArrayList<>();
        List<BusinessProcessingLog> logs = processingLogService.findCompletedByBusiness(businessKey, businessType);
        if (CollUtil.isNotEmpty(logs)) {
            for (BusinessProcessingLog log : logs) {
                Object obj = FlowableUtil.getFormBusinessDateById(log.getTaskDefKey(), log.getFormBusinessId());
                BusinessProcessingLogVo businessProcessingLogVo = transBusinessProcessingLog(log);
                list.add(new BusinessHistoricDetailVo(log.getTaskName(), obj, businessProcessingLogVo));
            }
        }
        return list;// 避免“返回结果为空”的错误提示信息
    }

    //将流程业务处理日志进行转换
    private BusinessProcessingLogVo transBusinessProcessingLog(BusinessProcessingLog businessProcessingLog) {
        BusinessProcessingLogVo vo = new BusinessProcessingLogVo();
        if (null != businessProcessingLog.getAssignee()) {
            SysUser assign = sysUserService.selectUserById(businessProcessingLog.getAssignee().getId());
            vo.setAssigneeName(assign != null ? assign.getUserName() : "");
        }
        vo.setAssigneeTypeName(FlowableConstantUtil.assigneeType.get(businessProcessingLog.getAssigneeType()));
        // 上午、下午开始时间换算
        String startTimeStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, businessProcessingLog.getStartTime());
        String endTimeStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, businessProcessingLog.getEndTime());
        vo.setStartTime(startTimeStr);
        vo.setEndTime(endTimeStr);
        if (StrUtil.isNotEmpty(businessProcessingLog.getStatus())) {
            vo.setStatus(businessProcessingLog.getStatus());
        }
        if (null != businessProcessingLog.getOverdueDays() && businessProcessingLog.getOverdueDays() > 0) {
            vo.setOverdue("逾期" + businessProcessingLog.getOverdueDays() + (businessProcessingLog.getWorkingDay() ? "个工作日" : "个自然日"));
        }
        return vo;
    }


    @Override
    public Boolean duplicateProcessingTask(String taskId) {
        Task task = flowableService.getByTaskQuery(taskId);
        if (task == null) {
            throw new ValidatorException(HttpStatus.ERROR, "操作失败，该业务可能已完成，请确认！");
        }

        if (StrUtil.isNotEmpty(task.getAssignee()) && !task.getAssignee().equals(SecurityUtils.getUserId().toString())) {
            throw new ValidatorException(HttpStatus.ERROR, "无权操作，该业务可能已经分配，请确认！");
        }
        return true;
    }

    @Override
    public Long terminate(String taskId, Long entityId, String businessType, String taskName, String taskDefKey) {
        BusinessProcessingLog processingLog = new BusinessProcessingLog();
        String procInstId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        if (processInstance != null) {
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
            if (bpmnModel == null) {
                throw new ServiceException("终止失败", HttpStatus.ERROR);
            }
            Process mainProcess = bpmnModel.getMainProcess();
            List<EndEvent> endNodes = mainProcess.findFlowElementsOfType(EndEvent.class);

            String endId = endNodes.get(0).getId();
            String processInstanceId = processInstance.getProcessInstanceId();
            //1、执行终止
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
            List<String> executionIds = executions.stream().map(Execution::getId).collect(Collectors.toList());
            runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            // 2.添加业务处理日志
            processingLog.setBusinessKey(entityId);
            processingLog.setBusinessType(businessType);
            processingLog.setHistoricTaskId(taskId);
            processingLog.setAssignee(sysUserService.selectUserById(SecurityUtils.getUserId()));
            processingLog.setAssigneeType(FlowableConstantUtil.PROCESS_TASK);
            processingLog.setTaskDefKey(taskDefKey);
            processingLog.setTaskName(taskName);
            processingLog.setStatus(FlowableConstantUtil.TERMINATE);
            processingLog.setFormBusinessId(entityId);
            processingLog.setStartTime(new Date());
            processingLog.setEndTime(new Date());
            processingLogService.add(processingLog);
        } else {
            throw new ServiceException("不存在运行的流程实例,请确认!", HttpStatus.ERROR);
        }
        return processingLog.getId();
    }
}
