package com.jsc.oscm.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jsc.oscm.dao.TaskRepository;
import com.jsc.oscm.entity.TargetWebsite;
import com.jsc.oscm.entity.Task;
import com.jsc.oscm.model.ReturnT;
import com.jsc.oscm.model.XxlJobInfo;
import com.jsc.oscm.util.HttpUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.*;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

@Service
public class TaskService {

    static final Logger logger = LogManager.getLogger(TaskService.class.getName());
    @Resource
    TaskRepository taskRepository;

    @Value(value = "${xxl.job.admin.server}")
    String xxlJobAdminServer;

    @Value(value = "${xxl.job.executor.handlerMulti}")
    String xxlJobExecutorHandlerMulti;

    @Value(value = "${xxl.job.executor.handlerSingle}")
    String xxlJobExecutorHandlerSingle;

    @Value(value = "${xxl.job.executor.groupid}")
    String xxlJobExecutorGroupId;

    @Value(value = "${crawl.website.project}")
    String websiteProject;

    @Value(value = "${crawl.website.spider}")
    String websiteSpider;

    @Value(value = "${systemflag}")
    String systemFlag;

    @Value("${crawl.website.project}")
    String websiteSpiderName;


    public Task addTask(Task task) {

        Task savedTask = taskRepository.save(task);
        //调用xxl-job接口生成相应的任务
        XxlJobInfo xxlJobInfo = buildXxlJobByTask(savedTask);

        String resultString = HttpUtil.post(xxlJobAdminServer + "xxl-job-admin/api/add", xxlJobInfo);
        if (resultString == null) {
            taskRepository.delete(savedTask);
            return null;
        }
        ReturnT<String> result = JSON.parseObject(resultString, ReturnT.class);
        if (result.code != 200) {
            taskRepository.delete(savedTask);
            return null;
        }
        savedTask.setXxlJobId(Integer.parseInt(result.content));
        savedTask = taskRepository.save(savedTask);

        Map<String, String> paramsMap = (Map<String, String>) JSON.parse(xxlJobInfo.getExecutorParam());
        paramsMap.put("xxljobInfoId", result.content);
        xxlJobInfo.setExecutorParam(JSON.toJSONString(paramsMap));
        xxlJobInfo.setId(Integer.parseInt(result.content));
        HttpUtil.post(xxlJobAdminServer + "xxl-job-admin/api/update", xxlJobInfo);
        return savedTask;
    }

    public boolean saveTaskDirect(Task task) {
        task.setCreateTime(new Date());
        task.setCron(task.getCron() == null || task.getCron().length() == 0 ? "0 0 0/6 * * ? *" : task.getCron()); //默认采集频率6小时一次
        return addTask(task) != null;
    }

    public boolean updateTaskDirect(Task task) {
        task.setCron(task.getCron() == null || task.getCron().length() == 0 ? "0 0 0/6 * * ? *" : task.getCron()); //默认采集频率6小时一次

        //先更新xxljob
        XxlJobInfo xxlJobInfo = buildXxlJobByTask(task);
        String resultString = HttpUtil.post(xxlJobAdminServer + "xxl-job-admin/api/update", xxlJobInfo);

        if (resultString == null) {
            return false;
        }
        ReturnT<String> result = JSON.parseObject(resultString, ReturnT.class);
        if (result.code != 200) {
            return false;
        }
        //更新task
        taskRepository.save(task);
        return true;
    }

    public void deleteByWebsiteIds(List<Integer> websiteIds) {
        for (Integer id : websiteIds) {
            List<Task> taskList = taskRepository.findAllByWebsiteId(id);
            taskList.forEach(task -> delTasks(Collections.singletonList(task.getId())));
        }
    }

    public void delTasks(List<Integer> ids) {
        List<Task> tasks = taskRepository.findAllById(ids);
        taskRepository.deleteAll(tasks);
        for (Task task : tasks) {
            delXxlJob(task.getXxlJobId().toString());
        }
    }

