#include "mission/mission_store.hpp"
#include "storage/database.hpp"
#include "util/file_util.hpp"

#include <gtest/gtest.h>

#include <atomic>
#include <filesystem>
#include <unistd.h>

namespace {

class MissionStoreTest : public ::testing::Test
{
protected:
  void SetUp() override
  {
    static std::atomic<unsigned> seq{0};
    dir_ = std::filesystem::temp_directory_path()
           / ("lm_test_" + std::to_string(getpid()) + "_"
              + std::to_string(seq.fetch_add(1)));
    std::filesystem::create_directories(dir_);
    db_ = std::make_unique<lm::Database>(dir_);
    std::string err;
    ASSERT_TRUE(db_->init(err)) << err;

    const auto fixture = std::filesystem::path(TEST_FIXTURE_DIR) / "missions.json";
    const auto raw = lm::FileUtil::readBinary(fixture);
    auto doc = nlohmann::json::parse(raw);
    ASSERT_TRUE(db_->setDocument("missions", doc));
    store_ = std::make_unique<lm::MissionStore>(*db_);
  }

  void TearDown() override
  {
    store_.reset();
    db_.reset();
    std::error_code ec;
    std::filesystem::remove_all(dir_, ec);
  }

  std::filesystem::path dir_;
  std::unique_ptr<lm::Database> db_;
  std::unique_ptr<lm::MissionStore> store_;
};

}  // namespace

TEST_F(MissionStoreTest, FindMissionFromFixture)
{
  const auto mission = store_->findMission("testmission00001");
  ASSERT_TRUE(mission.has_value());
  EXPECT_EQ((*mission)["name"], "Test Wait");
}

TEST_F(MissionStoreTest, AddTriggerValidCoil)
{
  std::string err;
  const auto trigger = store_->addTrigger(
      {{"name", "PLC line 1"}, {"coil_id", 1001}, {"mission_id", "testmission00001"}}, err);
  ASSERT_TRUE(trigger.has_value()) << err;
  EXPECT_EQ((*trigger)["coil_id"], 1001);
}

TEST_F(MissionStoreTest, AddTriggerRejectsInvalidCoil)
{
  std::string err;
  const auto trigger =
      store_->addTrigger({{"name", "bad"}, {"coil_id", 999}, {"mission_id", "testmission00001"}}, err);
  EXPECT_FALSE(trigger.has_value());
  EXPECT_FALSE(err.empty());
}

TEST_F(MissionStoreTest, AddTriggerRejectsDuplicateCoil)
{
  std::string err;
  ASSERT_TRUE(store_->addTrigger(
                  {{"name", "first"}, {"coil_id", 1002}, {"mission_id", "testmission00001"}}, err)
                  .has_value())
      << err;
  const auto dup = store_->addTrigger(
      {{"name", "second"}, {"coil_id", 1002}, {"mission_id", "testmission00001"}}, err);
  EXPECT_FALSE(dup.has_value());
}

TEST_F(MissionStoreTest, DeleteTrigger)
{
  std::string err;
  const auto trigger = store_->addTrigger(
      {{"name", "tmp"}, {"coil_id", 1003}, {"mission_id", "testmission00001"}}, err);
  ASSERT_TRUE(trigger.has_value()) << err;
  const std::string id = (*trigger)["id"].get<std::string>();
  EXPECT_TRUE(store_->deleteTrigger(id, err)) << err;
  EXPECT_FALSE(store_->findTriggerByCoil(1003).has_value());
}

TEST_F(MissionStoreTest, AddScheduleAsap)
{
  std::string err;
  const auto schedule = store_->addSchedule(
      {{"name", "Morning run"}, {"mission_id", "testmission00001"}, {"priority", 5}, {"start_mode", "asap"}},
      err);
  ASSERT_TRUE(schedule.has_value()) << err;
  EXPECT_EQ((*schedule)["priority"], 5);
  EXPECT_EQ((*schedule)["start_mode"], "asap");
}

TEST_F(MissionStoreTest, AddScheduleUnknownMissionFails)
{
  std::string err;
  const auto schedule =
      store_->addSchedule({{"name", "bad"}, {"mission_id", "missing"}, {"start_mode", "asap"}}, err);
  EXPECT_FALSE(schedule.has_value());
}
