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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.stylefeng.roses.kernel.dsctn.api.context.DataSourceContext;
import cn.stylefeng.roses.kernel.rule.enums.DbTypeEnum;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.design.api.callback.ModelRemoveCallback;
import cn.stylefeng.workflow.design.modular.enums.NodeEdgeExceptionEnum;
import cn.stylefeng.workflow.design.modular.mapper.NodeEdgeMapper;
import cn.stylefeng.workflow.design.modular.pojo.request.NodeEdgeRequest;
import cn.stylefeng.workflow.design.modular.service.NodeEdgeService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class NodeEdgeServiceImpl extends ServiceImpl<NodeEdgeMapper, NodeEdge> implements NodeEdgeService, ModelRemoveCallback {

    @Override
    public void add(NodeEdgeRequest nodeEdgeRequest) {
        NodeEdge nodeEdge = new NodeEdge();
        BeanUtil.copyProperties(nodeEdgeRequest, nodeEdge);
        this.save(nodeEdge);
    }

    @Override
    public void del(NodeEdgeRequest nodeEdgeRequest) {
        NodeEdge nodeEdge = this.queryNodeEdge(nodeEdgeRequest);
        this.removeById(nodeEdge.getEdgeId());
    }

    @Override
    public void edit(NodeEdgeRequest nodeEdgeRequest) {
        NodeEdge nodeEdge = this.queryNodeEdge(nodeEdgeRequest);
        BeanUtil.copyProperties(nodeEdgeRequest, nodeEdge);
        this.updateById(nodeEdge);
    }

    @Override
    public NodeEdge detail(NodeEdgeRequest nodeEdgeRequest) {
        return this.queryNodeEdge(nodeEdgeRequest);
    }

    @Override
    public PageResult<NodeEdge> findPage(NodeEdgeRequest nodeEdgeRequest) {
        LambdaQueryWrapper<NodeEdge> wrapper = createWrapper(nodeEdgeRequest);
        Page<NodeEdge> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(sysRolePage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDefinitionEdges(Long definitionId, List<NodeEdge> newEdgeList) {

        this.remove(new LambdaQueryWrapper<NodeEdge>().eq(NodeEdge::getDefinitionId, definitionId));

        if (DbTypeEnum.MYSQL.equals(DataSourceContext.me().getCurrentDbType())) {
            this.getBaseMapper().insertBatchSomeColumn(newEdgeList);
        } else {
            this.saveBatch(newEdgeList);
        }

    }

    @Override
    public List<NodeEdge> findList(NodeEdgeRequest nodeEdgeRequest) {
        LambdaQueryWrapper<NodeEdge> wrapper = this.createWrapper(nodeEdgeRequest);
        return this.list(wrapper);
    }

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

    private NodeEdge queryNodeEdge(NodeEdgeRequest nodeEdgeRequest) {
        NodeEdge nodeEdge = this.getById(nodeEdgeRequest.getEdgeId());
        if (ObjectUtil.isEmpty(nodeEdge)) {
            throw new ServiceException(NodeEdgeExceptionEnum.NODE_EDGE_NOT_EXISTED);
        }
        return nodeEdge;
    }

    private LambdaQueryWrapper<NodeEdge> createWrapper(NodeEdgeRequest nodeEdgeRequest) {
        LambdaQueryWrapper<NodeEdge> queryWrapper = new LambdaQueryWrapper<>();

        Long edgeId = nodeEdgeRequest.getEdgeId();
        Long modelId = nodeEdgeRequest.getModelId();
        Long definitionId = nodeEdgeRequest.getDefinitionId();
        Long sourceNodeId = nodeEdgeRequest.getSourceNodeId();
        Long targetNodeId = nodeEdgeRequest.getTargetNodeId();

        queryWrapper.eq(ObjectUtil.isNotNull(edgeId), NodeEdge::getEdgeId, edgeId);
        queryWrapper.eq(ObjectUtil.isNotNull(modelId), NodeEdge::getModelId, modelId);
        queryWrapper.eq(ObjectUtil.isNotNull(definitionId), NodeEdge::getDefinitionId, definitionId);
        queryWrapper.eq(ObjectUtil.isNotNull(sourceNodeId), NodeEdge::getSourceNodeId, sourceNodeId);
        queryWrapper.eq(ObjectUtil.isNotNull(targetNodeId), NodeEdge::getTargetNodeId, targetNodeId);

        return queryWrapper;
    }
}