/*
 * This file is a part of the LinkAhead Project.
 *
 * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
 * Copyright (C) 2022 Timm Fitschen <t.fitschen@indiscale.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 *
 */
#include "linkahead/entity.h"             // for Entity, Property
#include "linkahead/connection.h"         // for Connection, Connec...
#include "linkahead/message_code.h"       // for ENTITY_DOES_NOT_EXIST, Messag...
#include "linkahead/status_code.h"        // for StatusCode, SUCCESS
#include "linkahead/transaction.h"        // for Entity, Transaction
#include "linkahead/transaction_status.h" // for TransactionStatus
#include <chrono>                         // for operator""ms, chrono_literals
#include <gtest/gtest-message.h>          // for Message
#include <gtest/gtest-test-part.h>        // for TestPartResult
#include <gtest/gtest_pred_impl.h>        // for AssertionResult
#include <memory>                         // for allocator, unique_ptr, __shar...
#include <thread>

namespace linkahead::transaction {
using linkahead::entity::MessageCode;

/*
 * Test the retrieval of a non-existing entity
 *
 * The transaction is being executed and we can retrieve the state in the mean
 * time.
 */
TEST(test_async, retrieve_non_existing) {
  using namespace std::chrono_literals;
  const auto &connection = linkahead::connection::ConnectionManager::GetDefaultConnection();

  auto transaction(connection->CreateTransaction());

  const auto *id = "non-existing-id";
  transaction->RetrieveById(id);
  transaction->ExecuteAsynchronously();

  auto status = transaction->GetStatus();
  EXPECT_EQ(status.GetCode(), StatusCode::EXECUTING);

  // 1000ms are not always sufficient, when there is very high pipeline load.
  auto count = 1000;
  auto code = transaction->GetStatus().GetCode();
  do {
    // wait some time
    std::this_thread::sleep_for(10ms);

    // DONT call WaitForIt -> the transaction finishes in the background
    code = transaction->GetStatus().GetCode();
  } while (--count > 0 && code == StatusCode::EXECUTING);
  ASSERT_GT(count, 0) << "ERROR: Timeout while waiting for transaction.";
  EXPECT_EQ(code, TransactionStatus::TRANSACTION_ERROR().GetCode());
  ASSERT_EQ(code, StatusCode::GENERIC_TRANSACTION_ERROR);

  const auto &result_set = transaction->GetResultSet();

  const auto &entity = result_set.at(0);
  EXPECT_EQ(id, entity.GetId());
  EXPECT_TRUE(entity.HasErrors());
  ASSERT_EQ(entity.GetErrors().size(), 1);
  EXPECT_EQ(entity.GetErrors().at(0).GetCode(), MessageCode::ENTITY_DOES_NOT_EXIST);
}

} // namespace linkahead::transaction