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.Node;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.api.enums.NodeTypeEnum;
import cn.stylefeng.workflow.api.pojo.config.node.NodeData;
import cn.stylefeng.workflow.api.pojo.config.node.activity.NodeActivity;
import cn.stylefeng.workflow.api.pojo.config.node.condition.NodeCondition;
import cn.stylefeng.workflow.api.pojo.config.node.subflow.NodeSubFlow;
import cn.stylefeng.workflow.design.api.callback.ModelRemoveCallback;
import cn.stylefeng.workflow.design.modular.enums.NodeExceptionEnum;
import cn.stylefeng.workflow.design.modular.mapper.NodeMapper;
import cn.stylefeng.workflow.design.modular.pojo.request.NodeRequest;
import cn.stylefeng.workflow.design.modular.service.NodeEdgeService;
import cn.stylefeng.workflow.design.modular.service.NodeService;
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 jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Service
public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements NodeService, ModelRemoveCallback {

    @Resource
    private NodeEdgeService nodeEdgeService;

    @Override
    public void add(NodeRequest nodeRequest) {
        Node node = new Node();
        BeanUtil.copyProperties(nodeRequest, node);
        this.save(node);
    }

    @Override
    public void del(NodeRequest nodeRequest) {
        Node node = this.queryNode(nodeRequest);
        this.removeById(node.getNodeId());
    }

    @Override
    public void edit(NodeRequest nodeRequest) {
        Node node = this.queryNode(nodeRequest);
        BeanUtil.copyProperties(nodeRequest, node);
        this.updateById(node);
    }

    @Override
    public Node detail(NodeRequest nodeRequest) {
        return this.queryNode(nodeRequest);
    }

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDefinitionNodes(Long definitionId, List<Node> newNodeList) {

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

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

    @Override
    public Node getDefinitionApplicantNode(Long definitionId) {

        LambdaQueryWrapper<Node> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(Node::getDefinitionId, definitionId);

        queryWrapper.eq(Node::getNodeType, NodeTypeEnum.APPLICANT.getNodeType());

        List<Node> list = this.list(queryWrapper);

        if (ObjectUtil.isEmpty(list)) {
            throw new ServiceException(NodeExceptionEnum.CANT_FIND_APPLICANT);
        }

        if (ObjectUtil.isNotEmpty(list) && list.size() > 1) {
            throw new ServiceException(NodeExceptionEnum.APPLICANT_TOO_MANY);
        }

        return list.get(0);
    }

    @Override
    public List<Node> findList(NodeRequest nodeRequest) {
        LambdaQueryWrapper<Node> wrapper = this.createWrapper(nodeRequest);
        return this.list(wrapper);
    }

    @Override
    public void modelRemoveAction(Long modelId) {

        this.remove(new QueryWrapper<Node>().lambda().eq(Node::getModelId, modelId));

    }

    @Override
    public Node getNodeInfo(Long nodeId) {
        Node node = this.getById(nodeId);
        if (ObjectUtil.isEmpty(node)) {
            throw new ServiceException(NodeExceptionEnum.NODE_NOT_EXISTED);
        }
        return node;
    }

    @Override
    public List<Node> getDefinitionNodeList(Long definitionId) {

        if (ObjectUtil.isEmpty(definitionId)) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<Node> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(Node::getDefinitionId, definitionId);

        return this.list(queryWrapper);
    }

    @Override
    public List<NodeEdge> getDefinitionEdgeList(Long definitionId) {

        if (ObjectUtil.isEmpty(definitionId)) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<NodeEdge> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(NodeEdge::getDefinitionId, definitionId);

        return this.nodeEdgeService.list(queryWrapper);
    }

    private Node queryNode(NodeRequest nodeRequest) {
        Node node = this.getById(nodeRequest.getNodeId());
        if (ObjectUtil.isEmpty(node)) {
            throw new ServiceException(NodeExceptionEnum.NODE_NOT_EXISTED);
        }
        return node;
    }

    private LambdaQueryWrapper<Node> createWrapper(NodeRequest nodeRequest) {
        LambdaQueryWrapper<Node> queryWrapper = new LambdaQueryWrapper<>();

        Long definitionId = nodeRequest.getDefinitionId();
        queryWrapper.eq(ObjectUtil.isNotNull(definitionId), Node::getDefinitionId, definitionId);

        return queryWrapper;
    }

    @Override
    public NodeActivity getNodeActivity(Long nodeId) {

        Node node = this.getById(nodeId);
        if (ObjectUtil.isEmpty(node)) {
            return null;
        }

        NodeData nodeData = node.getNodeData();
        if (nodeData == null) {
            return null;
        }

        return nodeData.getNodeActivity();
    }

    @Override
    public NodeCondition getNodeCondition(Long nodeId) {

        Node node = this.getById(nodeId);
        if (ObjectUtil.isEmpty(node)) {
            return null;
        }

        NodeData nodeData = node.getNodeData();
        if (nodeData == null) {
            return null;
        }

        return nodeData.getNodeCondition();
    }

    @Override
    public NodeSubFlow getNodeSubFlowDetail(Long nodeId) {

        Node node = this.getById(nodeId);
        if (ObjectUtil.isEmpty(node)) {
            return null;
        }

        NodeData nodeData = node.getNodeData();
        if (nodeData == null) {
            return null;
        }

        return nodeData.getNodeSubFlow();
    }
}