    public Map<String, String> getTaskStatus(List<Integer> taskIds) {
        Map<String, String> statusMap = new HashMap<>();
        List<Task> tasks = taskRepository.findAllById(taskIds);
        List<String> queriedProjects = new LinkedList<>();
        List<String> offlineServers = new LinkedList<>();
        for (Task crawlTask : tasks) {
            String project = crawlTask.getProject();
            String server = crawlTask.getServer();
            if (offlineServers.contains(server)) {
                statusMap.put(crawlTask.getId().toString(), "offline");
                continue;
            }
            if (queriedProjects.contains(server + project)) {
                statusMap.put(crawlTask.getId().toString(), "无采集引擎");
                continue;
            } else {
                queriedProjects.add(server + project);
            }
            String url = String.format("http://%s/listjobs.json?project=%s", server, project);
            String resultString = HttpUtil.get(url);
            if (resultString == null || "".equals(resultString)) {
                logger.error(String.format("查询任务状态失败,原因可能为服务%s不在线。", server));
                offlineServers.add(server);
                statusMap.put(crawlTask.getId().toString(), "offline");
                continue;
            }
            Map<String, Object> resultObj = JSON.parseObject(resultString);
            if (!"ok".equals(resultObj.get("status"))) {
                logger.error(String.format("查询%s上的project:%s的任务状态失败.", server, project));
                statusMap.put(crawlTask.getId().toString(), "无采集引擎");
                continue;
            }
            List<Map<String, String>> pendingTasks = (List<Map<String, String>>) resultObj.get("pending");
            for (Map<String, String> task : pendingTasks) {
                statusMap.put(task.get("id"), "pending");
            }

            List<Map<String, String>> runningTasks = (List<Map<String, String>>) resultObj.get("running");
            for (Map<String, String> task : runningTasks) {
                statusMap.put(task.get("id"), "running");
            }

            List<Map<String, String>> finishedTasks = (List<Map<String, String>>) resultObj.get("finished");
            for (Map<String, String> task : finishedTasks) {
                statusMap.put(task.get("id"), "finished");
            }
        }
        return statusMap;
    }

    public void startTaskByIds(List<Integer> taskIds) {
        List<Task> tasks = taskRepository.findAllById(taskIds);
        for (Task task : tasks) {
            Map<String, Object> postData = new HashMap<>();
            postData.put("id", task.getXxlJobId());
            HttpUtil.formRequest(xxlJobAdminServer + "xxl-job-admin/api/trigger", postData);
        }
    }

    public void startTaskByWebsiteIds(List<Integer> ids) {
        List<Task> tasks = taskRepository.findAllByWebsiteIdIn(ids);
        for (Task task : tasks) {
            Map<String, Object> postData = new HashMap<>();
            postData.put("id", task.getXxlJobId());
            HttpUtil.formRequest(xxlJobAdminServer + "xxl-job-admin/api/trigger", postData);
        }
    }

    private boolean delXxlJob(String id) {
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("id", id);
        String resultString = HttpUtil.post(xxlJobAdminServer + "xxl-job-admin/api/remove", paramsMap);
        if (resultString == null) {
            return false;
        }
        ReturnT<String> returnT = JSON.parseObject(resultString, ReturnT.class);
        return returnT.code == 200;
    }

