#include <cstdlib>
#include <iostream>
#include <string_view>

#include "backends/asio_native_dsd_backend.h"

#ifndef APTT_ENABLE_ASIO
#define APTT_ENABLE_ASIO 0
#endif

namespace {

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

aptt::PlaybackConfig NativeDsdPlaybackConfig() {
  aptt::NativeDsdConfig dsd;
  dsd.dsd_rate_multiplier = 64;
  dsd.channel_count = 2;
  dsd.sample_type = aptt::SampleType::kDsd1BitLsbFirst;

  aptt::PlaybackConfig config;
  config.backend = aptt::BackendApi::kAsio;
  config.device.backend = aptt::BackendApi::kAsio;
  config.device.display_name = "Fixture ASIO DSD Driver";
  config.device.driver_name = "Fixture ASIO DSD Driver";
  config.device.stable_id = "asio:{01234567-89AB-CDEF-0123-456789ABCDEF}";
  config.device.is_known_problem_device = true;
  config.format = aptt::MakeNativeDsdFormat(dsd);
  config.hold_mode = aptt::HoldMode::kKeepWarmAfterStop;
  return config;
}

void TestPolicyIsConservative() {
  const auto policy = aptt::MakeAsioNativeDsdPolicy(
      NativeDsdPlaybackConfig());

  Expect(policy.requires_explicit_driver,
         "native DSD requires explicit ASIO driver");
  Expect(policy.keep_warm_after_signal,
         "native DSD defaults to keep-warm");
  Expect(!policy.release_after_drain,
         "native DSD does not default to release-after-drain");
  Expect(!policy.opens_control_panel,
         "native DSD never opens ASIO control panel automatically");
  Expect(!policy.uses_repeated_asio_exit,
         "native DSD avoids repeated ASIOExit loops");
  Expect(policy.uses_valid_dsd_idle,
         "native DSD keep-warm uses valid DSD idle");
}

void TestFormatGateRequiresNativeDsd() {
  auto config = NativeDsdPlaybackConfig();
  Expect(aptt::IsAsioNativeDsdPlaybackFormat(config.format),
         "native DSD format is accepted");

  auto dsd8 = config.format;
  dsd8.sample_type = aptt::SampleType::kDsd8Bit;
  dsd8.valid_bits_per_sample = 8;
  dsd8.container_bits_per_sample = 8;
  Expect(aptt::IsAsioNativeDsdPlaybackFormat(dsd8),
         "native DSD NER8 format is accepted explicitly");

  auto pcm = config.format;
  pcm.signal_mode = aptt::SignalMode::kPcm;
  Expect(!aptt::IsAsioNativeDsdPlaybackFormat(pcm),
         "PCM is rejected by native DSD gate");

  auto high_rate = config.format;
  high_rate.dsd_rate_multiplier = 512;
  high_rate.sample_rate_hz = 44100 * 512;
  Expect(aptt::IsAsioNativeDsdPlaybackFormat(high_rate),
         "DSD512 native DSD rate is accepted");

  auto bad_rate = config.format;
  bad_rate.dsd_rate_multiplier = 4096;
  bad_rate.sample_rate_hz = 44100 * 4096;
  Expect(!aptt::IsAsioNativeDsdPlaybackFormat(bad_rate),
         "unsupported native DSD rate is rejected");
}

void TestPreparedEventLogsDsdFields() {
  const auto event = aptt::MakeAsioNativeDsdPreparedEvent(
      3,
      "asio:{fixture}",
      NativeDsdPlaybackConfig().format,
      512);

  Expect(event.backend == aptt::BackendApi::kAsio,
         "native DSD prepared event records ASIO backend");
  Expect(event.backend_fields.at("signal_mode") == "ASIO Native DSD",
         "native DSD prepared event logs signal mode");
  Expect(event.backend_fields.at("native_dsd_sample_type") ==
             "ASIOSTDSDInt8LSB1",
         "native DSD prepared event logs sample type");
  Expect(event.backend_fields.at("native_dsd_byte_order") == "lsb-first",
         "native DSD prepared event logs byte order");
  Expect(event.backend_fields.at("native_dsd_rate_multiplier") == "64",
         "native DSD prepared event logs DSD rate");
  Expect(event.backend_fields.at("buffer_bytes_per_channel") == "512",
         "native DSD prepared event logs buffer bytes");
  Expect(event.backend_fields.at("keep_alive_format") == "valid-dsd-idle",
         "native DSD prepared event logs valid idle keep-warm");
}

void TestRuntimeEventLogsIdleBuffer() {
  aptt::RealtimeStats stats;
  stats.callback_count = 2;
  const auto event = aptt::MakeAsioNativeDsdRuntimeEvent(
      4,
      "asio:{fixture}",
      stats,
      1,
      512,
      true);

  Expect(event.type == aptt::DiagnosticEventType::kRuntimeStats,
         "native DSD runtime event is runtime stats");
  Expect(event.backend_fields.at("buffer_index") == "1",
         "native DSD runtime event logs buffer index");
  Expect(event.backend_fields.at("buffer_bytes_per_channel") == "512",
         "native DSD runtime event logs byte count");
  Expect(event.backend_fields.at("idle_buffer") == "true",
         "native DSD runtime event logs idle buffer");
}

void TestDisabledBuildFailsBeforeDriverReservation() {
#if !APTT_ENABLE_ASIO
  aptt::AsioNativeDsdBackend backend;
  const auto result = backend.initialize(NativeDsdPlaybackConfig());

  Expect(!result.ok,
         "native DSD initialize fails when ASIO build gate is OFF");
  Expect(result.error.message.find("ENABLE_ASIO is OFF") != std::string::npos,
         "native DSD disabled failure explains build gate");
  Expect(backend.lifecycle_state() == aptt::DeviceLifecycleState::kDiscovered,
         "native DSD disabled initialize does not reserve driver");
  Expect(backend.diagnostic_events().empty(),
         "native DSD disabled initialize emits no driver lifecycle events");

  const auto probe = backend.probeFormats(NativeDsdPlaybackConfig().device);
  Expect(probe.supported_formats.empty(),
         "native DSD disabled probe returns no supported formats");
  Expect(!probe.rejection_reasons.empty(),
         "native DSD disabled probe explains rejection");
#endif
}

void TestResetIsDisabled() {
  aptt::AsioNativeDsdBackend backend;
  const auto result = backend.reset();

  Expect(!result.ok, "native DSD reset is disabled");
  Expect(result.error.message.find("valid DSD idle") != std::string::npos,
         "native DSD reset failure names idle keep-warm");
}

}  // namespace

int main() {
  TestPolicyIsConservative();
  TestFormatGateRequiresNativeDsd();
  TestPreparedEventLogsDsdFields();
  TestRuntimeEventLogsIdleBuffer();
  TestDisabledBuildFailsBeforeDriverReservation();
  TestResetIsDisabled();

  std::cout << "ASIO native DSD backend tests passed\n";
  return 0;
}
