#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base_test.hpp"
#include "expression/pqp_column_expression.hpp"
#include "operators/projection.hpp"
#include "operators/table_wrapper.hpp"
#include "operators/union_all.hpp"
#include "storage/table.hpp"
#include "types.hpp"

namespace hyrise {
class OperatorsUnionAllTest : public BaseTest {
 protected:
  void SetUp() override {
    _table_wrapper_a =
        std::make_shared<TableWrapper>(load_table("resources/test_data/tbl/int_float.tbl", ChunkOffset{2}));

    _table_wrapper_b =
        std::make_shared<TableWrapper>(load_table("resources/test_data/tbl/int_float2.tbl", ChunkOffset{2}));

    _table_wrapper_c =
        std::make_shared<TableWrapper>(load_table("resources/test_data/tbl/int_int.tbl", ChunkOffset{2}));

    execute_all({_table_wrapper_a, _table_wrapper_b, _table_wrapper_c});
  }

  std::shared_ptr<TableWrapper> _table_wrapper_a;
  std::shared_ptr<TableWrapper> _table_wrapper_b;
  std::shared_ptr<TableWrapper> _table_wrapper_c;
};

TEST_F(OperatorsUnionAllTest, UnionOfValueTables) {
  std::shared_ptr<Table> expected_result = load_table("resources/test_data/tbl/int_float_union.tbl", ChunkOffset{2});

  auto union_all = std::make_shared<UnionAll>(_table_wrapper_a, _table_wrapper_b);
  union_all->execute();

  EXPECT_TABLE_EQ_UNORDERED(union_all->get_output(), expected_result);
}

TEST_F(OperatorsUnionAllTest, UnionOfValueReferenceTables) {
  std::shared_ptr<Table> expected_result = load_table("resources/test_data/tbl/int_float_union.tbl", ChunkOffset{2});

  const auto a = PQPColumnExpression::from_table(*_table_wrapper_a->get_output(), "a");
  const auto b = PQPColumnExpression::from_table(*_table_wrapper_a->get_output(), "b");

  auto projection =
      std::make_shared<Projection>(_table_wrapper_a, std::vector<std::shared_ptr<AbstractExpression>>{{a, b}});
  projection->execute();

  auto union_all = std::make_shared<UnionAll>(projection, _table_wrapper_b);
  union_all->execute();

  EXPECT_TABLE_EQ_UNORDERED(union_all->get_output(), expected_result);
}

TEST_F(OperatorsUnionAllTest, ThrowWrongColumnNumberException) {
  if constexpr (!HYRISE_DEBUG) {
    GTEST_SKIP();
  }

  std::shared_ptr<Table> test_table_c = load_table("resources/test_data/tbl/int.tbl", ChunkOffset{2});
  auto gt_c = std::make_shared<TableWrapper>(std::move(test_table_c));
  gt_c->execute();

  auto union_all = std::make_shared<UnionAll>(_table_wrapper_a, gt_c);

  EXPECT_THROW(union_all->execute(), std::logic_error);
}

TEST_F(OperatorsUnionAllTest, ThrowWrongColumnOrderException) {
  if constexpr (!HYRISE_DEBUG) {
    GTEST_SKIP();
  }

  std::shared_ptr<Table> test_table_d = load_table("resources/test_data/tbl/float_int.tbl", ChunkOffset{2});
  auto gt_d = std::make_shared<TableWrapper>(std::move(test_table_d));
  gt_d->execute();

  auto union_all = std::make_shared<UnionAll>(_table_wrapper_a, gt_d);

  EXPECT_THROW(union_all->execute(), std::logic_error);
}

TEST_F(OperatorsUnionAllTest, ThrowWrongColumnDefinitions) {
  auto union_all = std::make_shared<UnionAll>(_table_wrapper_a, _table_wrapper_c);

  EXPECT_THROW(union_all->execute(), std::logic_error);
}

}  // namespace hyrise
