#include <gtest/gtest.h>

#include "query_plan_common.hpp"

#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
#include "storage/v2/storage.hpp"

TEST(QueryPlan, CreateNodeWithAttributes) {
  storage::Storage db;
  auto dba = db.Access();

  auto label = storage::Label::FromInt(42);
  auto property = storage::Property::FromInt(1);

  query::AstStorage ast;
  query::SymbolTable symbol_table;

  query::plan::NodeCreationInfo node;
  node.symbol = symbol_table.CreateSymbol("n", true);
  node.labels.emplace_back(label);
  node.properties.emplace_back(property, ast.Create<PrimitiveLiteral>(42));

  query::plan::CreateNode create_node(nullptr, node);
  DbAccessor execution_dba(&dba);
  auto context = MakeContext(ast, symbol_table, &execution_dba);
  Frame frame(context.symbol_table.max_position());
  auto cursor = create_node.MakeCursor(utils::NewDeleteResource());
  int count = 0;
  while (cursor->Pull(frame, context)) {
    ++count;
    const auto &node_value = frame[node.symbol];
    EXPECT_EQ(node_value.type(), TypedValue::Type::Vertex);
    const auto &v = node_value.ValueVertex();
    EXPECT_TRUE(*v.HasLabel(storage::View::NEW, label));
    EXPECT_EQ(v.GetProperty(storage::View::NEW, property)->ValueInt(), 42);
    EXPECT_EQ(CountIterable(*v.InEdges(storage::View::NEW)), 0);
    EXPECT_EQ(CountIterable(*v.OutEdges(storage::View::NEW)), 0);
    // Invokes LOG(FATAL) instead of erroring out.
    // EXPECT_TRUE(v.HasLabel(label, storage::View::OLD).IsError());
  }
  EXPECT_EQ(count, 1);
}

TEST(QueryPlan, ScanAllEmpty) {
  query::AstStorage ast;
  query::SymbolTable symbol_table;
  storage::Storage db;
  auto dba = db.Access();
  DbAccessor execution_dba(&dba);
  auto node_symbol = symbol_table.CreateSymbol("n", true);
  {
    query::plan::ScanAll scan_all(nullptr, node_symbol, storage::View::OLD);
    auto context = MakeContext(ast, symbol_table, &execution_dba);
    Frame frame(context.symbol_table.max_position());
    auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
    int count = 0;
    while (cursor->Pull(frame, context)) ++count;
    EXPECT_EQ(count, 0);
  }
  {
    query::plan::ScanAll scan_all(nullptr, node_symbol, storage::View::NEW);
    auto context = MakeContext(ast, symbol_table, &execution_dba);
    Frame frame(context.symbol_table.max_position());
    auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
    int count = 0;
    while (cursor->Pull(frame, context)) ++count;
    EXPECT_EQ(count, 0);
  }
}

TEST(QueryPlan, ScanAll) {
  storage::Storage db;
  {
    auto dba = db.Access();
    for (int i = 0; i < 42; ++i) dba.CreateVertex();
    EXPECT_FALSE(dba.Commit().HasError());
  }
  query::AstStorage ast;
  query::SymbolTable symbol_table;
  auto dba = db.Access();
  DbAccessor execution_dba(&dba);
  auto node_symbol = symbol_table.CreateSymbol("n", true);
  query::plan::ScanAll scan_all(nullptr, node_symbol);
  auto context = MakeContext(ast, symbol_table, &execution_dba);
  Frame frame(context.symbol_table.max_position());
  auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
  int count = 0;
  while (cursor->Pull(frame, context)) ++count;
  EXPECT_EQ(count, 42);
}

TEST(QueryPlan, ScanAllByLabel) {
  storage::Storage db;
  auto label = db.NameToLabel("label");
  ASSERT_TRUE(db.CreateIndex(label));
  {
    auto dba = db.Access();
    // Add some unlabeled vertices
    for (int i = 0; i < 12; ++i) dba.CreateVertex();
    // Add labeled vertices
    for (int i = 0; i < 42; ++i) {
      auto v = dba.CreateVertex();
      ASSERT_TRUE(v.AddLabel(label).HasValue());
    }
    EXPECT_FALSE(dba.Commit().HasError());
  }
  auto dba = db.Access();
  query::AstStorage ast;
  query::SymbolTable symbol_table;
  auto node_symbol = symbol_table.CreateSymbol("n", true);
  DbAccessor execution_dba(&dba);
  query::plan::ScanAllByLabel scan_all(nullptr, node_symbol, label);
  auto context = MakeContext(ast, symbol_table, &execution_dba);
  Frame frame(context.symbol_table.max_position());
  auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
  int count = 0;
  while (cursor->Pull(frame, context)) ++count;
  EXPECT_EQ(count, 42);
}