#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>

#include <language/utils/OFStream.hpp>
#include <utils/Messenger.hpp>
#include <utils/Stringify.hpp>

#include <filesystem>

#ifdef __linux__
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#endif   // __linux__

// clazy:excludeall=non-pod-global-static

TEST_CASE("OFStream", "[language]")
{
  SECTION("ofstream")
  {
    const std::string basename =
      std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("ofstream_dir").append("ofstream_");
    const std::string filename = basename + stringify(parallel::rank());

    // Ensures that the file is closed after this line
    std::make_shared<OFStream>(filename) << "foo" << 3 << " bar\n";

    if (parallel::rank() == 0) {
      REQUIRE(std::filesystem::is_regular_file(filename));

      std::ifstream is(filename);

      char file_content[10];
      for (size_t i = 0; i < 10; ++i) {
        char c = is.get();

        file_content[i] = c;
        if (c == '\n') {
          file_content[i + 1] = '\0';
          REQUIRE(i == 8);

          c = is.get();
          REQUIRE(is.eof());
          break;
        }
      }

      std::string content = file_content;
      REQUIRE(content == "foo3 bar\n");

      std::filesystem::remove(filename);
    }

    REQUIRE(not std::filesystem::exists(filename));
  }

#ifdef __linux__
  SECTION("invalid filename")
  {
    if (parallel::rank() == 0) {
      const std::string filename      = "tests/badpath/invalidpath/ofstream";
      std::filesystem::path directory = std::filesystem::path{filename}.parent_path();
      std::filesystem::create_directories(directory);
      std::filesystem::permissions(directory,
                                   std::filesystem::perms::owner_all | std::filesystem::perms::group_all |
                                     std::filesystem::perms::others_all,
                                   std::filesystem::perm_options::remove);

      int fd    = open("tests/badpath/invalidpath", O_DIRECTORY);
      int flags = FS_IMMUTABLE_FL;
      ioctl(fd, FS_IOC_SETFLAGS, &flags);
      REQUIRE_THROWS_WITH(std::make_shared<OFStream>(filename), "error: cannot create file " + filename);

      flags = 0;
      ioctl(fd, FS_IOC_SETFLAGS, &flags);

      std::filesystem::permissions(directory,
                                   std::filesystem::perms::owner_all | std::filesystem::perms::group_all |
                                     std::filesystem::perms::others_all,
                                   std::filesystem::perm_options::add);
      std::filesystem::remove_all("tests/badpath");
    }
  }
#endif   // __linux__
}