#include <cstdlib>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <mutex>
#include <string>
#include <string_view>
#include <thread>
#include <vector>

#include "core/diagnostic_log.h"

namespace {

void Expect(bool condition, std::string_view message) {
  if (!condition) {
    std::cerr << "FAILED: " << message << "\n";
    std::exit(1);
  }
}

void TestLifecycleRoundtrip() {
  auto event = aptt::MakeLifecycleEvent(
      42,
      aptt::BackendApi::kWasapiExclusive,
      "endpoint-123",
      aptt::DeviceLifecycleState::kPrepared,
      aptt::DeviceLifecycleState::kRunningSignal,
      "started event-driven render stream");
  event.wall_time_utc = "2026-06-19T10:00:00Z";
  event.qpc_ticks = 123456789;
  event.backend_fields["hresult"] = "0x00000000";
  event.backend_fields["share_mode"] = "exclusive";

  const std::string line = aptt::SerializeDiagnosticEventJsonl(event);
  const auto parsed = aptt::ParseDiagnosticEventJsonl(line);

  Expect(parsed.has_value(), "lifecycle event parses");
  Expect(parsed->schema_version == aptt::kDiagnosticLogSchemaVersion,
         "schema version roundtrips");
  Expect(parsed->type == aptt::DiagnosticEventType::kLifecycleTransition,
         "event type roundtrips");
  Expect(parsed->sequence == 42, "sequence roundtrips");
  Expect(parsed->backend == aptt::BackendApi::kWasapiExclusive,
         "backend roundtrips");
  Expect(parsed->device_id == "endpoint-123", "device ID roundtrips");
  Expect(parsed->lifecycle_from == aptt::DeviceLifecycleState::kPrepared,
         "lifecycle from roundtrips");
  Expect(parsed->lifecycle_to == aptt::DeviceLifecycleState::kRunningSignal,
         "lifecycle to roundtrips");
  Expect(parsed->backend_fields.at("share_mode") == "exclusive",
         "backend field roundtrips");
}

void TestFormatAndStatsRoundtrip() {
  aptt::DiagnosticEvent event;
  event.type = aptt::DiagnosticEventType::kRuntimeStats;
  event.sequence = 7;
  event.backend = aptt::BackendApi::kNull;
  event.device_id = "null-device";
  event.message = "stats tick";

  aptt::FormatDescriptor format;
  format.sample_rate_hz = 48000;
  format.channels.channel_count = 2;
  event.format = format;

  aptt::RealtimeStats stats;
  stats.frames_generated = 48000;
  stats.frames_submitted = 48000;
  stats.callback_count = 100;
  stats.underrun_count = 0;
  event.realtime_stats = stats;
  event.backend_fields["buffer_model"] = "synthetic";

  const auto parsed = aptt::ParseDiagnosticEventJsonl(
      aptt::SerializeDiagnosticEventJsonl(event));

  Expect(parsed.has_value(), "runtime event parses");
  Expect(parsed->format.has_value(), "format is present after parse");
  Expect(parsed->format->sample_rate_hz == 48000, "sample rate roundtrips");
  Expect(parsed->format->channels.channel_count == 2, "channel count roundtrips");
  Expect(parsed->realtime_stats.has_value(), "stats are present after parse");
  Expect(parsed->realtime_stats->frames_generated == 48000,
         "frames generated roundtrips");
  Expect(parsed->realtime_stats->callback_count == 100,
         "callback count roundtrips");
  Expect(parsed->backend_fields.at("buffer_model") == "synthetic",
         "runtime backend field roundtrips");
}

void TestFixtureContainsLifecycleTransition() {
#ifndef APTT_SOURCE_DIR
#error "APTT_SOURCE_DIR must be defined"
#endif
  const std::filesystem::path fixture_path =
      std::filesystem::path(APTT_SOURCE_DIR) / "tests" / "fixtures" /
      "sample_session.jsonl";
  std::ifstream fixture(fixture_path);
  Expect(fixture.good(), "sample JSONL fixture exists");

  bool saw_lifecycle_transition = false;
  std::string line;
  while (std::getline(fixture, line)) {
    const auto event = aptt::ParseDiagnosticEventJsonl(line);
    if (event && event->type == aptt::DiagnosticEventType::kLifecycleTransition) {
      saw_lifecycle_transition = true;
      Expect(event->lifecycle_from.has_value(), "fixture lifecycle from exists");
      Expect(event->lifecycle_to.has_value(), "fixture lifecycle to exists");
    }
  }
  Expect(saw_lifecycle_transition, "fixture contains lifecycle transition");
}

void TestDiagnosticSequenceCounterIsThreadSafe() {
  aptt::DiagnosticSequenceCounter counter;
  std::mutex values_mutex;
  std::vector<std::uint64_t> values;
  constexpr int kThreadCount = 8;
  constexpr int kIterationsPerThread = 512;
  values.reserve(kThreadCount * kIterationsPerThread);

  std::vector<std::thread> threads;
  for (int thread_index = 0; thread_index < kThreadCount; ++thread_index) {
    threads.emplace_back([&] {
      std::vector<std::uint64_t> local_values;
      local_values.reserve(kIterationsPerThread);
      for (int i = 0; i < kIterationsPerThread; ++i) {
        local_values.push_back(counter.Next());
      }
      std::lock_guard lock(values_mutex);
      values.insert(values.end(), local_values.begin(), local_values.end());
    });
  }
  for (auto& thread : threads) {
    thread.join();
  }

  std::sort(values.begin(), values.end());
  Expect(values.size() == kThreadCount * kIterationsPerThread,
         "all concurrent sequence values were collected");
  for (std::size_t i = 0; i < values.size(); ++i) {
    Expect(values[i] == i + 1,
           "concurrent sequence counter produces a contiguous sequence");
  }
  Expect(counter.value() == values.size(),
         "sequence counter final value matches generated count");
}

}  // namespace

int main() {
  TestLifecycleRoundtrip();
  TestFormatAndStatsRoundtrip();
  TestFixtureContainsLifecycleTransition();
  TestDiagnosticSequenceCounterIsThreadSafe();

  std::cout << "diagnostic log tests passed\n";
  return 0;
}
