#include <cstdlib>
#include <cstdint>
#include <iostream>
#include <string_view>
#include <vector>

#include "core/audio_backend.h"
#include "core/audio_types.h"
#include "core/device_lifecycle.h"
#include "core/pcm_sample_conversion.h"

namespace {

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

void TestDefaultConfigIsConservative() {
  const aptt::PlaybackConfig config;
  Expect(config.backend == aptt::BackendApi::kNull, "default backend is Null");
  Expect(config.frequency_hz == 1000.0, "default frequency is 1 kHz");
  Expect(config.amplitude_dbfs == -20.0, "default amplitude is -20 dBFS");
  Expect(config.hold_mode == aptt::HoldMode::kKeepWarmAfterStop,
         "default hold mode keeps device warm");
  Expect(config.max_reacquire_attempts == 1,
         "default reacquire attempts are bounded");
}

void TestFormatDefaults() {
  const aptt::FormatDescriptor format;
  Expect(format.signal_mode == aptt::SignalMode::kPcm, "default signal is PCM");
  Expect(format.sample_rate_hz == 48000, "default sample rate is 48 kHz");
  Expect(format.sample_type == aptt::SampleType::kFloat32,
         "default sample type is float32");
  Expect(format.channels.channel_count == 2, "default channel count is stereo");
}

void TestLifecycleHappyPath() {
  using aptt::DeviceLifecycleState;
  const aptt::LifecyclePolicy policy;

  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kDiscovered,
                                  DeviceLifecycleState::kReserved, policy)
             .allowed,
         "discovered -> reserved");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kReserved,
                                  DeviceLifecycleState::kPrepared, policy)
             .allowed,
         "reserved -> prepared");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kPrepared,
                                  DeviceLifecycleState::kRunningSignal, policy)
             .allowed,
         "prepared -> running signal");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kRunningSignal,
                                  DeviceLifecycleState::kRunningSilence, policy)
             .allowed,
         "running signal -> running silence");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kRunningSilence,
                                  DeviceLifecycleState::kDraining, policy)
             .allowed,
         "running silence -> draining");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kDraining,
                                  DeviceLifecycleState::kHeldOpen, policy)
             .allowed,
         "draining -> held open");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kHeldOpen,
                                  DeviceLifecycleState::kReleasing, policy)
             .allowed,
         "held open -> releasing");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kReleasing,
                                  DeviceLifecycleState::kReleased, policy)
             .allowed,
         "releasing -> released");
}

void TestUnsafeTransitionsAreBlocked() {
  using aptt::DeviceLifecycleState;
  const aptt::LifecyclePolicy known_problem_policy{
      .allow_immediate_release = false,
      .allow_reinitialize_loop = false,
      .known_problem_device = true,
  };

  const auto running_to_release = aptt::EvaluateTransition(
      DeviceLifecycleState::kRunningSignal,
      DeviceLifecycleState::kReleasing,
      known_problem_policy);
  Expect(!running_to_release.allowed,
         "running signal -> releasing requires explicit override");
  Expect(running_to_release.safety_warning,
         "blocked immediate release carries warning");

  const auto released_to_discovered = aptt::EvaluateTransition(
      DeviceLifecycleState::kReleased,
      DeviceLifecycleState::kDiscovered,
      known_problem_policy);
  Expect(!released_to_discovered.allowed,
         "released -> discovered reinitialize loop is blocked");
  Expect(released_to_discovered.safety_warning,
         "blocked reinitialize loop carries warning");
}

void TestExplicitExpertOverrides() {
  using aptt::DeviceLifecycleState;
  const aptt::LifecyclePolicy expert_policy{
      .allow_immediate_release = true,
      .allow_reinitialize_loop = true,
      .known_problem_device = false,
  };

  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kRunningSignal,
                                  DeviceLifecycleState::kReleasing,
                                  expert_policy)
             .allowed,
         "expert policy can allow immediate release");
  Expect(aptt::EvaluateTransition(DeviceLifecycleState::kReleased,
                                  DeviceLifecycleState::kDiscovered,
                                  expert_policy)
             .allowed,
         "expert policy can allow reinitialize loop");
}

aptt::FormatDescriptor MakePcmFormat(aptt::SampleType sample_type,
                                     std::uint32_t valid_bits,
                                     std::uint32_t container_bits) {
  aptt::FormatDescriptor format;
  format.signal_mode = aptt::SignalMode::kPcm;
  format.sample_rate_hz = 48000;
  format.sample_type = sample_type;
  format.valid_bits_per_sample = valid_bits;
  format.container_bits_per_sample = container_bits;
  format.channels.channel_count = 2;
  return format;
}

void TestPcmSampleConversionCoversRenderableIntegerContainers() {
  const std::vector<float> samples = {-1.0F, 0.0F, 1.0F, 0.5F};

  const auto int16 = aptt::ConvertFloat32InterleavedToPcmBytes(
      samples,
      2,
      MakePcmFormat(aptt::SampleType::kInt16, 16, 16));
  Expect(int16.size() == 8, "int16 conversion writes 2 bytes per sample");
  Expect(int16[0] == 0x00 && int16[1] == 0x80,
         "int16 conversion is little-endian signed PCM");

  const auto packed24 = aptt::ConvertFloat32InterleavedToPcmBytes(
      samples,
      2,
      MakePcmFormat(aptt::SampleType::kInt24Packed, 24, 24));
  Expect(packed24.size() == 12,
         "packed 24-bit conversion writes 3 bytes per sample");

  const auto int24_in_32 = aptt::ConvertFloat32InterleavedToPcmBytes(
      samples,
      2,
      MakePcmFormat(aptt::SampleType::kInt24In32, 24, 32));
  Expect(int24_in_32.size() == 16,
         "24-in-32 conversion writes 4 bytes per sample");
  Expect(int24_in_32[0] == 0x00,
         "24-in-32 conversion pads the low byte");

  const auto int32 = aptt::ConvertFloat32InterleavedToPcmBytes(
      samples,
      2,
      MakePcmFormat(aptt::SampleType::kInt32, 32, 32));
  Expect(int32.size() == 16, "int32 conversion writes 4 bytes per sample");

  const auto float32 = aptt::ConvertFloat32InterleavedToPcmBytes(
      samples,
      2,
      MakePcmFormat(aptt::SampleType::kFloat32, 32, 32));
  Expect(float32.size() == 16, "float32 conversion writes 4 bytes per sample");

  const auto invalid = aptt::ConvertFloat32InterleavedToPcmBytes(
      {0.0F, 0.0F, 0.0F},
      2,
      MakePcmFormat(aptt::SampleType::kInt16, 16, 16));
  Expect(invalid.empty(), "misaligned interleaved input is rejected");
}

}  // namespace

int main() {
  TestDefaultConfigIsConservative();
  TestFormatDefaults();
  TestLifecycleHappyPath();
  TestUnsafeTransitionsAreBlocked();
  TestExplicitExpertOverrides();
  TestPcmSampleConversionCoversRenderableIntegerContainers();

  std::cout << "core model tests passed\n";
  return 0;
}
