package com.base.task.cdlatch.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.base.orbit.utils.DateUtils;
import com.base.orbit.utils.IdGenerator;
import com.base.task.TaskInitialize;
import com.base.task.core.TaskCache;
import com.base.task.handler.BizTaskStepStateNotice;
import com.base.task.model.BaseTaskStateNotice;
import com.base.task.model.Enums.TaskTypeEnum;
import com.base.task.model.Task;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class CountDownLatchAspect {
    @Resource
    TaskInitialize taskInitialize;
    @Resource
    BizTaskStepStateNotice notice;

    @Pointcut("@annotation(com.base.task.cdlatch.aop.Countdlatch)")
    public void countDownLatch() {
    }

    CountDownLatch countDownLatch = null;

    @Around(value = "countDownLatch()")
    public void aroundEph(ProceedingJoinPoint joinPoint) throws InterruptedException {
        if (countDownLatch!=null&&countDownLatch.getCount()!=0){
            countDownLatch.await();
        }
        log.info("===================环绕通知开始================");
        Object[] returnobj = null;

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Countdlatch countdlatch = method.getAnnotation(Countdlatch.class);
        try {
            TaskTypeEnum type = countdlatch.value();
            returnobj = getObjects(joinPoint, type);
            if (!StringUtils.isEmpty(countdlatch.step())){
                BaseTaskStateNotice baseTaskStateNotice = notice.getBaseTaskStateNotice(returnobj[0],0);
                notice.notice(baseTaskStateNotice);
            }
            //执行目标方法
            Object o1 = joinPoint.proceed(returnobj);
            List<Task> returntasks = castList(o1, Task.class);
            if (!CollectionUtils.isEmpty(returntasks)) {
                log.info("------------"+type+"计算入库执行开始------------");
                long start = System.currentTimeMillis();
                countDownLatch = new CountDownLatch(returntasks.size());
                for (Task task : returntasks) {
                    task.setCountDownLatch(countDownLatch);
                    TaskCache.getInstance().addTask(task);
                }
                //等到所有线程执行完毕
                countDownLatch.await(1800, TimeUnit.SECONDS);
                long end = System.currentTimeMillis();
                log.info("------------"+type+"计算入库执行完毕------------");
                log.info(type+"执行总时长为" + (end - start) / 1000 + "秒");
                //检查任务完成情况
                if (countDownLatch.getCount()<=0){
                    log.info("------------"+type+"计算入库执行完毕------------");
                    log.info(type+"执行总时长为" + (end - start) / 1000 + "秒");
                  /*  if (!StringUtils.isEmpty(countdlatch.step())){
                        BaseTaskStateNotice baseTaskStateNotice = notice.getBaseTaskStateNotice(returnobj[0],1);
                        notice.notice(baseTaskStateNotice);
                    }*/
                }else {
                    log.info("任务超时");
                    //超时通知
                    if (!StringUtils.isEmpty(countdlatch.step())){
                        BaseTaskStateNotice baseTaskStateNotice = notice.getBaseTaskStateNotice(returnobj[0],2);
                        notice.notice(baseTaskStateNotice);
                    }
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            if (!StringUtils.isEmpty(countdlatch.step())){
                BaseTaskStateNotice baseTaskStateNotice = notice.getBaseTaskStateNotice(returnobj[0],2);
                notice.notice(baseTaskStateNotice);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            if (!StringUtils.isEmpty(countdlatch.step())){
                BaseTaskStateNotice baseTaskStateNotice = notice.getBaseTaskStateNotice(returnobj[0],2);
                notice.notice(baseTaskStateNotice);
            }
        }

    }

    private int checkIsFinish(CountDownLatch countDownLatch,int taskCount){
       return (int) (taskCount-countDownLatch.getCount());
    }


    private Object[] getObjects(ProceedingJoinPoint joinPoint, TaskTypeEnum type) {
        try {
            Object[] arguments = joinPoint.getArgs();
            Map map = JSON.parseObject(JSON.toJSONString(arguments[0]), Map.class);
            setTaks(type, map);

            Object o = changeObj(arguments[0], map);
            Object[] returnobj = new Object[]{o};
            return returnobj;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /*Object 转list*/
    private <T> List<T> castList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }

    private Map setTaks(TaskTypeEnum type, Map map) {

        //封装task
        List<Task> tasks = new ArrayList<>();
        String startTime = String.valueOf(map.get("startTime"));
        String endTime = String.valueOf(map.get("endTime"));
        if (type.equals(TaskTypeEnum.EPHEMERIS_CALCULATION)){
            //星历计算提前减八小时 因为一个exe只能算一天内数据
            Date newstart = new Date(DateUtils.StringToDate(startTime).getTime() - 8 * 3600 * 1000);
            Date newend = new Date(DateUtils.StringToDate(endTime).getTime() - 8 * 3600 * 1000);
            startTime = DateUtils.DateToString(newstart);
            endTime = DateUtils.DateToString(newend);
        }
        List<Map<String, String>> dayList = new ArrayList<>();
        if (type.equals(TaskTypeEnum.INSTANCE)){
            HashMap<String, String> map2 = new HashMap<>();
            map2.put("startTime", startTime);
            map2.put("endTime", endTime);
            dayList.add(map2);
        }else {
            dayList = DateUtils.splitDateByDay(startTime, endTime);
        }
        // 开始时间和结束时间相差天数
       /* long difference = DateUtils.getDay(startTime, endTime);*/
        /*舜耕修改*/
        /*List<Map<String, String>> dayList = DateUtils.splitDateByDay(startTime, endTime);*/
        for (int i = 0; i < dayList.size(); i++) {
            String taskSerial = type + "_" + DateUtils.dateTimeNow() + "_" + IdGenerator.getStrId();
            Task task = new Task(taskSerial);

            task.setTaskType(type);
            String resultDataSavePath=taskInitialize.FilepathMap.get(type+"_datpath").get(i);
            String inpPath = taskInitialize.FilepathMap.get(type+"_inppath").get(i);
            String okFilePath = taskInitialize.FilepathMap.get(type+"_okpath").get(i);
            String exeFilePath = taskInitialize.FilepathMap.get(type+"_exepath").get(i);
            task.setExeFileName(taskInitialize.FilepathMap.get(type+"_exename").get(0));
            task.setExeFilePath(exeFilePath);
            task.setInpName(taskInitialize.FilepathMap.get(type+"_inpname").get(0));
            task.setInpPath(inpPath);
            task.setOkFileName(taskInitialize.FilepathMap.get(type+"_okname").get(0));
            task.setOkFilePath(okFilePath);
            task.setResultDataSavePath(resultDataSavePath);
            task.setResultDataSaveName(taskInitialize.FilepathMap.get(type+"_datname").get(0));
            if (!CollectionUtils.isEmpty( taskInitialize.FilepathMap.get(type+"_hrpredatname"))){
                task.setResultHrpreDataSaveName(taskInitialize.FilepathMap.get(type+"_hrpredatname").get(0));
            }
            task.setTaskHandler(taskInitialize.map.get(type));
            task.setState(0);
            List<String> satelliteCodes = JSONArray.parseArray(JSON.toJSONString(map.get("satelliteCodes")), String.class);
            task.setSatelliteCodes(satelliteCodes);
            task.setStartTime(dayList.get(i).get("startTime"));
            task.setEndTime(dayList.get(i).get("endTime"));
            tasks.add(task);
        }
        map.put("tasks", tasks);
        return map;

    }


    public Object changeObj(Object obj, Map map) throws Exception {

        Field[] fieldInfo = obj.getClass().getDeclaredFields();
        for (Field field : fieldInfo) {
            int mod = field.getModifiers();
            if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                continue;
            }
            field.setAccessible(true);
            field.set(obj, map.get(field.getName()));
        }
        return obj;
    }

}
