package cn.stylefeng.workflow.design.modular.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.workflow.api.entity.design.Definition;
import cn.stylefeng.workflow.api.entity.design.Model;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.api.pojo.config.MyFlowDesign;
import cn.stylefeng.workflow.api.pojo.config.SixGraphDesign;
import cn.stylefeng.workflow.api.pojo.config.model.FlowGlobalConfig;
import cn.stylefeng.workflow.api.pojo.config.model.ModelForm;
import cn.stylefeng.workflow.api.pojo.config.model.ModelTimer;
import cn.stylefeng.workflow.api.pojo.instance.DefinitionFormatInfo;
import cn.stylefeng.workflow.design.api.callback.DefinitionUpgradeCallback;
import cn.stylefeng.workflow.design.api.callback.ModelRemoveCallback;
import cn.stylefeng.workflow.design.modular.enums.DefinitionExceptionEnum;
import cn.stylefeng.workflow.design.modular.factory.DefinitionFactory;
import cn.stylefeng.workflow.design.modular.factory.GraphFactory;
import cn.stylefeng.workflow.design.modular.mapper.DefinitionMapper;
import cn.stylefeng.workflow.design.modular.pojo.request.DefinitionRequest;
import cn.stylefeng.workflow.design.modular.service.DefinitionService;
import cn.stylefeng.workflow.design.modular.service.ModelService;
import cn.stylefeng.workflow.design.modular.service.NodeEdgeService;
import cn.stylefeng.workflow.design.modular.service.NodeService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DefinitionServiceImpl extends ServiceImpl<DefinitionMapper, Definition> implements DefinitionService, ModelRemoveCallback {

    @Resource
    private ModelService modelService;

    @Resource
    private NodeService nodeService;

    @Resource
    private NodeEdgeService nodeEdgeService;

    @Override
    public Definition detail(DefinitionRequest definitionRequest) {
        LambdaQueryWrapper<Definition> definitionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        definitionLambdaQueryWrapper.eq(Definition::getDefinitionId, definitionRequest.getDefinitionId());
        definitionLambdaQueryWrapper.select(Definition::getDefinitionId, Definition::getModelId, Definition::getDefinitionVersion,
                Definition::getMyFlowDesign, Definition::getXSixGraphJson);
        Definition definition = this.getOne(definitionLambdaQueryWrapper, false);
        if (definition == null) {
            return null;
        }

        Long modelId = definition.getModelId();
        Model model = modelService.getById(modelId);
        if (ObjectUtil.isNotEmpty(model)) {
            definition.setModelName(model.getModelName());
        }

        return definition;
    }

    @Override
    public Definition upgradeDefinitionAndSave(Long definitionId) {

        Definition definition = this.getById(definitionId);
        if (definition == null) {
            throw new ServiceException(DefinitionExceptionEnum.DEFINITION_NOT_EXISTED);
        }

        Definition newDefinition = DefinitionFactory.upgradeDefinition(definition);
        this.save(newDefinition);

        return newDefinition;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSixGraphDesign(DefinitionRequest definitionRequest) {

        Definition definition = this.queryDefinition(definitionRequest);

        this.updateDesign(definition, definitionRequest);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long upgradeAndSave(DefinitionRequest definitionRequest) {

        SixGraphDesign sixGraphDesign = definitionRequest.getSixGraphDesign();
        JSONObject replaceIdNewX6GraphDesign = DefinitionFactory.createReplaceIdNewX6GraphDesign(definitionRequest.getSixGraphDesign());
        sixGraphDesign.setNodeGraph(replaceIdNewX6GraphDesign);

        Definition definition = this.upgrade(definitionRequest.getDefinitionId());

        this.updateDesign(definition, definitionRequest);

        return definition.getDefinitionId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Definition upgrade(Long oldDefinitionId) {

        Definition oldDefinition = this.getById(oldDefinitionId);
        Definition newDefinition = DefinitionFactory.upgradeDefinition(oldDefinition);

        this.save(newDefinition);

        Long modelId = newDefinition.getModelId();
        modelService.updateModelVersion(modelId, newDefinition.getDefinitionVersion(), newDefinition.getDefinitionId());

        Map<String, DefinitionUpgradeCallback> beansOfType = SpringUtil.getBeansOfType(DefinitionUpgradeCallback.class);
        for (DefinitionUpgradeCallback value : beansOfType.values()) {
            value.definitionUpgradeAction(oldDefinition, newDefinition);
        }

        return newDefinition;
    }

    @Override
    public void modelRemoveAction(Long modelId) {
        this.remove(new LambdaQueryWrapper<Definition>().eq(Definition::getModelId, modelId));
    }

    @Override
    public Definition getDefinitionInfo(Long definitionId) {
        Definition definition = this.getById(definitionId);
        if (definition == null) {
            throw new ServiceException(DefinitionExceptionEnum.DEFINITION_NOT_EXISTED);
        }
        return definition;
    }

    @Override
    public FlowGlobalConfig getDefinitionGlobalConfig(Long definitionId) {

        Definition definitionInfo = this.getById(definitionId);
        if (definitionInfo == null) {
            return null;
        }

        SixGraphDesign xSixGraphJson = definitionInfo.getXSixGraphJson();
        if (ObjectUtil.isNotEmpty(xSixGraphJson)) {
            return xSixGraphJson.getFlowGlobalConfig();
        } else {
            return null;
        }
    }

    @Override
    public Definition updateDefinition(Definition definition, MyFlowDesign myFlowDesign, SixGraphDesign sixGraphDesign) {

        definition.setMyFlowDesign(myFlowDesign);
        definition.setXSixGraphJson(sixGraphDesign);

        if (ObjectUtil.isEmpty(sixGraphDesign)) {
            return definition;
        }

        List<NodeEdge> nodeEdges = GraphFactory.parseX6EdgeList(definition, sixGraphDesign.getNodeGraph());
        List<Node> nodes = GraphFactory.parseX6NodeList(definition, sixGraphDesign.getNodeGraph());

        this.nodeEdgeService.updateDefinitionEdges(definition.getDefinitionId(), nodeEdges);
        this.nodeService.updateDefinitionNodes(definition.getDefinitionId(), nodes);

        this.updateById(definition);

        return definition;
    }

    @Override
    public DefinitionFormatInfo getDefinitionInfoDetail(Long definitionId) {
        if (ObjectUtil.isEmpty(definitionId)) {
            return new DefinitionFormatInfo();
        }

        LambdaQueryWrapper<Definition> definitionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        definitionLambdaQueryWrapper.eq(Definition::getDefinitionId, definitionId);
        definitionLambdaQueryWrapper.select(Definition::getModelId, Definition::getDefinitionVersion);
        Definition definition = this.getOne(definitionLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(definition)) {
            return new DefinitionFormatInfo();
        }

        String modelName = modelService.getModelName(definition.getModelId());

        DefinitionFormatInfo definitionFormatInfo = new DefinitionFormatInfo();
        definitionFormatInfo.setDefinitionId(definitionId);
        definitionFormatInfo.setModelId(definition.getModelId());
        definitionFormatInfo.setDefinitionVersion(definition.getDefinitionVersion());
        definitionFormatInfo.setModelName(modelName);
        return definitionFormatInfo;
    }

    @Override
    public List<ModelForm> getDefinitionFormDefinitionIdList(Long definitionId) {

        FlowGlobalConfig flowGlobalConfig = getFlowGlobalConfig(definitionId);
        if (flowGlobalConfig == null) {
            return null;
        }

        return flowGlobalConfig.getFormDefinitionList();
    }

    @Override
    public Map<Long, Definition> getDefinitionListInfo(List<Long> definitionIdList) {
        LambdaQueryWrapper<Definition> definitionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        definitionLambdaQueryWrapper.in(Definition::getDefinitionId, definitionIdList);
        List<Definition> definitionList = this.list(definitionLambdaQueryWrapper);
        if (ObjectUtil.isEmpty(definitionList)) {
            return new HashMap<>();
        }
        return definitionList.stream().collect(Collectors.toMap(Definition::getDefinitionId, definition -> definition));
    }

    @Override
    public List<ModelTimer> getNodeWorkflowTimerList(Long definitionId) {

        FlowGlobalConfig flowGlobalConfig = getFlowGlobalConfig(definitionId);
        if (flowGlobalConfig == null) {
            return null;
        }

        return flowGlobalConfig.getModelTimerList();
    }

    private Definition queryDefinition(DefinitionRequest definitionRequest) {
        Definition definition = this.getById(definitionRequest.getDefinitionId());
        if (ObjectUtil.isEmpty(definition)) {
            throw new ServiceException(DefinitionExceptionEnum.DEFINITION_NOT_EXISTED);
        }
        return definition;
    }

    private void updateDesign(Definition definition, DefinitionRequest definitionRequest) {
        this.updateDefinition(definition, definitionRequest.getMyFlowDesign(), definitionRequest.getSixGraphDesign());
    }

    private FlowGlobalConfig getFlowGlobalConfig(Long definitionId) {
        Definition definitionInfo = this.getDefinitionInfo(definitionId);
        if (ObjectUtil.isEmpty(definitionInfo)) {
            return null;
        }

        SixGraphDesign xSixGraphJson = definitionInfo.getXSixGraphJson();
        if (ObjectUtil.isEmpty(xSixGraphJson)) {
            return null;
        }

        FlowGlobalConfig flowGlobalConfig = xSixGraphJson.getFlowGlobalConfig();
        if (ObjectUtil.isEmpty(flowGlobalConfig)) {
            return null;
        }
        return flowGlobalConfig;
    }

}