package cn.cslg.pas.service;


import cn.cslg.pas.common.JsonPage;
import cn.cslg.pas.common.model.dto.QueryQrtzTaskLogsDTO;
import cn.cslg.pas.common.model.dto.TaskAddNewDTO;

import cn.cslg.pas.common.model.dto.QueryTaskDTO;
import cn.cslg.pas.common.model.vo.*;
import cn.cslg.pas.common.utils.*;
import cn.cslg.pas.common.utils.SecurityUtils.LoginUtils;

import cn.cslg.pas.domain.Personnel;
import cn.cslg.pas.domain.Task;
import cn.cslg.pas.domain.asso.TaskCondition;
import cn.cslg.pas.mapper.TaskMapper;
import cn.cslg.pas.common.model.dto.UploadFileDTO;
import cn.cslg.pas.service.asso.TaskConditionService;
import cn.cslg.pas.service.upLoadPatent.*;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 任务表 服务类
 * </p>
 *
 * @author 王岩
 * @since 2022-02-27
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class TaskService extends ServiceImpl<TaskMapper, Task> implements ITaskService {
    private final FileUtils fileUtils;
    private final LoginUtils loginUtils;
    private final RequestService requestService;
    private final OutInterfaceService outInterfaceService;
    private final PantentQueueService pantentQueueService;
    private final ExcutePatentDataExcel excutePatentDataExcel;
    private final ExcutePatentDataEpo excutePatentDataEpo;
    private final ExcutePatentDataStar excutePatentDataStar;
    private final TaskConditionService taskConditionService;
    private final JobService jobService;
    private final TaskMapper taskMapper;


    public String getFileUrl(Integer id) {
        Task task = this.getById(id);
        return fileUtils.getSystemPath() + task.getUrl();
    }

    public Integer add(UploadFileDTO fileDTO, Integer projectId, Integer productId, Integer total, Integer type, Integer fieldNum, String oldName) {
        //创建任务表实体类，准备装载数据 ↓
        Task task = new Task();

        //任务开始时间
        task.setStartTime(DateUtils.getDateTime());
        //任务状态（0.队列中 1.进行中 2.成功 3.失败）
        task.setStatus(0);
        if (projectId != null) {
            //专题库id
            task.setProjectId(projectId);
        }
        if (productId != null) {
            //产品id
            task.setProductId(productId);
        }
        //文件名称
        task.setFileName(fileDTO.getFileName());
        //文件路径
        task.setUrl(fileDTO.getPath());
        //文件的专利总数量
        task.setTotal(total);
        //文件大小
        task.setFileSize(fileDTO.getFileSize());
        //任务类型 （1.上传 2导出）
        task.setType(type);
        //导入导出字段数量
        task.setFieldNum(fieldNum);
        //创建人id
        task.setCreateBy(loginUtils.getId());
        //文件原始名称
        task.setOldName(oldName);

        //数据入任务表
        task.insert();
        return task.getId();
    }

    /**
     * 新增导入专利任务
     *
     * @param fileDTO Excel文件
     * @param json    前台参数
     * @return 返回任务id
     */
    public Integer addTask2(UploadFileDTO fileDTO, Integer total, String json) {
        //将前台参数json格式转换为实体类（从中取出专题库id存入任务表）
        ProjectImportPatentVO projectImportPatentVO = JsonUtils.jsonToPojo(json, ProjectImportPatentVO.class);

        //创建任务表实体类，准备装载数据 ↓
        Task task = new Task();

        //任务开始时间
        //task.setStartTime(DateUtils.getDateTime());
        //任务状态（0.队列中 1.进行中 2.成功 3.失败）
        task.setStatus(0);
        if (projectImportPatentVO.getProjectId() != null && projectImportPatentVO.getReportId() == null) {
            //若是和专题库关联
            task.setProjectId(projectImportPatentVO.getProjectId());
        } else if (projectImportPatentVO.getProjectId() == null && projectImportPatentVO.getReportId() != null) {
            //若是和报告关联
            task.setReportId(projectImportPatentVO.getReportId());
        }
        //文件名称
        task.setFileName(fileDTO.getFileName());
        //文件路径
        task.setUrl(fileDTO.getPath());
        //文件的专利总数量
        task.setTotal(total);
        //文件大小
        task.setFileSize(fileDTO.getFileSize());
        //任务类型 （1.上传 2导出）
        task.setType(1);
        //导入导出字段数量
        task.setFieldNum(0);
        //创建人id
        task.setCreateBy(loginUtils.getId());
        //文件原始名称
        task.setOldName(fileDTO.getName());
        //成功条数
        task.setSuccessNum(0);
        //失败条数
        task.setDefaultNum(0);
        //前台参数json格式
        task.setPramJson(json);
        //任务类型1（0普通任务 1定时任务）
        task.setTaskType(0);

        //数据入任务表
        task.insert();
        return task.getId();
    }

    /**
     * 新增网站导入专利任务
     *
     * @param taskAddNewDTO 前台参数
     * @return 返回任务id
     */
    public Integer addTask3(TaskAddNewDTO taskAddNewDTO, Integer total, TaskCondition taskCondition) {
        //创建任务表实体类，准备装载数据 ↓
        Task task = new Task();

        //任务开始时间
        //task.setStartTime(DateUtils.getDateTime());
        //任务状态（0.队列中 1.进行中 2.成功 3.失败）
        task.setStatus(0);
        //判断当前导入专利任务是与专题库关联还是与报告关联
        if (taskAddNewDTO.getProjectId() != null && taskAddNewDTO.getReportId() == null) {
            //专题库id
            task.setProjectId(taskAddNewDTO.getProjectId());
        } else if (taskAddNewDTO.getProjectId() == null && taskAddNewDTO.getReportId() != null) {
            //报告id
            task.setReportId(taskAddNewDTO.getReportId());
        }
        //专利总数量
        task.setTotal(total);
        //任务类型 （1Excel导入 2导出 3欧专局导入 4专利之星导入）
        task.setType(taskAddNewDTO.getTaskType2());
        //导入导出字段数量
        task.setFieldNum(0);
        //创建人id
        task.setCreateBy(loginUtils.getId());
        //成功条数
        task.setSuccessNum(0);
        //失败条数
        task.setDefaultNum(0);
        //所属任务条件id
        task.setTaskConditionId(taskCondition.getId());
        //与专题库关联信息（自定义字段、文件夹）
        ProjectImportPatentVO projectImportPatentVO = new ProjectImportPatentVO();
        BeanUtils.copyProperties(taskAddNewDTO, projectImportPatentVO);
        String pramJson = JSONObject.toJSONString(projectImportPatentVO);
        task.setPramJson(pramJson);
        //任务类型1（0普通任务 1定时任务）
        task.setTaskType(taskAddNewDTO.getTaskType());

        //数据入任务表
        log.info("任务表新增数据");
        task.insert();
        return task.getId();
    }

    public Task edit(Integer id, Integer status) {
        Task task = this.getById(id);
        task.setStatus(status);
        task.setEndTime(DateUtils.getDateTime());
        task.updateById();
        return task;
    }

    public String delete(Integer id) {
        Task temp = this.getById(id);
        this.removeById(id);
        if (StringUtils.isNotEmpty(temp.getUrl())) {
            FileUtil.del(fileUtils.getSystemPath(temp.getUrl()));
        }
        return Response.success(true);
    }

    public void deleteTask(Integer taskId) {
        log.info("开始处理【删除任务】的业务，参数为：id={}", taskId);

        //检查任务是否存在，若不存在则提示用户"删除失败，当前任务已不存在，请尝试刷新页面"
        Task task = this.getById(taskId);
        if (task == null) {
            ThrowException.throwXiaoShiException("删除失败，当前任务已不存在，请尝试刷新页面");
        }

        //检查任务状态
        //若是已完成或已暂停，则直接删除
        if (task.getStatus().equals(2) || task.getStatus().equals(4)) {
            this.removeById(task);
            //若是网站导入任务，则还要删除任务条件表对应数据
            if (task.getType().equals(3) || task.getType().equals(4)) {
                taskConditionService.removeById(task.getTaskConditionId());
            }
        }

        //若是等待中，则先要将生产者队列中的该任务剔除，再删除

        //若是进行中，则不允许删除，并提示用户"当前任务正在进行中，无法删除"


    }

    public IPage<Task> getPageList(TaskVO params) throws IOException {
        String createName = params.getCreateName();
        //如果此次查询是要根据创建人名称查询则👇
        if (createName != null && !createName.equals("")) {
            //远程调用权限系统的根据创建人名称模糊查询创建人ids的接口
            String res = outInterfaceService.getPersonIdByNamePCS(createName);
            List<Integer> createPersonIds = JSONArray.parseArray(res, Integer.class);
            //当未查询到时给创建人ids集合赋值唯一元素id=-1，即查空页返回
            if (createPersonIds == null || createPersonIds.size() == 0) {
                createPersonIds = new ArrayList<>(Collections.singletonList(-1));
            }
            params.setCreatePersonIds(createPersonIds);
        }
        IPage<Task> pageList = baseMapper.getPageList(new Page<>(params.getCurrent(), params.getSize()), params);
        this.setDataList(pageList.getRecords());
        return pageList;
    }


    public PageVO queryTasks(QueryTaskDTO queryTaskDTO) throws IOException {
        log.info("开始处理【查询任务】的业务，参数为：{}", queryTaskDTO);

        //专题库id和报告id都没有时，根据当前登陆人id查询
        if (queryTaskDTO.getProjectId() == null && queryTaskDTO.getReportId() == null) {
            queryTaskDTO.setCreateId(loginUtils.getId());
        }
        //根据创建人名称模糊查询
        if (queryTaskDTO.getCreateName() != null && !queryTaskDTO.getCreateName().equals("")) {
            String res = outInterfaceService.getPersonIdByNamePCS(queryTaskDTO.getCreateName());
            List<Integer> personIds = JSON.parseArray(res, Integer.class);
            if (personIds.size() == 0) {  //若根据该名称查询不到任何人员，则将人员ids集合添加一个元素-1（防止sql报错）
                personIds.add(-1);
            }
            queryTaskDTO.setPersonIds(personIds);
        }
        //若有排序，则根据创建时间排
        if (queryTaskDTO.getOrderBy() != null) {
            queryTaskDTO.setOrderBy("create_time");
        }
        //若要分页
        if (queryTaskDTO.getCurrent() != null) {
            PageHelper.startPage(queryTaskDTO.getCurrent(), queryTaskDTO.getSize());
        }
        List<QueryTaskVO> tasks = taskMapper.queryTasks(queryTaskDTO);
        PageInfo<QueryTaskVO> pageInfo = new PageInfo<>(tasks);

        //装载创建人名称、装载任务名称
        if (tasks.size() > 0) {
            List<Integer> createPersonIds = tasks.stream().map(QueryTaskVO::getCreateBy).collect(Collectors.toList());
            String res = outInterfaceService.getPersonnelByIdsFromPCS(createPersonIds);
            com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(res);
            List<Personnel> personnels = com.alibaba.fastjson.JSONObject.parseArray(jsonObject.getString("data"), Personnel.class);
            //遍历装载创建人名称
            for (QueryTaskVO task : tasks) {
                for (Personnel personnel : personnels) {
                    if (personnel.getId().equals(task.getCreateBy())) {
                        task.setCreateName(personnel.getPersonnelName());
                        break;
                    }
                }
            }
            //遍历装载任务名称（检索式+创建时间+创建人名称）
            for (QueryTaskVO task : tasks) {
                if (task.getType().equals(3) || task.getType().equals(4)) {
                    String conditions = task.getConditions();
                    conditions = conditions.substring(conditions.indexOf("(") + 1, conditions.lastIndexOf(")"));
                    String createTime = new SimpleDateFormat("yyyyMMdd").format(task.getCreateTime());
                    String taskName = createTime + " - 检索式:" + conditions + " - " + task.getCreateName();
                    task.setTaskName(taskName);
                }
            }
        }

        //手动创建分页信息返回对象
        PageVO pageVO = new PageVO()
                .setCurrent(pageInfo.getPageNum())
                .setSize(pageInfo.getPageSize())
                .setTotal((int) pageInfo.getTotal())
                .setRecords(tasks);
        return pageVO;
    }

    public Page<Task> queryQrtzTaskLogs(QueryQrtzTaskLogsDTO queryQrtzTaskLogsDTO) {
        log.info("开始处理【查询定时任务执行日志】的业务，参数为：taskId={}", queryQrtzTaskLogsDTO.getTaskId());

        //检查任务是否存在，若不存在则提示用户"该定时任务已不存在，请尝试刷新页面"
        Task task = this.getById(queryQrtzTaskLogsDTO.getTaskId());
        if (task == null) {
            ThrowException.throwXiaoShiException("该定时任务已不存在，请尝试刷新页面");
        }

        //检查任务是否为定时任务，若不是则提示用户"该任务不为定时任务，暂无更新日志"
        TaskCondition taskCondition = taskConditionService.getById(task.getTaskConditionId());
        if (!taskCondition.getTaskType().equals(1)) {
            ThrowException.throwXiaoShiException("该任务不为定时任务，暂无更新日志");
        }

        Page<Task> pageList = this.page(new Page<>(queryQrtzTaskLogsDTO.getCurrent(), queryQrtzTaskLogsDTO.getSize()), new LambdaQueryWrapper<Task>().eq(Task::getTaskConditionId, task.getTaskConditionId()).orderByDesc(Task::getCreateTime));

        return pageList;
    }

    public void cancelTask(Integer taskId) throws SchedulerException, InterruptedException {
        log.info("开始处理【取消任务】的业务，参数为：taskId={}", taskId);

        //检查任务是否存在，若不存在则提示用户"取消任务失败，当前任务已不存在，请尝试刷新页面"
        Task task = this.getById(taskId);
        if (task == null) {
            ThrowException.throwXiaoShiException("取消任务失败，当前任务已不存在，请尝试刷新页面");
            return;
        }

        //检查任务状态是否为已完成（普通任务），若是则提示用户"取消任务失败，已完成的任务无法取消"
        if (task.getStatus().equals(2) && task.getTaskType().equals(0)) {
            ThrowException.throwXiaoShiException("取消任务失败，已完成的任务无法取消");
        }

        //若是定时任务（即本次请求目的是取消该定时任务），删除job中的该定时任务条件
        if (task.getTaskType().equals(1)) {
            //删除job中的该定时任务条件
            jobService.deleteJob(task.getTaskConditionId());
        }

        //若该任务处于进行中
        if (task.getStatus().equals(1)) {
            //将生产者生产专利中的公共任务状态改为暂停
            if (task.getType() == 1) {
                excutePatentDataExcel.setTaskStatus(taskId, 4);
            } else if (task.getType() == 3) {
                excutePatentDataEpo.setTaskStatus(taskId, 4);
            } else if (task.getType() == 4) {
                excutePatentDataStar.setTaskStatus(taskId, 4);
            }
            if (task.getType().equals(3) || task.getType().equals(4)) {
                Thread.sleep(500);
                //将专利各部分装载线程队列中该任务的专利清空
                pantentQueueService.setQueueRemoveTasks(taskId);
            }
            Thread.sleep(1000);
            //将消费者的专利队列中所有该任务的专利剔除
            pantentQueueService.consumerQueueRemoveTasks(taskId);
        }

        //若该任务处于等待中
        if (task.getStatus().equals(0)) {
            //将生产者任务队列中该任务剔除
            pantentQueueService.taskQueueListRemoveTask(Arrays.asList(task.getId()));
        }

        //最后修改任务状态为5已取消
        Task updateTask = new Task();
        updateTask.setId(task.getId());
        updateTask.setStatus(5);
        log.info("os_task表修改任务状态为取消");
        this.updateById(updateTask);

    }

    public List<Task> getQueueList(Integer type, Integer projectId, Integer productId) {
        LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Task::getCreateBy, loginUtils.getId());
        queryWrapper.eq(Task::getType, type);
        queryWrapper.in(Task::getStatus, new ArrayList<Integer>() {{
            add(0);
            add(1);
        }});
        if (projectId != null) {
            queryWrapper.eq(Task::getProjectId, projectId);
        }
        if (productId != null) {
            queryWrapper.eq(Task::getProductId, productId);
        }
        queryWrapper.orderByAsc(Task::getStartTime);
        return this.list(queryWrapper);
    }

    public Integer add2(Integer projectId, Long fieldNum, Integer total) {
        Task task = new Task();
        task.setStatus(0);
        task.setProjectId(projectId);
        task.setType(2);
        task.setTotal(total);
        task.setFieldNum(fieldNum.intValue());
        task.setCreateBy(loginUtils.getId());
        task.setStartTime(DateUtils.getDateTime());
        task.insert();
        return task.getId();
    }

    public void updateStatus(Integer id, Integer status, Integer endTime) {
        Task task = this.getById(id);
        task.setStatus(status);
        if (endTime != null) {
            task.setEndTime(endTime);
        }
        task.updateById();
    }

    public void updateTime(Integer id, Integer status, Integer endTime, String url, String fileName) {
        Task task = this.getById(id);
        task.setStatus(status);
        task.setEndTime(endTime);
        task.setFileName(fileName);
        task.setUrl(url);
        task.updateById();
    }

    private void setDataList(List<Task> taskList) throws IOException {
        //获得创建人的id集合
        List<Integer> createIds = taskList.stream().map(Task::getCreateBy).collect(Collectors.toList());
        //获取专题库负责人对应信息
        String jsonObject1 = requestService.getPersonnelFromPCS(createIds);
        JSONArray jsonArray = JSON.parseArray(jsonObject1);
        List<ProjectVO.Personnel> personnelList = jsonArray.toJavaList(ProjectVO.Personnel.class);
        for (Task task : taskList) {
            for (ProjectVO.Personnel personnel : personnelList) {
                //装载创建人名
                if (task.getCreateBy() != null) {
                    if (task.getCreateBy().equals(personnel.getId())) {
                        task.setCreateName(personnel.getPersonnelName());
                    }
                }

            }
        }
    }

    /**
     * 暂停任务
     *
     * @param taskId 任务id
     * @return
     */
    @Override
    public Integer pauseTask(Integer taskId) throws InterruptedException {
        log.info("开始处理【暂停任务】的业务，参数为：taskId={}", taskId);

        //检查任务是否存在，若不存在则提示用户 "暂停失败，当前任务已不存在，请尝试刷新页面"
        log.info("检查尝试暂停的任务是否存在");
        Task task = this.getById(taskId);
        if (task == null) {
            ThrowException.throwXiaoShiException("暂停失败，当前任务已不存在，请尝试刷新页面");
            return null;
        }

        //检查任务是否处于进行中状态，若不是则提示用户 "暂停失败，当前任务不处于进行中状态，请尝试刷新页面"
        if (!task.getStatus().equals(1)) {
            ThrowException.throwXiaoShiException("暂停失败，当前任务未进行，请尝试刷新页面");
        }

        //任务表将任务状态修改为4暂停
        Task updateTask = new Task();
        updateTask.setId(taskId);
        updateTask.setStatus(4);
        log.info("任务表修改数据，暂停任务，任务id={}", taskId);
        boolean updateResult = this.updateById(updateTask);
        if (!updateResult) {
            ThrowException.throwXiaoShiException("暂停失败，服务器忙请稍后再试");
        }

        //将生产者生产专利中的公共任务状态改为暂停
        if (task.getType() == 1) {
            excutePatentDataExcel.setTaskStatus(taskId, 4);
        } else if (task.getType() == 3) {
            excutePatentDataEpo.setTaskStatus(taskId, 4);
        } else if (task.getType() == 4) {
            excutePatentDataStar.setTaskStatus(taskId, 4);
        }

        //若是网站导入任务，则还要将专利各部分装载线程队列中该任务的专利清空
        if (task.getType().equals(3) || task.getType().equals(4)) {
            //等上半秒（上述代码虽然使得生产暂停，但是生产暂停前正在存入生产队列的过程中，等2秒使得这些放入消费者队列后再清空消费者）
            Thread.sleep(500);
            pantentQueueService.setQueueRemoveTasks(taskId);
        }

        //等上一秒（上述代码虽然使得生产暂停，但是生产暂停前已生产的专利正在存入消费者队列的过程中，等2秒使得这些放入消费者队列后再清空消费者）
        Thread.sleep(1000);
        //将消费者的专利队列中所有该任务的专利剔除
        pantentQueueService.consumerQueueRemoveTasks(taskId);

        log.info("暂停任务成功");
        return updateTask.getStatus();

    }

    /**
     * 继续任务
     *
     * @param taskId 任务id
     */
    @Override
    public Integer continueTask(Integer taskId) {
        log.info("开始处理【继续任务】的业务，参数为：taskId={}", taskId);

        //检查任务是否存在，若不存在则提示用户 "继续任务失败，当前任务已不存在，请尝试刷新页面"
        log.info("检查尝试继续的任务是否存在");
        Task task = this.getById(taskId);
        if (task == null) {
            ThrowException.throwXiaoShiException("继续任务失败，当前任务已不存在，请尝试刷新页面");
            return null;
        }

        //检查任务是否处于暂停状态，若不是则提示用户 "继续任务失败，当前任务未暂停，请尝试刷新页面"
        if (!task.getStatus().equals(4)) {
            ThrowException.throwXiaoShiException("继续任务失败，当前任务未暂停，请尝试刷新页面");
        }

        //继续任务（将任务状态改为0即等待中）
        Task updateTask = new Task();
        updateTask.setId(taskId);
        updateTask.setStatus(0);
        log.info("任务表修改数据，继续任务，任务id={}", taskId);
        boolean updateResult = this.updateById(updateTask);
        if (!updateResult) {
            ThrowException.throwXiaoShiException("继续任务失败，服务器忙请稍后再试");
        }

        //将当前任务添加进生产者任务队列进行排队，并唤醒生产者线程
        pantentQueueService.taskQueueListAddTask(new ArrayList<>(Arrays.asList(taskId)));

        //将生产者生产专利类中的公共任务状态从暂停改为0
        if (task.getType().equals(1) && excutePatentDataExcel.getpTaskId().equals(task.getId())) {
            excutePatentDataExcel.setTaskStatus(taskId, 0);
        } else if (task.getType().equals(3) && excutePatentDataEpo.getpTaskId().equals(task.getId())) {
            excutePatentDataEpo.setTaskStatus(taskId, 0);
        } else if (task.getType().equals(4) && excutePatentDataStar.getpTaskId().equals(task.getId())) {
            excutePatentDataStar.setTaskStatus(taskId, 0);
        }

        pantentQueueService.awakeTasktch();

        log.info("继续任务成功");

        return updateTask.getStatus();
    }


}
