package com.xxl.job.executor.service.jobhandler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.log.XxlJobFileAppender;
import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.executor.util.HttpUtil;
import com.xxl.job.executor.util.JobWaitingQueueThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * XxlJob开发示例（Bean模式）
 * <p>
 * 开发步骤：
 * 1、在Spring Bean实例中，开发Job方法，方式格式要求为 "public ReturnT<String> execute(String param)"
 * 2、为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")"，注解value值对应的是调度中心新建任务的JobHandler属性的值。
 * 3、执行日志：需要通过 "XxlJobLogger.log" 打印执行日志；
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Component
public class SampleXxlJob {
    private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Value(value = "${oscm.server.url}")
    private String oscmServer;

    @Value(value = "${xxl.job.admin.addresses}")
    private String xxlAdminServer;

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

    @Resource
    JedisPool jedisPool;

    @Resource
    JobWaitingQueueThread jobWaitingQueueThread;

    /**
     * 采集引擎触发handler
     * param:{
     * "host":"",
     * "port":"",
     * "project":"",
     * "spider":"",
     * "setting":"",
     * "jobid":"", 采集管理平台中的任务id
     * "inkeyword":"",
     * "exkeyword":""
     * }
     * 执行其需回调采集管理平台接口，更新采集任务中的scrapyd_task_id字段
     */
    @XxlJob("demoJobHandler")
    public ReturnT<String> demoJobHandler(String param) throws Exception {
        XxlJobLogger.log("demoJobHandler");
        JSONObject paramsMap = JSON.parseObject(param);
        Integer nodeId = paramsMap.getInteger("nodeIds");
        //1、中止当前project正在运行的job,或取消该次调度

        //2、调用xxl-admin接口，获取xxl-job的执行记录id
        String xxlGroupId = paramsMap.getString("xxlgroupid");
        String xxlJobInfoId = paramsMap.getString("xxljobInfoId");
        Thread.sleep(2000);
        JSONObject xxlLog = JSON.parseObject(HttpUtil.get(String.format("%s/api/joblog/%s/%s", xxlAdminServer, xxlGroupId, xxlJobInfoId)));
        assert xxlLog != null;
        String xxlLogId = xxlLog.getString("id");
        paramsMap.put("job_id", xxlLogId);
        //3、触发新的job
        String scheduleUrl = String.format("http://%s:%s/schedule.json", paramsMap.getString("host"), paramsMap.getString("port"));
        Map<String, String> postData = new HashMap<>();
        postData.put("project", paramsMap.get("project").toString());
        postData.put("spider", paramsMap.get("spider").toString());
        postData.put("params", JSON.toJSONString(paramsMap));
        String scheduleResult = HttpUtil.formPost(scheduleUrl, postData);
        XxlJobLogger.log("scheduleResult:" + scheduleResult);
        //4、回调采集管理平台接口，创建执行记录,从scheduleResult中可解析出scrapyd任务id
        JSONObject scheduleResultMap = JSON.parseObject(scheduleResult);
        if (scheduleResultMap != null && (scheduleResultMap.getString("status") == null || !scheduleResultMap.getString("status").equals("ok")))
            return new ReturnT<>(ReturnT.FAIL_CODE, "scrapyd调度失败！");
        assert scheduleResultMap != null;
        String scrapydJobId = scheduleResultMap.getString("jobid");
        //5、调用oscm接口，创建执行记录
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String oscmTaskId = paramsMap.getString("taskid");
        Map<String, Object> oscmLogMap = new HashMap<>();
        oscmLogMap.put("jobId", xxlLogId);
        oscmLogMap.put("taskId", oscmTaskId);
        oscmLogMap.put("createTime", simpleDateFormat.format(new Date()));
        oscmLogMap.put("startTime", simpleDateFormat.format(new Date()));
        oscmLogMap.put("scrapydId", scrapydJobId);
        oscmLogMap.put("status", "0");
        oscmLogMap.put("pagesCount", "0");
        oscmLogMap.put("itemsCount", "0");
        oscmLogMap.put("filesCount", "0");
        oscmLogMap.put("downloadBytes", "0");
        oscmLogMap.put("nodeId", nodeId);
        if (scrapydJobId == null) {
            //调度失败
            oscmLogMap.put("finishTime", simpleDateFormat.format(new Date()));
            oscmLogMap.put("status", "2");
            oscmLogMap.put("result", "0");
            HttpUtil.post(String.format("%s/api/open/crawljob", oscmServer), oscmLogMap);
            return ReturnT.FAIL;
        }

        XxlJobLogger.log("创建执行记录");
        String temp = HttpUtil.post(String.format("%s/api/open/crawljob", oscmServer), oscmLogMap);
        XxlJobLogger.log(temp);
        return ReturnT.SUCCESS;
    }


    //单站点采集任务
    @XxlJob("scheduleHandler")
    public ReturnT<String> scheduleHandler(String params) throws Exception {
        JSONObject paramsMap = JSON.parseObject(params);
        Integer websiteId = paramsMap.getInteger("webSiteId");
        //1、获取站点
        String uString = String.format("%s/api/open/target/website/%d", oscmServer, websiteId);
        String response = HttpUtil.get(uString);
        JSONObject result = JSON.parseObject(response);
        if (result == null || result.getInteger("code") != ReturnT.SUCCESS_CODE) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "获取站点失败！");
        }
        JSONObject website = result.getJSONObject("content");
        URL url = null;
        if (website != null) {
            url = new URL(website.getString("startUrl"));
        }
        Jedis jedis = jedisPool.getResource();
        if (ObjectUtils.isEmpty(url)) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "站点URL为空！");
        }
        String clusterName = url.getHost() + "_" + UUID.randomUUID() + "_" + website.getString("id");
        String redisKey = String.format("WebSite_%s:start_urls", clusterName);
        jedis.zadd(redisKey, website.getInteger("weight"), website.getString("startUrl")); //添加url到带有权重的集合
        //3、获取xxl joblog
        String xxlGroupId = paramsMap.getString("xxlgroupid");
        String xxlJobInfoId = paramsMap.getString("xxljobInfoId");
        JSONObject xxlLog = JSON.parseObject(HttpUtil.get(String.format("%s/api/joblog/%s/%s", xxlAdminServer, xxlGroupId, xxlJobInfoId)));
        assert xxlLog != null;
        String xxlLogId = xxlLog.getString("id");
        paramsMap.put("job_id", xxlLogId);
        paramsMap.put("clusterName", clusterName);
        paramsMap.put("siteUrl", website.getString("startUrl"));
        paramsMap.put("logFileName", XxlJobFileAppender.contextHolder.get());
        //4、构造采集任务并添加进采集队列
        Map<String, Object> CrawlTaskInfo = new HashMap<>();
        CrawlTaskInfo.put("paramsMap", paramsMap);
        CrawlTaskInfo.put("url", website.getString("startUrl"));
        CrawlTaskInfo.put("oscmServer", oscmServer);
        CrawlTaskInfo.put("xxlAdminServer", xxlAdminServer);
        jobWaitingQueueThread.addToWaitingQueue(CrawlTaskInfo);
        jedis.close();
        return ReturnT.SUCCESS;
    }


    public void init() {
        logger.info("init");
    }

    public void destroy() {
        logger.info("destroy");
    }

}
