package com.tykj.dev.device.task.service.impl;

import com.github.wenhao.jpa.PredicateBuilder;
import com.github.wenhao.jpa.Specifications;
import com.tykj.dev.device.task.repository.TaskDao;
import com.tykj.dev.device.task.repository.TaskLogDao;
import com.tykj.dev.device.task.service.TaskService;
import com.tykj.dev.device.task.subject.bto.TaskBto;
import com.tykj.dev.device.task.subject.common.StatusEnum;
import com.tykj.dev.device.task.subject.common.GlobalMap;
import com.tykj.dev.device.task.subject.domin.Task;
import com.tykj.dev.device.task.subject.domin.TaskLog;
import com.tykj.dev.device.task.subject.vo.TaskSelectVo;
import com.tykj.dev.device.task.subject.vo.TaskUserVo;
import com.tykj.dev.device.task.utils.TaskUtils;
import com.tykj.dev.device.user.subject.service.UserPublicService;
import com.tykj.dev.device.user.util.UserUtils;
import com.tykj.dev.misc.exception.ApiException;
import com.tykj.dev.misc.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author dengdiyi
 */
@Service
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private TaskUtils taskUtils;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private UserPublicService userPublicService;

    @Autowired
    private TaskLogDao taskLogDao;

    /**
     * <p>业务进行到下一个状态</p>
     * <li>不指定待办用户</li>
     */
    @Override
    public TaskBto moveToNext(TaskBto taskBto) {
        //status++
        taskBto.setBillStatus(GlobalMap.getStatusEnumMap().getOrDefault(taskBto.getBillStatus() + 1, StatusEnum.END).id);
        //list add 0,point++
        taskBto.getInvolveUserIdList().add(0);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        return taskBto;
    }

    /**
     * <p>业务进行到下一个状态</p>
     * <li>不指定待办用户</li>
     * <li>添加自定义信息</li>
     * @param customInfo 自定义信息
     */
    @Override
    public TaskBto moveToNext(TaskBto taskBto, String customInfo) {
        //status++
        taskBto.setBillStatus(GlobalMap.getStatusEnumMap().getOrDefault(taskBto.getBillStatus() + 1, StatusEnum.END).id);
        //list add 0,point++
        taskBto.getInvolveUserIdList().add(0);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        //set customInfo
        taskBto.setCustomInfo(customInfo);
        return taskBto;
    }

    /**
     * <p>业务进行到下一个状态（指定待办用户）</p>
     * <li>指定待办用户</li>
     * @param userId 待办用户Id
     */
    @Override
    public TaskBto moveToNext(TaskBto taskBto, Integer userId) {
        //status++
        taskBto.setBillStatus(GlobalMap.getStatusEnumMap().getOrDefault(taskBto.getBillStatus() + 1, StatusEnum.END).id);
        //list add userId,point++
        taskBto.getInvolveUserIdList().add(userId);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        return taskBto;
    }

    /**
     * <p>业务进行到下一个状态（指定待办用户）</p>
     * <li>指定待办用户</li>
     * <li>添加自定义信息</li>
     * @param userId     待办用户Id
     * @param customInfo 自定义信息
     */
    @Override
    public TaskBto moveToNext(TaskBto taskBto, Integer userId, String customInfo) {
        //status++
        taskBto.setBillStatus(GlobalMap.getStatusEnumMap().getOrDefault(taskBto.getBillStatus() + 1, StatusEnum.END).id);
        //list add userId,point++
        taskBto.getInvolveUserIdList().add(userId);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        //set customInfo
        taskBto.setCustomInfo(customInfo);
        return taskBto;
    }

    /**
     * <p>业务进行到特殊状态</p>
     * <li>不指定待办用户</li>
     * @param statusEnum 状态枚举
     */
    @Override
    public TaskBto moveToSpecial(TaskBto taskBto, StatusEnum statusEnum) {
        //status++
        taskBto.setBillStatus(statusEnum.id);
        //list add 0,point++
        taskBto.getInvolveUserIdList().add(0);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        return taskBto;
    }

    /**
     * <p>业务进行到特殊状态</p>
     * <li>不指定待办用户</li>
     * <li>添加自定义信息</li>
     * @param statusEnum 状态枚举
     * @param customInfo 自定义信息
     */
    @Override
    public TaskBto moveToSpecial(TaskBto taskBto, StatusEnum statusEnum, String customInfo) {
        //status++
        taskBto.setBillStatus(statusEnum.id);
        //list add 0,point++
        taskBto.getInvolveUserIdList().add(0);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        //set customInfo
        taskBto.setCustomInfo(customInfo);
        return taskBto;
    }

    /**
     * <p>业务进行到特殊状态</p>
     * <li>指定待办用户</li>
     * @param statusEnum 状态枚举
     * @param userId     待办用户Id
     */
    @Override
    public TaskBto moveToSpecial(TaskBto taskBto, StatusEnum statusEnum, Integer userId) {
        //status++
        taskBto.setBillStatus(statusEnum.id);
        //list add userId,point++
        taskBto.getInvolveUserIdList().add(userId);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        return taskBto;
    }

    /**
     * <p>业务进行到特殊状态</p>
     * <li>指定待办用户</li>
     * <li>添加自定义信息</li>
     * @param statusEnum 状态枚举
     * @param userId     待办用户Id
     * @param customInfo 自定义信息
     */
    @Override
    public TaskBto moveToSpecial(TaskBto taskBto, StatusEnum statusEnum, Integer userId, String customInfo) {
        //status++
        taskBto.setBillStatus(statusEnum.id);
        //list add userId,point++
        taskBto.getInvolveUserIdList().add(userId);
        taskBto.setCurrentPoint(taskBto.getCurrentPoint() + 1);
        //set readList empty
        taskBto.setUserReadDetailList(new ArrayList<>());
        //set customInfo
        taskBto.setCustomInfo(customInfo);
        return taskBto;
    }

    /**
     * 业务完结
     */
    @Override
    public TaskBto moveToEnd(TaskBto taskBto) {
        //set status end
        taskBto.setBillStatus(StatusEnum.END.id);
        return taskBto;
    }

    /**
     * 业务审核失败封存
     */
    @Override
    public TaskBto moveToArchive(TaskBto taskBto) {
        //set status archive
        taskBto.setBillStatus(StatusEnum.ARCHIVE.id);
        return taskBto;
    }

    /**
     * 新增Task
     */
    @Override
    @ExceptionHandler(Exception.class)
    public Task start(TaskBto taskBto) {
        return taskDao.save(taskBto.toDo());
    }

    /**
     * 更新Task
     */
    @Override
    public Task update(TaskBto taskBto) {
        return taskDao.save(taskBto.toDo());
    }

    /**
     * 根据id获得Task
     */
    @Override
    public TaskBto get(Integer id) {
        Optional<Task> task = taskDao.findById(id);
        return task.map(Task::parse2Bto).orElse(null);
    }

    /**
     * 根据billId和businessType获得Task
     */
    @Override
    public TaskBto get(Integer billId, Integer businessType) {
        return taskDao.findByBillIdAndBusinessType(billId, businessType).parse2Bto();
    }

    /**
     * @param taskSelectVo
     * 获取跟踪和待办业务列表
     */
    @Override
    public List<TaskUserVo> getList(TaskSelectVo taskSelectVo) {
        List<TaskUserVo> taskEntities = getTaskUserVoList(taskSelectVo);
        if (taskSelectVo.getOrders() != null) {
            //根据vo待办和跟踪时间的查询顺序按顺序或降序排序输出
            if (taskSelectVo.getSelectNum() == 2 && "trackingTime".equals(taskSelectVo.getOrders().get(0).getCoulmn())) {
                if ("ASC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                    return taskUtils.orderByTrackingTimeDateAsc(taskEntities);
                }
                if ("DESC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                    return taskUtils.orderByTrackingTimeDateDesc(taskEntities);
                }
            }
            if (taskSelectVo.getSelectNum() == 3 && "userTime".equals(taskSelectVo.getOrders().get(0).getCoulmn())) {
                if ("ASC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                    return taskUtils.orderByUserTimeDateAsc(taskEntities);
                }
                if ("DESC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                    return taskUtils.orderByUserTimeDateDesc(taskEntities);
                }
            }
        }
        return taskEntities;
    }

    /**
     * @param taskSelectVo 获取业务管理中的业务列表
     */
    @Override
    public List<TaskUserVo> getManageList(TaskSelectVo taskSelectVo) {
        Integer num = taskSelectVo.getSelectNum();
        Integer userId = userUtils.getCurrentUserId();
        //业务管理中的待办和跟踪
        if (num==2||num==3){
            //查询出符合筛选条件的所有task
            List<TaskUserVo> list = taskDao.findAll(getSelectSpecification(taskSelectVo)).stream()
                    .map(Task::parse2Bto)
                    .map(TaskBto::toVo)
                    .collect(Collectors.toList());
            //查询当前用户的跟踪和待办并和list按id取交集
            List<TaskUserVo> taskUserVos = getList(taskSelectVo).stream()
                    .filter(taskUserVo -> find(taskUserVo.getId(),list)>-1)
                    .collect(Collectors.toList());
            //判断是否需要按发起时间排序
            if (taskSelectVo.getOrders()!=null) {
                if ("createTime".equals(taskSelectVo.getOrders().get(0).getCoulmn())) {
                    if ("ASC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                        return taskUtils.orderByCreateTimeAsc2(taskUserVos);
                    }
                    if ("DESC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                        return taskUtils.orderByCreateTimeDesc2(taskUserVos);
                    }
                }
            }
            return taskUserVos;
        }
        if (num==4||num==5||num==1||num==0) {
            //获取单位等级
            Integer level = userUtils.getCurrentUnitLevel();
            //获取该单位以及下属单位所有用户Id
            List<Integer> idLists = userPublicService.findAllUserIdByUnitsName(userUtils.getCurrentUserUnitName());
            List<TaskUserVo> taskUserVos = new ArrayList<>();
            //省能看到所有业务
            if (level==1) {
                taskUserVos = taskDao.findAll(getSelectSpecification(taskSelectVo)).stream()
                        .map(Task::parse2Bto)
                        .map(TaskBto::toVo)
                        .collect(Collectors.toList());
            }
            //市或县只能看到涉及人员和idLists有交集的
            if (level==2||level==3){
                taskUserVos = taskDao.findAll(getSelectSpecification(taskSelectVo)).stream()
                        .map(Task::parse2Bto)
                        .map(TaskBto::toVo)
                        .filter(taskUserVo -> !Collections.disjoint(idLists, taskUserVo.getInvolveUserIdList()))
                        .collect(Collectors.toList());
            }
            //set经办人，置顶以及阅读情况
            for (TaskUserVo taskUserVo:taskUserVos) {
                List<Integer> idList = taskUserVo.getUserReadDetailList();
                List<Integer> idList2 = taskUserVo.getTopFlagDetailList();
                if (taskUserVo.getInvolveUserIdList() != null && taskUserVo.getInvolveUserIdList().size() > 0) {
                    //筛选出userId大于0的
                    List<Integer> userIds = taskUserVo.getInvolveUserIdList().stream().filter(integer -> integer > 0).collect(Collectors.toList());
                    if (userIds.size() > 0) {
                        //List最后一个id即为最新经办人
                        taskUserVo.setProcessingUser(userPublicService.getOne(userIds.get(userIds.size() - 1)).getName());
                    }
                }
                //判断当前用户是否已读
                if (idList.contains(userId)) {
                    taskUserVo.setIsRead(1);
                }
                //判断当前用户是否置顶
                if (idList2.contains(userId)) {
                    taskUserVo.setIsTop(1);
                }
            }
            //判断是否需要按发起时间排序
            if (taskSelectVo.getOrders()!=null) {
                if ("createTime".equals(taskSelectVo.getOrders().get(0).getCoulmn())) {
                    if ("ASC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                        return taskUtils.orderByCreateTimeAsc2(taskUserVos);
                    }
                    if ("DESC".equals(taskSelectVo.getOrders().get(0).getDirection().toString())) {
                        return taskUtils.orderByCreateTimeDesc2(taskUserVos);
                    }
                }
            }
            return taskUserVos;
        }
        else {
            throw new ApiException(ResultUtil.failed("selectNum只能为0，1，2，3，4，5"));
        }
    }

    /**
     * @param taskBto 任务bto
     * @param userId  新增涉及用户id
     */
    @Override
    public TaskBto addInvolveUser(TaskBto taskBto, Integer userId) {
        List<Integer> list =  taskBto.getInvolveUserIdList();
        //添加涉及用户Id
        list.add(userId);
        taskBto.setInvolveUserIdList(list);
        //指针后移
        taskBto.setCurrentPoint(taskBto.getCurrentPoint()+1);
        return taskBto;
    }

    /**
     * @param taskSelectVo 查询vo
     * 查询跟踪和待办列表
     * @return taskUserVo列表
     */
    private List<TaskUserVo> getTaskUserVoList(TaskSelectVo taskSelectVo) {
        Integer userId = userUtils.getCurrentUserId();
        Integer bussinessType = taskSelectVo.getBusinessType();
        //筛选出未完结和封存业务，映射成bto
        List<TaskBto> taskBtos = taskDao.findAll().stream()
                .filter(task -> (!task.getBillStatus().equals(StatusEnum.END.id))&&(!task.getBillStatus().equals(StatusEnum.ARCHIVE.id)))
                .map(Task::parse2Bto)
                .collect(Collectors.toList());
        //查询待办
        if (taskSelectVo.getSelectNum() == 2) {
            //当前指针对应userId是当前用户或者为0且所属单位为当前单位
            List<TaskBto> taskBtoList = taskBtos.stream()
                    .filter(taskBto -> userId.equals(taskBto.getInvolveUserIdList().get(taskBto.getCurrentPoint())) || (taskBto.getInvolveUserIdList().get(taskBto.getCurrentPoint()) == 0 && taskBto.getOwnUnit().equals(userUtils.getCurrentUnitId())))
                    .collect(Collectors.toList());
            //查询所有的业务
            if (bussinessType == null) {
                List<TaskUserVo> taskUserVos = taskBtoList.stream().map(TaskBto::toVo).collect(Collectors.toList());
                return setTaskUserVo(taskUserVos,0);
            } else {
                //查询工作台其它业务
                if (bussinessType == 0) {
                    List<TaskUserVo> taskUserVos = taskBtoList.stream()
                            .filter(taskBto -> !new ArrayList<>(Arrays.asList(3,4,5,6)).contains(taskBto.getBusinessType()))
                            .map(TaskBto::toVo)
                            .collect(Collectors.toList());
                    return setTaskUserVo(taskUserVos,0);
                }
                //查询某一业务
                else {
                    List<TaskUserVo> taskUserVos = taskBtoList.stream()
                            .filter(taskBto -> taskBto.getBusinessType().equals(bussinessType))
                            .map(TaskBto::toVo)
                            .collect(Collectors.toList());
                    return setTaskUserVo(taskUserVos,0);
                }
            }
        }
        //查询跟踪
        if (taskSelectVo.getSelectNum() == 3) {
            //涉及人员包括当前用户且指针对应UserId不是当前用户
            List<TaskBto> taskBtoList = taskBtos.stream()
                    .filter(taskBto -> taskBto.getInvolveUserIdList().contains(userId)&&!userId.equals(taskBto.getInvolveUserIdList().get(taskBto.getCurrentPoint())))
                    .collect(Collectors.toList());
            //查询所有业务
            if (bussinessType == null) {
                List<TaskUserVo> taskUserVos = taskBtoList.stream().map(TaskBto::toVo).collect(Collectors.toList());
                return setTaskUserVo(taskUserVos,1);
            } else {
                //查询工作台其它业务
                if (bussinessType == 0) {
                    List<TaskUserVo> taskUserVos = taskBtoList.stream()
                            .filter(taskBto -> !new ArrayList<>(Arrays.asList(3,4,5,6)).contains(taskBto.getBusinessType()))
                            .map(TaskBto::toVo)
                            .collect(Collectors.toList());
                    return setTaskUserVo(taskUserVos,1);
                }
                //查询某一业务
                else {
                    List<TaskUserVo> taskUserVos = taskBtoList.stream()
                            .filter(taskBto -> taskBto.getBusinessType().equals(bussinessType))
                            .map(TaskBto::toVo)
                            .collect(Collectors.toList());
                    return setTaskUserVo(taskUserVos,1);
                }
            }
        } else {
            throw new ApiException(ResultUtil.failed("selectNum只能为2或3"));
        }
    }

    /**
     * @param list taskUserVos
     * @param type 0:set待办 1:set跟踪
     * set一些输出给前端的值
     */
    private List<TaskUserVo> setTaskUserVo(List<TaskUserVo> list, int type) {
        Integer userId = userUtils.getCurrentUserId();
        list.forEach(taskUserVo -> {
            List<Integer> idList = taskUserVo.getUserReadDetailList();
            List<Integer> idList2 = taskUserVo.getTopFlagDetailList();
            if (taskUserVo.getInvolveUserIdList() != null && taskUserVo.getInvolveUserIdList().size() > 0) {
                //筛选出userId大于0的
                List<Integer> userIds = taskUserVo.getInvolveUserIdList().stream().filter(integer -> integer > 0).collect(Collectors.toList());
                if (userIds.size() > 0) {
                    //List最后一个id即为最新经办人
                    taskUserVo.setProcessingUser(userPublicService.getOne(userIds.get(userIds.size() - 1)).getName());
                }
            }
            //判断当前用户是否已读
            if (idList.contains(userId)) {
                taskUserVo.setIsRead(1);
            }
            //判断当前用户是否置顶
            if (idList2.contains(userId)) {
                taskUserVo.setIsTop(1);
            }
            //set待办时间
            if (type == 0) {
                long time = System.currentTimeMillis() - taskUserVo.getUpdateTime().getTime();
                int day = new Long(time / 86_400_000).intValue();
                int hour = new Long((time % 86_400_000) / 3_600_000).intValue();
                taskUserVo.setTrackingTime(day + "天" + hour + "小时");
                taskUserVo.setTrackingTimeDate(new Date(time));
            }
            //set跟踪时间
            if (type == 1) {
                Date userTime = getUserTime(taskUserVo.getId());
                if (userTime == null) {
                    taskUserVo.setUserTime("0天0小时");
                    taskUserVo.setUserTimeDate(new Date(0));
                }
                else {
                    long time = System.currentTimeMillis() - userTime.getTime();
                    int day = new Long(time / 86_400_000).intValue();
                    int hour = new Long((time % 86_400_000) / 3_600_000).intValue();
                    taskUserVo.setUserTime(day + "天" + hour + "小时");
                    taskUserVo.setUserTimeDate(new Date(time));
                }
            }
        });
        return list;
    }

    /**
     * @param taskId 业务id
     * 获取当前用户上次处理业务时间
     */
    private Date getUserTime(Integer taskId) {
        List<TaskLog> list = taskLogDao.getAllByTaskId(taskId);
        if (list.size()>0) {
            //筛选出当前用户操作该业务的所有日志，根据业务日志的创建时间降序排列，得到最新的上一次操作时间
            List<TaskLog> taskLogs = taskUtils.orderByCreateTimeDesc(list.stream().filter(taskLog -> taskLog.getCreateUserId().equals(userUtils.getCurrentUserId())).collect(Collectors.toList()));
            return taskLogs.get(0).getCreateTime();
        }
        else {
            return null;
        }
    }

    /**
     * @param taskSelectVo
     * task查询器
     */
    private Specification<Task> getSelectSpecification(TaskSelectVo taskSelectVo){
        PredicateBuilder<Task> predicateBuilder = Specifications.and();
        if (taskSelectVo.getBusinessType()!=null){
            predicateBuilder.eq("businessType", taskSelectVo.getBusinessType());
        }
        if (taskSelectVo.getContent()!=null){
            Class<Task> taskEntityClass = Task.class;
            Field[] declaredFields = taskEntityClass.getDeclaredFields();
            PredicateBuilder<Task> p = Specifications.or();
            for (Field field : declaredFields) {
                if (field.getType().equals(String.class)&&field.getAnnotation(Transient.class)==null) {
                    p.like(field.getName(), "%" + taskSelectVo.getContent() + "%");
                }
            }
            predicateBuilder.predicate(p.build());
        }
        if (taskSelectVo.getStartTime()!=null){
            predicateBuilder.gt("createTime", taskSelectVo.getStartTime());
        }
        if (taskSelectVo.getEndTime()!=null){
            predicateBuilder.lt("createTime", taskSelectVo.getEndTime());
        }
        if (taskSelectVo.getSelectNum()==4){
            predicateBuilder.eq("billStatus",StatusEnum.END.id);
        }
        if (taskSelectVo.getSelectNum()==5){
            predicateBuilder.eq("billStatus",StatusEnum.ARCHIVE.id);
        }
        if (taskSelectVo.getSelectNum()==1){
            predicateBuilder.eq("createUserId",userUtils.getCurrentUnitId());
        }
        return predicateBuilder.build();
    }

    /**
     * 判断list中是否包含某个id的taskUserVo,若不存在返回-1，存在则返回第一次出现的索引值
     */
    int find(Integer id,List<TaskUserVo> list){
        int index = -1;
        if (list!=null&&list.size()>0){
            for (int i=0;i<list.size();i++){
                if (list.get(i).getId().equals(id)){
                    index = i;
                    break;
                }
            }
        }
        return index;
    }
}
