#include <doctest/doctest.h>

#include <filesystem>
#include <fstream>

#include "torlinkc/config/paths.hpp"
#include "torlinkc/engine/persist.hpp"
#include "test_support.hpp"

using namespace torlinkc;
using torlinkc::test::StateDirSandbox;
namespace fs = std::filesystem;

TEST_CASE("torrentExportName strips filesystem-hostile characters") {
  CHECK(torrentExportName("My/Movie:2024", "id1") == "My Movie 2024.torrent");
}

TEST_CASE("torrentExportName collapses whitespace and trims trailing dots/spaces") {
  CHECK(torrentExportName("Weird   Name...  ", "id1") == "Weird Name.torrent");
}

TEST_CASE("torrentExportName falls back to the id when the name is empty after sanitizing") {
  CHECK(torrentExportName("   ", "abc123") == "abc123.torrent");
}

TEST_CASE("torrentExportName caps the length at 180 characters") {
  const std::string longName(300, 'x');
  auto out = torrentExportName(longName, "id1");
  // 180 chars of name + ".torrent"
  CHECK(out.size() == 180 + std::string(".torrent").size());
}

TEST_CASE("saveQueue/loadQueue round-trips through an atomic write") {
  StateDirSandbox sandbox;

  QueueItem it;
  it.id = "abc";
  it.name = "Test Torrent";
  it.magnet = "magnet:?xt=urn:btih:" + std::string(40, 'a');
  it.dir = "/tmp/downloads";
  it.status = DownloadStatus::Paused;
  it.progress = 42;
  it.totalBytes = 1000;
  it.downloadedBytes = 420;
  it.eta = 12.5;
  it.error = "boom";
  it.addedAt = 1234;

  saveQueue({it});
  auto loaded = loadQueue();
  REQUIRE(loaded.size() == 1);
  CHECK(loaded[0].id == "abc");
  CHECK(loaded[0].name == "Test Torrent");
  CHECK(loaded[0].status == DownloadStatus::Paused);
  CHECK(loaded[0].progress == 42);
  CHECK(loaded[0].totalBytes == 1000);
  CHECK(loaded[0].downloadedBytes == 420);
  REQUIRE(loaded[0].eta.has_value());
  CHECK(*loaded[0].eta == doctest::Approx(12.5));
  REQUIRE(loaded[0].error.has_value());
  CHECK(*loaded[0].error == "boom");
  CHECK(loaded[0].addedAt == 1234);
}

TEST_CASE("loadQueue returns an empty vector when no file exists") {
  StateDirSandbox sandbox;
  CHECK(loadQueue().empty());
}

TEST_CASE("saveSeeds/loadSeeds round-trips") {
  StateDirSandbox sandbox;
  saveSeeds({SeedRecord{"s1", SeedStatus::Seeding}, SeedRecord{"s2", SeedStatus::Paused}});
  auto loaded = loadSeeds();
  REQUIRE(loaded.size() == 2);
  CHECK(loaded[0].id == "s1");
  CHECK(loaded[0].status == SeedStatus::Seeding);
  CHECK(loaded[1].id == "s2");
  CHECK(loaded[1].status == SeedStatus::Paused);
}

TEST_CASE("loadSeeds accepts the legacy bare-id-array format as all-seeding") {
  StateDirSandbox sandbox;
  // Write the legacy format directly, matching what an old torlink install
  // could have left on disk.
  fs::create_directories(fs::path(paths::seedsFile()).parent_path());
  std::ofstream(paths::seedsFile()) << R"(["legacy1","legacy2"])";
  auto loaded = loadSeeds();
  REQUIRE(loaded.size() == 2);
  CHECK(loaded[0].status == SeedStatus::Seeding);
  CHECK(loaded[1].status == SeedStatus::Seeding);
}

TEST_CASE("torrent metadata cache: save, exists, export, delete") {
  StateDirSandbox sandbox;
  CHECK_FALSE(torrentMetaExists("t1"));

  saveTorrentMeta("t1", std::string("fake bencode bytes"));
  CHECK(torrentMetaExists("t1"));

  fs::path exportDir = fs::temp_directory_path() / "torlinkc-test-export";
  auto exported = exportTorrentMeta("t1", "My Torrent", exportDir.string());
  REQUIRE(exported.has_value());
  CHECK(fs::exists(*exported));
  fs::remove_all(exportDir);

  deleteTorrentMeta("t1");
  CHECK_FALSE(torrentMetaExists("t1"));
}
