package com.yunzhupaas.flowable.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yunzhupaas.base.entity.VisualdevEntity;
import com.yunzhupaas.base.service.SuperServiceImpl;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.exception.WorkFlowException;
import com.yunzhupaas.flowable.entity.TemplateEntity;
import com.yunzhupaas.flowable.entity.TemplateJsonEntity;
import com.yunzhupaas.flowable.entity.TemplateNodeEntity;
import com.yunzhupaas.flowable.enums.NodeEnum;
import com.yunzhupaas.flowable.job.QuartzJobUtil;
import com.yunzhupaas.flowable.job.TimeTriggerJob;
import com.yunzhupaas.flowable.job.TriggerJobUtil;
import com.yunzhupaas.flowable.mapper.TemplateJsonMapper;
import com.yunzhupaas.flowable.model.flowable.FlowAbleUrl;
import com.yunzhupaas.flowable.model.template.FlowConfigModel;
import com.yunzhupaas.flowable.model.templatejson.TemplateJsonInfoVO;
import com.yunzhupaas.flowable.model.templatenode.TemplateNodeUpFrom;
import com.yunzhupaas.flowable.model.templatenode.nodejson.NodeModel;
import com.yunzhupaas.flowable.model.trigger.TimeTriggerModel;
import com.yunzhupaas.flowable.service.TemplateJsonService;
import com.yunzhupaas.flowable.service.TemplateNodeService;
import com.yunzhupaas.flowable.service.TemplateService;
import com.yunzhupaas.flowable.util.ServiceUtil;
import com.yunzhupaas.util.*;
import org.quartz.JobDataMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class TemplateJsonServiceImpl extends SuperServiceImpl<TemplateJsonMapper, TemplateJsonEntity> implements TemplateJsonService {

    @Autowired
    private FlowAbleUrl flowAbleUrl;
    @Autowired
    private TemplateService templateService;
    @Autowired
    private TemplateNodeService templateNodeService;
    @Autowired
    private ServiceUtil serviceUtil;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public List<TemplateJsonEntity> getListByTemplateIds(List<String> id) {
        List<TemplateJsonEntity> list = new ArrayList<>();
        if (!id.isEmpty()) {
            QueryWrapper<TemplateJsonEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().in(TemplateJsonEntity::getTemplateId, id);
            list.addAll(this.list(queryWrapper));
        }
        return list;
    }

    @Override
    public List<TemplateJsonEntity> getList(String templateId) {
        QueryWrapper<TemplateJsonEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TemplateJsonEntity::getTemplateId, templateId);
        List<TemplateJsonEntity> list = this.list(queryWrapper);

        List<TemplateJsonEntity> resList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(list)) {
            // 启用中
            List<TemplateJsonEntity> enableList = list.stream().filter(e -> ObjectUtil.equals(e.getState(), 1))
                    .sorted(Comparator.comparing(TemplateJsonEntity::getCreatorTime).reversed()).collect(Collectors.toList());
            // 已归档
            List<TemplateJsonEntity> historicList = list.stream().filter(e -> ObjectUtil.equals(e.getState(), 2))
                    .sorted(Comparator.comparing(TemplateJsonEntity::getCreatorTime).reversed()).collect(Collectors.toList());
            // 设计中
            List<TemplateJsonEntity> designList = list.stream().filter(e -> ObjectUtil.equals(e.getState(), 0))
                    .sorted(Comparator.comparing(TemplateJsonEntity::getCreatorTime).reversed()).collect(Collectors.toList());

            resList.addAll(enableList);
            resList.addAll(historicList);
            resList.addAll(designList);
        }

        return resList;
    }

    @Override
    public List<TemplateJsonEntity> getListOfEnable() {
        QueryWrapper<TemplateJsonEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TemplateJsonEntity::getState, 1);
        return this.list(queryWrapper);
    }

    @Override
    public TemplateJsonEntity getInfo(String id) throws WorkFlowException {
        QueryWrapper<TemplateJsonEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TemplateJsonEntity::getId, id);
        TemplateJsonEntity jsonEntity = this.getOne(queryWrapper);
        if (jsonEntity == null) {
            throw new WorkFlowException(MsgCode.FA001.get());
        }
        return jsonEntity;
    }

    @Override
    public boolean update(String id, TemplateJsonEntity entity) {
        entity.setId(id);
        return this.updateById(entity);
    }

    @DSTransactional
    @Override
    public void save(TemplateNodeUpFrom from) throws WorkFlowException {
        String templateJsonId = from.getFlowId();
        String id = from.getId();
        String flowXml = from.getFlowXml();
        Map<String, Map<String, Object>> flowNodes = from.getFlowNodes();
        TemplateJsonEntity jsonEntity = getInfo(templateJsonId);
        jsonEntity.setFlowXml(flowXml);
        TemplateEntity entity = templateService.getInfo(id);
        String flowConfig = from.getFlowConfig();
        entity.setFlowConfig(flowConfig);
        FlowConfigModel config = JsonUtil.getJsonToBean(flowConfig, FlowConfigModel.class);
        config = config == null ? new FlowConfigModel() : config;
        entity.setVisibleType(config.getVisibleType());
        //发布流程
        if (Objects.equals(from.getType(), 1)) {
            if (StringUtil.isEmpty(jsonEntity.getFlowableId())) {
                String deploymentId = flowAbleUrl.deployFlowAble(flowXml, templateJsonId);
                jsonEntity.setFlowableId(deploymentId);
            }
            //改流程版本
            if (StringUtil.isNotEmpty(id)) {
                TemplateJsonEntity info = getList(id).stream().filter(t -> Objects.equals(t.getState(), 1)).findFirst().orElse(null);
                int state = jsonEntity.getState();
                if (info != null) {
                    // 变更归档状态，排序码
                    //info.setSortCode(0L);
                    info.setState(2);
                    this.update(info.getId(), info);
                }
                jsonEntity.setState(1);
                //jsonEntity.setSortCode(1L);
                entity.setFlowId(templateJsonId);
                entity.setFlowableId(jsonEntity.getFlowableId());
                entity.setVersion(jsonEntity.getVersion());
                entity.setEnabledMark(1);
                entity.setStatus(1);
                // 归档状态的仅启用
                if (Objects.equals(2, state)) {
                    templateService.update(entity.getId(), entity);
                    this.update(jsonEntity.getId(), jsonEntity);
//                    this.timeTrigger(jsonEntity.getId());
                    return;
                }
            }
        }
        templateService.update(entity.getId(), entity);

        TemplateNodeEntity timeTriggerNode = null;
        if (ObjectUtil.isNotEmpty(flowNodes)) {
            List<TemplateNodeEntity> list = templateNodeService.getList(jsonEntity.getId());
            List<String> deleteList = list.stream().map(TemplateNodeEntity::getId).collect(Collectors.toList());
            templateNodeService.delete(deleteList);
            // 开始节点的表单
            String startFormId = null;
            for (String key : flowNodes.keySet()) {
                NodeModel startNode = JsonUtil.getJsonToBean(flowNodes.get(key), NodeModel.class);
                if (ObjectUtil.equals(startNode.getType(), NodeEnum.start.getType())) {
                    startFormId = startNode.getFormId();
                    break;
                }
            }
            String formId = null;
            for (String key : flowNodes.keySet()) {
                NodeModel nodeModel = JsonUtil.getJsonToBean(flowNodes.get(key), NodeModel.class);
                if (ObjectUtil.equals(nodeModel.getType(), NodeEnum.global.getType())) {
                    formId = nodeModel.getFormId();
                }
                TemplateNodeEntity nodeEntity = list.stream().filter(t -> ObjectUtil.equals(t.getNodeCode(), key)).findFirst().orElse(new TemplateNodeEntity());
                nodeEntity.setId(StringUtil.isNotEmpty(nodeEntity.getId()) ? nodeEntity.getId() : RandomUtil.uuId());
                nodeEntity.setFlowId(jsonEntity.getId());
                nodeEntity.setNodeCode(key);
                nodeEntity.setNodeJson(JsonUtil.getObjectToString(flowNodes.get(key)));
                nodeEntity.setNodeType(nodeModel.getType());
                if (StringUtil.isNotEmpty(nodeModel.getFormId())) {
                    nodeEntity.setFormId(nodeModel.getFormId());
                } else {
                    nodeEntity.setFormId(startFormId);
                }
                // 触发节点：定时、webhook 将flowId设置到formId字段   通知将msgId设置到formId
                if (ObjectUtil.equals(nodeModel.getType(), NodeEnum.timeTrigger.getType())) {
                    nodeEntity.setFormId(nodeEntity.getFlowId());
                    if (Objects.equals(from.getType(), 1)) {
                        timeTriggerNode = nodeEntity;
                    }
                } else if (ObjectUtil.equals(nodeModel.getType(), NodeEnum.webhookTrigger.getType())) {
                    nodeEntity.setFormId(nodeEntity.getFlowId());
                } else if (ObjectUtil.equals(nodeModel.getType(), NodeEnum.noticeTrigger.getType())) {
                    nodeEntity.setFormId(nodeModel.getNoticeId());
                }
                templateNodeService.setIgnoreLogicDelete().removeById(nodeEntity.getId());
                templateNodeService.setIgnoreLogicDelete().saveOrUpdate(nodeEntity);
                templateNodeService.clearIgnoreLogicDelete();
            }
            // 保存表单关联
            if (null != formId) {
                List<String> formIds = new ArrayList<>();
                formIds.add(formId);
            }
        }
        this.update(jsonEntity.getId(), jsonEntity);
        if (null != timeTriggerNode) {
            this.timeTrigger(timeTriggerNode);
        }
    }

    public void timeTrigger(String flowId) {
        List<TemplateNodeEntity> list = templateNodeService.getList(flowId);
        List<TemplateNodeEntity> timeTriggerList = list.stream()
                .filter(e -> ObjectUtil.equals(e.getNodeType(), NodeEnum.timeTrigger.getType())).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(timeTriggerList)) {
            for (TemplateNodeEntity nodeEntity : timeTriggerList) {
                this.timeTrigger(nodeEntity);
            }
        }
    }

    public void timeTrigger(TemplateNodeEntity nodeEntity) {
        NodeModel nodeModel = JsonUtil.getJsonToBean(nodeEntity.getNodeJson(), NodeModel.class);
        String id = RandomUtil.uuId();
        TimeTriggerModel model = new TimeTriggerModel();
        model.setId(id);
        model.setFlowId(nodeEntity.getFlowId());
        model.setUserInfo(UserProvider.getUser());
        model.setCron(nodeModel.getCron());
        model.setEndLimit(nodeModel.getEndLimit());
        model.setEndTimeType(nodeModel.getEndTimeType());

        String start = nodeModel.getStartTime();
        String end = nodeModel.getEndTime();
        Date startTime = ObjectUtil.isNotEmpty(start) ? new Date(Long.parseLong(start)) : new Date();
        Date endTime = ObjectUtil.equals(nodeModel.getEndTimeType(), 2) ? new Date(Long.parseLong(end)) : null;
        model.setStartTime(startTime.getTime());
        model.setEndTime(endTime != null ? endTime.getTime() : null);

        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.putAll(JsonUtil.entityToMap(model));

        redisUtil.removeHash(TriggerJobUtil.TRIGGER_MODEL, model.getId());
        boolean isAdd = endTime == null || endTime.getTime() > System.currentTimeMillis();
        if (isAdd) {
            QuartzJobUtil.addJob(id, nodeModel.getCron(), TimeTriggerJob.class, jobDataMap, startTime, endTime);
        }
    }

    @DSTransactional
    @Override
    public void create(TemplateNodeUpFrom from) {
        Map<String, Map<String, Object>> flowNodes = from.getFlowNodes();
        String flowXml = from.getFlowXml();
        String templateJsonId = from.getFlowId();
        String id = from.getId();
        List<TemplateJsonEntity> jsonList = getList(id);
        int version = jsonList.stream().map(TemplateJsonEntity::getVersion).mapToInt(Integer::parseInt).max().orElse(0) + 1;
        TemplateJsonEntity entity = new TemplateJsonEntity();
        entity.setId(StringUtil.isNotEmpty(templateJsonId) ? templateJsonId : RandomUtil.uuId());
        entity.setTemplateId(id);
        entity.setFlowXml(flowXml);
        NodeModel startNode = JsonUtil.getJsonToList(new ArrayList<>(flowNodes.values()), NodeModel.class).stream().filter(t -> NodeEnum.start.getType().equals(t.getType())).findFirst().orElse(null);
        entity.setVersion(String.valueOf(version));
        entity.setState(0);
        entity.setSortCode(0L);
        String formId = null != startNode ? startNode.getFormId() : null;
        for (String key : flowNodes.keySet()) {
            NodeModel nodeModel = JsonUtil.getJsonToBean(flowNodes.get(key), NodeModel.class);
            TemplateNodeEntity nodeEntity = new TemplateNodeEntity();
            nodeEntity.setId(StringUtil.isNotEmpty(nodeEntity.getId()) ? nodeEntity.getId() : RandomUtil.uuId());
            nodeEntity.setFlowId(entity.getId());
            nodeEntity.setNodeCode(key);
            nodeEntity.setNodeJson(JsonUtil.getObjectToString(flowNodes.get(key)));
            nodeEntity.setNodeType(nodeModel.getType());
            nodeEntity.setFormId(StringUtil.isBlank(nodeModel.getFormId()) ? formId : nodeModel.getFormId());
            templateNodeService.setIgnoreLogicDelete().removeById(nodeEntity.getId());
            templateNodeService.setIgnoreLogicDelete().saveOrUpdate(nodeEntity);
            templateNodeService.clearIgnoreLogicDelete();
        }
        this.setIgnoreLogicDelete().removeById(entity.getId());
        this.setIgnoreLogicDelete().saveOrUpdate(entity);
        this.clearIgnoreLogicDelete();
    }

    @Override
    public void delete(TemplateJsonEntity entity) {
        if (entity != null) {
            this.removeById(entity.getId());
        }
    }

    @Override
    public void delete(List<String> idList) {
        if (idList.size() > 0) {
            QueryWrapper<TemplateJsonEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().in(TemplateJsonEntity::getId, idList);
            this.remove(queryWrapper);
            templateNodeService.delete(idList);
        }
    }

    @Override
    public void copy(TemplateJsonEntity entity, String flowId) {
        List<TemplateNodeEntity> list = templateNodeService.getList(entity.getId());
        Map<String, Map<String, Object>> flowNodes = new HashMap<>();
        for (TemplateNodeEntity nodeEntity : list) {
            Map<String, Object> map = JsonUtil.stringToMap(nodeEntity.getNodeJson());
            if (ObjectUtil.equals(nodeEntity.getNodeType(), NodeEnum.webhookTrigger.getType())) {
                map.put("webhookUrl", "");
            }
            flowNodes.put(nodeEntity.getNodeCode(), map);
        }
        TemplateNodeUpFrom from = new TemplateNodeUpFrom();
        from.setId(entity.getTemplateId());
        from.setFlowXml(entity.getFlowXml());
        from.setFlowNodes(flowNodes);
        from.setFlowId(flowId);
        create(from);
    }

    @Override
    public TemplateJsonInfoVO getInfoVo(String id) throws WorkFlowException {
        TemplateJsonEntity jsonEntity = this.getInfo(id);
        TemplateEntity entity = templateService.getInfo(jsonEntity.getTemplateId());
        TemplateJsonInfoVO vo = JsonUtil.getJsonToBean(entity, TemplateJsonInfoVO.class);
        vo.setFlowXml(jsonEntity.getFlowXml());
        List<TemplateNodeEntity> templateNodeList = templateNodeService.getList(jsonEntity.getId());
        Map<String, Map<String, Object>> flowNodes = new HashMap<>();
        for (TemplateNodeEntity nodeEntity : templateNodeList) {
            flowNodes.put(nodeEntity.getNodeCode(), JsonUtil.stringToMap(nodeEntity.getNodeJson()));
        }
        vo.setFlowableId(jsonEntity.getFlowableId());
        vo.setFlowNodes(flowNodes);
        vo.setFlowId(jsonEntity.getId());
        return vo;
    }

    @Override
    public VisualdevEntity getFormInfo(String id) throws WorkFlowException {
        TemplateEntity template = templateService.getInfo(id);
        String flowId = template.getFlowId();
        TemplateJsonEntity jsonEntity = this.getInfo(flowId);
        List<TemplateNodeEntity> templateNodeList = templateNodeService.getList(jsonEntity.getId());
        TemplateNodeEntity global = templateNodeList.stream().filter(e -> ObjectUtil.equals(e.getNodeType(), NodeEnum.global.getType())).findFirst().orElse(null);
        if (null != global) {
            return serviceUtil.getFormInfo(global.getFormId());
        }
        return null;
    }
}