    private XxlJobInfo buildXxlJobByTask(Task task) {
        XxlJobInfo xxlJobInfo = new XxlJobInfo();
        xxlJobInfo.setJobGroup(Integer.parseInt(xxlJobExecutorGroupId));
        xxlJobInfo.setJobCron(task.getCron());
        xxlJobInfo.setJobDesc(task.getName());
        xxlJobInfo.setAuthor("jsc");
        xxlJobInfo.setExecutorRouteStrategy("FIRST");
        if (task.getTaskType() == null || task.getTaskType() == Task.MULTI_TASK) {
            xxlJobInfo.setExecutorHandler(xxlJobExecutorHandlerMulti);
        } else {
            xxlJobInfo.setExecutorHandler(xxlJobExecutorHandlerSingle);
        }

        Map<String, String> paramsMap = new HashMap<>();
        //任务调度可能会使用的参数
        paramsMap.put("name", task.getName());
        if (task.getServer() != null && task.getServer().length() > 0) {
            paramsMap.put("host", task.getServer().split(":")[0]);
            paramsMap.put("port", task.getServer().split(":")[1]);
        }
        paramsMap.put("project", task.getProject());
        paramsMap.put("spider", task.getSpider());
        paramsMap.put("taskid", task.getId().toString());
        paramsMap.put("xxlgroupid", xxlJobExecutorGroupId);
        if (task.getXxlJobId() != null) {
            paramsMap.put("xxljobInfoId", task.getXxlJobId().toString());
        }

        if (task.getWebsiteId() != null) {
            paramsMap.put("webSiteId", task.getWebsiteId().toString());
        }
        if (task.getNodeIds() != null) {
            paramsMap.put("nodeIds", task.getNodeIds());
        }
        paramsMap.put("rediskey", "myspider_common:start_urls");
        paramsMap.put("systemflag", systemFlag);
        paramsMap.put("clusterName", task.getClusterName());
        xxlJobInfo.setExecutorParam(JSON.toJSONString(paramsMap));
        xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");
        xxlJobInfo.setExecutorTimeout(0);
        xxlJobInfo.setExecutorFailRetryCount(0);
        xxlJobInfo.setGlueType("BEAN");
        xxlJobInfo.setGlueRemark("GLUE");
        xxlJobInfo.setTriggerStatus(Integer.parseInt(task.getEnableFlag()));
        xxlJobInfo.setTriggerLastTime(0);
        xxlJobInfo.setTriggerNextTime(0);
        if (task.getXxlJobId() != null) {
            xxlJobInfo.setId(task.getXxlJobId());
        }
        xxlJobInfo.setUpdateTime(new Date());
        return xxlJobInfo;
    }

    public boolean saveCrawlTaskByWebsite(TargetWebsite targetWebsite) {
        Task task = new Task();
        task.setWebsiteId(targetWebsite.getId());
        task.setCarrierType("0");
        task.setCreateTime(new Date());
        task.setCron("0 0 0/6 * * ?");
        task.setEnableFlag("0");
        task.setProject(websiteProject);
        task.setTaskType(Task.SINGLE_TASK);
        task.setSpider(websiteSpider);
        task.setName(String.format("%s的采集任务", targetWebsite.getSiteName()));
        task = addTask(task);
        if (task != null) {
            return true;
        } else {
            logger.error("保存采集任务失败");
            return false;
        }
    }

    public List<Task> queryAllTaskBySite(Integer websiteId) {
        return taskRepository.findAllByWebsiteId(websiteId);
    }

    public Task findTaskById(int id) {
        Optional<Task> crawlTask = taskRepository.findById(id);
        return crawlTask.orElse(null);
    }

    public List<Task> queryTasksByType(Integer tasktype) {
        Task task = new Task();
        task.setTaskType(tasktype);
        Example<Task> example = Example.of(task);
        return taskRepository.findAll(example);
    }

    public List<Task> queryTasks(String params) {
        JSONObject paramObj = JSON.parseObject(params);
        Task task = new Task();
        task.setTaskType(paramObj.getInteger("taskType"));
        task.setProject(paramObj.getString("project"));
        task.setSystemFlag(paramObj.getString("systemFlag"));
        Example<Task> example = Example.of(task);
        return taskRepository.findAll(example);
    }

    public List<Task> getTaskByIds(List<Integer> ids) {
        return taskRepository.findAllById(ids);
    }

    public List<Task> queryPageable(Task task, Integer index, Integer size) {
        Example<Task> example = Example.of(task);
        Pageable pageable = PageRequest.of(index, size, Sort.by(Sort.Order.desc("id")));
        if (task.getName() != null) {
            ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());
            example = Example.of(task, matcher);
        }
        return taskRepository.findAll(example, pageable).getContent();
    }

    public Long queryCount(Task task) {
        Example<Task> example = Example.of(task);
        if (task.getName() != null) {
            ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());
            example = Example.of(task, matcher);
        }
        return taskRepository.count(example);
    }
}
