#include <library/cpp/testing/unittest/registar.h>

#include <wmconsole/version3/wmcutil/yt/yt_utils.h>

using namespace NWebmaster;

struct TNodeSamples {
public:
    TNodeSamples() {
        STRING_NODE = NYT::TNode("test string");
        I64_NODE = NYT::TNode((i64) -123);
        UI64_NODE = NYT::TNode((ui64) 321);
        DOUBLE_NODE = NYT::TNode(0.125);
        BOOL_NODE = NYT::TNode(true);
        ENTITY_NODE = NYT::TNode::CreateEntity();

        LIST_NODE = NYT::TNode::CreateList();
        MAP_NODE = NYT::TNode::CreateMap();

        LIST_NODE.Add(STRING_NODE);
        MAP_NODE["string"] = STRING_NODE;

        LIST_NODE.Add(I64_NODE);
        MAP_NODE["i64"] = I64_NODE;

        LIST_NODE.Add(UI64_NODE);
        MAP_NODE["ui64"] = UI64_NODE;

        LIST_NODE.Add(DOUBLE_NODE);
        MAP_NODE["double"] = DOUBLE_NODE;

        LIST_NODE.Add(BOOL_NODE);
        MAP_NODE["bool"] = BOOL_NODE;

        LIST_NODE.Add(ENTITY_NODE);
        MAP_NODE["entity"] = ENTITY_NODE;

        NYT::TNode intListNode = NYT::TNode::CreateList();
        NYT::TNode intMapNode = NYT::TNode::CreateMap();

        LIST_NODE.Add(intListNode);
        MAP_NODE["list"] = intListNode;

        LIST_NODE.Add(intMapNode);
        MAP_NODE["map"] = intMapNode;
    }

public:
    NYT::TNode STRING_NODE ;
    NYT::TNode I64_NODE;
    NYT::TNode UI64_NODE;
    NYT::TNode DOUBLE_NODE;
    NYT::TNode BOOL_NODE;
    NYT::TNode ENTITY_NODE;
    NYT::TNode LIST_NODE;
    NYT::TNode MAP_NODE;
};

using namespace NYTUtils;

struct TReaderMockImpl : public NYT::INodeReaderImpl {
public:
    TReaderMockImpl(const NYT::TNode &value)
        : Value(value)
    {
    }

    virtual const NYT::TNode &GetRow() const override {
        return Value;
    }

    virtual bool IsValid() const override {
        return Valid;
    }

    virtual void Next() override {
        Valid = false;
    }

    virtual ui32 GetTableIndex() const override {
        return 0;
    }

    virtual ui32 GetRangeIndex() const override {
        return 0;
    }

    virtual ui64 GetRowIndex() const override {
        return 0;
    }

    virtual void NextKey() override {
        Next();
    }

    virtual void MoveRow(NYT::TNode* result) override {
        *result = std::move(Value);
        Value.Clear();
        Valid = false;
    }

private:
    bool Valid = true;
    NYT::TNode Value;
};

