#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <set>
#include <string>
#include <string_view>
#include <vector>

#include "core/diagnostic_bundle.h"

namespace {

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

std::string ReadText(const std::filesystem::path& path) {
  std::ifstream file(path, std::ios::binary);
  return std::string((std::istreambuf_iterator<char>(file)),
                     std::istreambuf_iterator<char>());
}

aptt::DiagnosticBundleInput BuildInput() {
  aptt::DiagnosticBundleInput input;
  input.config.backend = aptt::BackendApi::kNull;
  input.config.duration_ms = 100;
  input.config.release_cooldown_ms = 1500;
  input.config.reacquire_cooldown_ms = 2500;
  input.session_id = "fixture-session";
  input.created_utc = "2026-06-19T00:00:00Z";
  input.device = aptt::DeviceInfo{
      .backend = aptt::BackendApi::kNull,
      .display_name = "Null Backend",
      .stable_id = "null-device",
      .driver_name = "synthetic",
      .is_default = true,
      .is_known_problem_device = false,
  };
  input.format_probe.requested = input.config.format;
  input.format_probe.supported_formats.push_back(input.config.format);
  auto device_snapshot = aptt::MakeDiscoveredDeviceSnapshot(
      input.device,
      aptt::DeviceAvailability::kActive);
  device_snapshot.backend_fields["fixture_device_field"] = "device-value";
  aptt::CapabilityProbeRecord probe;
  probe.kind = aptt::CapabilityProbeKind::kFormatSupport;
  probe.support = aptt::CapabilitySupport::kSupported;
  probe.requested_format = input.config.format;
  probe.accepted_format = input.config.format;
  probe.detail = "fixture accepted format";
  probe.backend_fields["fixture_probe_field"] = "probe-value";
  device_snapshot.probes.push_back(probe);
  aptt::DiscoverySnapshot snapshot;
  snapshot.capture_utc = input.created_utc;
  snapshot.backend = aptt::BackendApi::kNull;
  snapshot.devices.push_back(device_snapshot);
  input.discovery_snapshot = snapshot;
  input.final_stats.frames_generated = 4800;
  input.final_stats.frames_submitted = 4800;
  input.final_stats.frames_accepted = 4800;
  input.final_stats.callback_count = 19;
  input.events.push_back(aptt::MakeLifecycleEvent(
      1,
      aptt::BackendApi::kNull,
      "null-device",
      aptt::DeviceLifecycleState::kPrepared,
      aptt::DeviceLifecycleState::kRunningSignal,
      "synthetic run started"));
  input.pcm_summary = aptt::BuildPcmRunSummary(
      input.config.backend,
      input.device.stable_id,
      input.events,
      input.final_stats);
  input.summary = "NullBackend diagnostic bundle\n";
  input.build_metadata = aptt::MakeCurrentDiagnosticBuildMetadata();
  input.ui_log_lines.push_back("fixture UI log line");
  input.selected_condition_json =
      "{\n  \"fixture_selected_condition\": true\n}\n";
  return input;
}

void TestBundleWritesExpectedFiles() {
  const std::filesystem::path out =
      std::filesystem::current_path() / "diagnostic_bundle_test_output";
  std::filesystem::remove_all(out);

  const auto result = aptt::WriteDiagnosticBundleDirectory(out, BuildInput());
  Expect(result.ok, "bundle writer succeeds");

  const std::set<std::string> expected = {
      "bundle_manifest.json",
      "build_metadata.json",
      "config.json",
      "playback.jsonl",
      "realtime.csv",
      "release.json",
      "summary.csv",
      "pcm_summary.json",
      "device_dump.json",
      "device_snapshot.json",
      "format_probe.json",
      "init.log",
      "selected_condition.json",
      "ui_log.txt",
      "summary.txt",
  };
  for (const auto& name : expected) {
    Expect(std::filesystem::exists(out / name), name + " exists");
  }

  Expect(ReadText(out / "config.json").find("\"backend\": \"Null\"") !=
             std::string::npos,
         "config records backend");
  Expect(ReadText(out / "config.json").find("\"valid_bits_per_sample\"") !=
             std::string::npos,
         "config records complete format details");
  Expect(ReadText(out / "bundle_manifest.json").find("bundle_manifest.json") !=
             std::string::npos,
         "manifest lists bundle files");
  Expect(ReadText(out / "build_metadata.json").find("Audio Path Tone Tester") !=
             std::string::npos,
         "build metadata records app name");
  Expect(ReadText(out / "playback.jsonl").find("lifecycle_transition") !=
             std::string::npos,
         "playback JSONL records lifecycle");
  Expect(ReadText(out / "realtime.csv").find("4800,4800,4800") != std::string::npos,
         "realtime CSV records frames");
  Expect(ReadText(out / "pcm_summary.json").find("\"auto_backend_switch_attempted\": false") !=
             std::string::npos,
         "PCM summary records no backend auto-switch");
  Expect(ReadText(out / "summary.csv").find("frames_generated,4800") !=
             std::string::npos,
         "summary CSV records frame total");
  Expect(ReadText(out / "summary.txt").find("NullBackend") != std::string::npos,
         "summary records backend");
  Expect(ReadText(out / "init.log").find("session_id=fixture-session") !=
             std::string::npos,
         "init log records session identity");
  Expect(ReadText(out / "ui_log.txt").find("fixture UI log line") !=
             std::string::npos,
         "UI log is exported");
  Expect(ReadText(out / "selected_condition.json")
             .find("fixture_selected_condition") != std::string::npos,
         "selected condition evidence is exported");
}

void TestBundleConfigRecordsHighLoadDiagnostics() {
  const std::filesystem::path out =
      std::filesystem::current_path() / "diagnostic_bundle_high_load_output";
  std::filesystem::remove_all(out);

  auto input = BuildInput();
  input.config.high_load_diagnostics_enabled = true;
  input.config.high_load_risk_acknowledged = true;
  input.config.high_load_render_delay_ms = 45;
  input.config.high_load_delay_every_callbacks = 2;

  const auto result = aptt::WriteDiagnosticBundleDirectory(out, input);
  Expect(result.ok, "high-load bundle writer succeeds");

  const auto config_json = ReadText(out / "config.json");
  Expect(config_json.find("\"high_load_diagnostics_enabled\": true") !=
             std::string::npos,
         "config records high-load enabled state");
  Expect(config_json.find("\"high_load_risk_acknowledged\": true") !=
             std::string::npos,
         "config records high-load acknowledgement");
  Expect(config_json.find("\"high_load_mechanism\": \"bounded_render_callback_delay\"") !=
             std::string::npos,
         "config records high-load mechanism");
  Expect(config_json.find("\"high_load_render_delay_ms\": 45") !=
             std::string::npos,
         "config records high-load delay");
  Expect(config_json.find("\"high_load_delay_every_callbacks\": 2") !=
             std::string::npos,
         "config records high-load cadence");

  std::filesystem::remove_all(out);
}

void TestBundleRecordsReleaseOutcome() {
  const std::filesystem::path out =
      std::filesystem::current_path() / "diagnostic_bundle_release_output";
  std::filesystem::remove_all(out);

  auto input = BuildInput();
  input.release_attempted = true;
  input.release_ok = false;
  input.release_backend = "MME";
  input.release_status = "MME error";
  input.release_error = "MME drain timeout before release";
  input.release_stats = input.final_stats;
  input.release_stats.callback_count = 42;
  input.release_stats.underrun_count = 7;

  const auto result = aptt::WriteDiagnosticBundleDirectory(out, input);
  Expect(result.ok, "release outcome bundle writer succeeds");

  const auto release_json = ReadText(out / "release.json");
  Expect(release_json.find("\"release_attempted\": true") !=
             std::string::npos,
         "release JSON records attempted state");
  Expect(release_json.find("\"release_ok\": false") != std::string::npos,
         "release JSON records failure state");
  Expect(release_json.find("MME drain timeout before release") !=
             std::string::npos,
         "release JSON records release error");
  Expect(release_json.find("\"callback_count\": 42") != std::string::npos,
         "release JSON records final callback count");

  const auto summary_csv = ReadText(out / "summary.csv");
  Expect(summary_csv.find("release_attempted,true") != std::string::npos,
         "summary CSV records release attempted");
  Expect(summary_csv.find("release_underrun_count,7") != std::string::npos,
         "summary CSV records release underrun count");

  std::filesystem::remove_all(out);
}

void TestBundlePreservesBackendFieldsInDeviceSnapshot() {
  const std::filesystem::path out =
      std::filesystem::current_path() / "diagnostic_bundle_roundtrip_output";
  std::filesystem::remove_all(out);

  const auto result = aptt::WriteDiagnosticBundleDirectory(out, BuildInput());
  Expect(result.ok, "bundle writer succeeds for roundtrip test");

  const auto parsed = aptt::ParseDiscoverySnapshotJson(
      ReadText(out / "device_snapshot.json"));
  Expect(parsed.has_value(), "device snapshot parses");
  Expect(parsed->devices.size() == 1, "one device roundtrips");
  Expect(parsed->devices.front().backend_fields.at("fixture_device_field") ==
             "device-value",
         "device backend field roundtrips");
  Expect(parsed->devices.front().probes.front().backend_fields.at(
             "fixture_probe_field") == "probe-value",
         "probe backend field roundtrips");
}

void TestBundlePreservesKernelStreamingDopPlaybackFields() {
  const std::filesystem::path out =
      std::filesystem::current_path() / "diagnostic_bundle_ks_dop_output";
  std::filesystem::remove_all(out);

  auto input = BuildInput();
  input.config.backend = aptt::BackendApi::kKernelStreaming;
  input.device.backend = aptt::BackendApi::kKernelStreaming;
  input.device.display_name = "Fixture KS DoP pin";
  input.device.stable_id = "ks:\\\\?\\SWD#MMDEVAPI#{fixture-filter}#pin:3";
  input.events.clear();

  aptt::DiagnosticEvent event;
  event.type = aptt::DiagnosticEventType::kFormatProbe;
  event.sequence = 1;
  event.backend = aptt::BackendApi::kKernelStreaming;
  event.device_id = input.device.stable_id;
  event.message = "Kernel Streaming DoP carrier pin created and prepared";
  event.backend_fields["ks_filter_symbolic_link"] =
      "\\\\?\\SWD#MMDEVAPI#{fixture-filter}";
  event.backend_fields["ks_pin_id"] = "3";
  event.backend_fields["signal_mode"] = "DoP";
  event.backend_fields["keep_alive_format"] = "valid-dop-silence";
  event.backend_fields["dop_dsd_rate_multiplier"] = "64";
  event.backend_fields["dop_marker_first"] = "0x05";
  event.backend_fields["exact_carrier_required"] = "true";
  input.events.push_back(event);

  const auto result = aptt::WriteDiagnosticBundleDirectory(out, input);
  Expect(result.ok, "bundle writer succeeds for KS DoP fields");
  const auto playback = ReadText(out / "playback.jsonl");
  Expect(playback.find("ks_filter_symbolic_link") != std::string::npos,
         "playback log preserves KS filter identity");
  Expect(playback.find("valid-dop-silence") != std::string::npos,
         "playback log preserves KS DoP keep-alive field");
  Expect(playback.find("dop_dsd_rate_multiplier") != std::string::npos,
         "playback log preserves KS DoP multiplier");
  Expect(playback.find("exact_carrier_required") != std::string::npos,
         "playback log preserves KS exact carrier requirement");
}

void TestBundleRefusesSilentOverwrite() {
  const std::filesystem::path out =
      std::filesystem::current_path() / "diagnostic_bundle_overwrite_output";
  std::filesystem::remove_all(out);

  auto input = BuildInput();
  Expect(aptt::WriteDiagnosticBundleDirectory(out, input).ok,
         "first bundle write succeeds");
  const auto second = aptt::WriteDiagnosticBundleDirectory(out, input);
  Expect(!second.ok, "second bundle write is refused by default");
  Expect(second.error.message.find("refusing to overwrite") != std::string::npos,
         "overwrite refusal is explicit");

  input.allow_overwrite = true;
  Expect(aptt::WriteDiagnosticBundleDirectory(out, input).ok,
         "explicit overwrite is allowed");
}

void TestZipExportWritesBundleArchive() {
  const std::filesystem::path zip_path =
      std::filesystem::current_path() / "diagnostic_bundle_test_output.zip";
  std::filesystem::remove(zip_path);

  const auto result = aptt::WriteDiagnosticBundleZip(zip_path, BuildInput());
  Expect(result.ok, "zip bundle writer succeeds");
  const auto zip = ReadText(zip_path);
  Expect(zip.size() > 4, "zip output is non-empty");
  Expect(static_cast<unsigned char>(zip[0]) == 0x50 &&
             static_cast<unsigned char>(zip[1]) == 0x4B &&
             static_cast<unsigned char>(zip[2]) == 0x03 &&
             static_cast<unsigned char>(zip[3]) == 0x04,
         "zip starts with a local file header");
  Expect(zip.find("bundle_manifest.json") != std::string::npos,
         "zip contains manifest entry");
  Expect(zip.find("build_metadata.json") != std::string::npos,
         "zip contains build metadata entry");

  const auto second = aptt::WriteDiagnosticBundleZip(zip_path, BuildInput());
  Expect(!second.ok, "zip writer refuses overwrite by default");
}

void TestExpectedFileFixture() {
#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" /
      "diagnostic_bundle_expected_files.txt";
  std::ifstream fixture(fixture_path);
  Expect(fixture.good(), "expected-files fixture exists");

  std::set<std::string> names;
  std::string line;
  while (std::getline(fixture, line)) {
    if (!line.empty()) {
      names.insert(line);
    }
  }
  Expect(names.count("bundle_manifest.json") == 1, "fixture lists manifest");
  Expect(names.count("build_metadata.json") == 1,
         "fixture lists build metadata");
  Expect(names.count("config.json") == 1, "fixture lists config");
  Expect(names.count("playback.jsonl") == 1, "fixture lists playback");
  Expect(names.count("realtime.csv") == 1, "fixture lists realtime");
  Expect(names.count("summary.csv") == 1, "fixture lists summary CSV");
  Expect(names.count("pcm_summary.json") == 1, "fixture lists PCM summary");
  Expect(names.count("device_dump.json") == 1, "fixture lists device dump");
  Expect(names.count("device_snapshot.json") == 1,
         "fixture lists device snapshot");
  Expect(names.count("format_probe.json") == 1, "fixture lists format probe");
  Expect(names.count("init.log") == 1, "fixture lists init log");
  Expect(names.count("selected_condition.json") == 1,
         "fixture lists selected condition");
  Expect(names.count("ui_log.txt") == 1, "fixture lists UI log");
  Expect(names.count("summary.txt") == 1, "fixture lists summary");
}

}  // namespace

int main() {
  TestBundleWritesExpectedFiles();
  TestBundleConfigRecordsHighLoadDiagnostics();
  TestBundleRecordsReleaseOutcome();
  TestBundlePreservesBackendFieldsInDeviceSnapshot();
  TestBundlePreservesKernelStreamingDopPlaybackFields();
  TestBundleRefusesSilentOverwrite();
  TestZipExportWritesBundleArchive();
  TestExpectedFileFixture();

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