#pragma once

#include "expression.h"

#include <balancer/serval/core/config.h>
#include <balancer/serval/mod/apphost/stream.pb.h>

#include <util/generic/map.h>
#include <util/generic/string.h>
#include <util/generic/vector.h>

namespace NSv::NAppHost {
    struct TNode {
        TString Name;
        // For http sources, the path to make a request to. For gRPC sources, the value
        // of the `Path` field in the `TRequest`.
        TString Handler;
        // For terminal nodes, the condition under which the node should be visited.
        // (Non-terminal nodes are visited iff there is at least one output.)
        TExpression Async;
        TVector<size_t /* edge id */> I;
        TVector<size_t /* edge id */> O;
        NSv::TNumber<ui64>& Visits;
        NSv::TNumber<ui64>& CriticalPath; // XXX maybe a histogram (buckets = graph duration)?
        NSv::THistogram& RequestSize;
        NSv::THistogram& ResponseSize;
        // An action that will transform the request into a response. If not specified,
        // the node is transparent, i.e. all input items are simply copied to the output.
        NSv::TAction Action;
        // Whether to emulate the gRPC protocol when constructing a request to this node.
        // (If the attached action is `proxy`, it should use h2c backends.)
        bool Grpc = false;
        // Whether the output of this node consists of items with the same source name
        // in the request. Input nodes never have an action attached.
        bool IsInput = false;
        // Whether evaluating this node is the goal of the graph, i.e. this is RESPONSE
        // or a node marked as `async`.
        bool IsTerminal = false;
        // Whether inputs should be sent when ready instead of being buffered until
        // all of them are.
        bool StreamIn = false;
        // Whether this node is used as an input to any other node or as a flag
        // in some edge expression. Can only be false for terminal nodes, naturally.
        bool NeedOutput = true;
    };

    struct TEdge {
        // Endpoint nodes of this edge. `Source` can be >= `Nodes.size()`, in which
        // case it's actually a direct embed with index `Source - Nodes.size()`.
        size_t Source;
        size_t Target;
        // A condition for transferring anything over this edge. If it evaluates
        // to false, only the completion notification is transmitted.
        TExpression Expr;
        // Source name override. Used for streaming into subgraphs, which separate
        // inputs into nodes.
        TString Rename;
        TVector<std::pair<TString /* item type */, TString /* rename */>> Types;
        enum { AllItems, FirstOfType, LastOfType } Filter;
        bool Negate = false;
    };

    struct TGraph {
        TMap<TString, size_t> Index;
        TMap<std::pair<size_t /* node */, TString /* item */>, size_t> ItemIndex;
        TVector<TVector<size_t /* edge referencing this item */>> Items;
        TVector<TNode> Nodes;
        TVector<TEdge> Edges;
        TVector<TResponse> Embeds;
        size_t ResponseId;

        // Parse a graph description as a list of nodes.
        //
        //   - INPUT
        //   - TRANSPARENT:
        //     - INPUT->NODE_RENAME@-type_exception
        //   - TRANSFORMING:
        //     - ^TRANSPARENT@type_filter->type_rename: "INPUT[edge-condition]"
        //     action: argument
        //     option1: value1
        //     option2: value2
        //   - RESPONSE:
        //     - TRANSFORMING
        //
        TGraph(const YAML::Node&, NSv::TAuxData&);
    };
}