Y_UNIT_TEST_SUITE(TYTUtilTest) {
    Y_UNIT_TEST(TestFromNode) {
        const TNodeSamples samples;

        UNIT_ASSERT_EQUAL(FromNode<TString>(samples.STRING_NODE), samples.STRING_NODE.AsString());
        UNIT_ASSERT_EQUAL(FromNode<i64>(samples.I64_NODE), samples.I64_NODE.AsInt64());
        UNIT_ASSERT_EQUAL(FromNode<ui64>(samples.UI64_NODE), samples.UI64_NODE.AsUint64());
        UNIT_ASSERT_EQUAL(FromNode<double>(samples.DOUBLE_NODE), samples.DOUBLE_NODE.AsDouble());
        UNIT_ASSERT_EQUAL(FromNode<bool>(samples.BOOL_NODE), samples.BOOL_NODE.AsBool());
        UNIT_ASSERT_EQUAL(FromNode<NYT::TNode::TListType>(samples.LIST_NODE), samples.LIST_NODE.AsList());
        UNIT_ASSERT_EQUAL(FromNode<NYT::TNode::TMapType>(samples.MAP_NODE), samples.MAP_NODE.AsMap());
        UNIT_ASSERT_EQUAL(FromNode<NYT::TNode>(samples.MAP_NODE), samples.MAP_NODE);

        // FromNodeOrDefault
        UNIT_ASSERT_EQUAL(FromNodeOrDefault<TString>(samples.STRING_NODE, "def"), samples.STRING_NODE.AsString());
        UNIT_ASSERT_EQUAL(FromNodeOrDefault<TString>(samples.ENTITY_NODE, "def"), "def");

        UNIT_ASSERT_EQUAL(FromNodeOrDefault<ui64>(samples.UI64_NODE, 15), samples.UI64_NODE.AsUint64());
        UNIT_ASSERT_EQUAL(FromNodeOrDefault<ui64>(samples.ENTITY_NODE, 15), 15);
    }

    Y_UNIT_TEST(TestGetNodeField) {
        const TNodeSamples samples;
        const NYT::TNode MAP_NODE = samples.MAP_NODE;

        UNIT_ASSERT_EQUAL(GetNodeField<TString>(MAP_NODE, "string"), samples.STRING_NODE.AsString());
        UNIT_ASSERT_EQUAL(GetNodeField<double>(MAP_NODE, "double"), samples.DOUBLE_NODE.AsDouble());

        // GetNodeFieldOrDefault
        UNIT_ASSERT_EQUAL(GetNodeFieldOrDefault<TString>(MAP_NODE, "string", "def"), samples.STRING_NODE.AsString());
        UNIT_ASSERT_EQUAL(GetNodeFieldOrDefault<TString>(MAP_NODE, "entity", "def"), "def");
        UNIT_ASSERT_EQUAL(GetNodeFieldOrDefault<TString>(MAP_NODE, "undefined", "def"), "def");

        UNIT_ASSERT_EQUAL(GetNodeFieldOrDefault<double>(MAP_NODE, "double", 2.5), samples.DOUBLE_NODE.AsDouble());
        UNIT_ASSERT_EQUAL(GetNodeFieldOrDefault<double>(MAP_NODE, "entity", 2.5), 2.5);
        UNIT_ASSERT_EQUAL(GetNodeFieldOrDefault<double>(MAP_NODE, "undefined", 2.5), 2.5);
    }


    Y_UNIT_TEST(TestGetRowField) {
        const TNodeSamples samples;

        NYT::TTableReader<NYT::TNode> reader(new TReaderMockImpl(samples.MAP_NODE));

        UNIT_ASSERT_EQUAL(GetRowField<TString>(&reader, "string"), samples.STRING_NODE.AsString());
        UNIT_ASSERT_EQUAL(GetRowField<double>(&reader, "double"), samples.DOUBLE_NODE.AsDouble());

        // GetNodeFieldOrDefault
        UNIT_ASSERT_EQUAL(GetRowFieldOrDefault<TString>(&reader, "string", "def"), samples.STRING_NODE.AsString());
        UNIT_ASSERT_EQUAL(GetRowFieldOrDefault<TString>(&reader, "entity", "def"), "def");
        UNIT_ASSERT_EQUAL(GetRowFieldOrDefault<TString>(&reader, "undefined", "def"), "def");

        UNIT_ASSERT_EQUAL(GetRowFieldOrDefault<double>(&reader, "double", 2.5), samples.DOUBLE_NODE.AsDouble());
        UNIT_ASSERT_EQUAL(GetRowFieldOrDefault<double>(&reader, "entity", 2.5), 2.5);
        UNIT_ASSERT_EQUAL(GetRowFieldOrDefault<double>(&reader, "undefined", 2.5), 2.5);
    };
}
