#include "backends/null_backend.h"

#include <algorithm>
#include <cstdint>
#include <utility>

namespace aptt {

BackendApi NullBackend::api() const {
  return BackendApi::kNull;
}

DeviceList NullBackend::enumerateDevices() {
  return {DeviceInfo{
      .backend = BackendApi::kNull,
      .display_name = "Null Backend",
      .stable_id = "null-device",
      .driver_name = "synthetic",
      .is_default = true,
      .is_known_problem_device = false,
  }};
}

FormatProbeResult NullBackend::probeFormats(const DeviceInfo&) {
  FormatProbeResult result;
  result.requested = config_.format;
  result.supported_formats.push_back(FormatDescriptor{});
  return result;
}

OperationResult NullBackend::initialize(const PlaybackConfig& config) {
  config_ = config;
  config_.backend = BackendApi::kNull;
  if (config_.device.stable_id.empty()) {
    config_.device = enumerateDevices().front();
  }
  stats_ = {};
  generator_.ResetPhase();
  state_ = DeviceLifecycleState::kReserved;
  if (!TransitionTo(DeviceLifecycleState::kPrepared)) {
    return OperationResult::Failure(1, last_error_.message);
  }
  return OperationResult::Success();
}

OperationResult NullBackend::start() {
  if (!TransitionTo(DeviceLifecycleState::kRunningSignal)) {
    return OperationResult::Failure(2, last_error_.message);
  }

  const std::uint32_t sample_rate = config_.format.sample_rate_hz;
  const std::uint32_t duration_ms = config_.continuous ? 100 : config_.duration_ms;
  const std::uint64_t total_frames =
      std::max<std::uint64_t>(1, (static_cast<std::uint64_t>(sample_rate) * duration_ms) / 1000);
  const std::uint32_t block_frames = std::min<std::uint32_t>(256, static_cast<std::uint32_t>(total_frames));

  PcmSineConfig sine_config;
  sine_config.sample_rate_hz = config_.format.sample_rate_hz;
  sine_config.channel_count = config_.format.channels.channel_count;
  sine_config.frequency_hz = config_.frequency_hz;
  sine_config.amplitude_dbfs = config_.amplitude_dbfs;
  if (config_.left_channel_enabled && !config_.right_channel_enabled) {
    sine_config.channel_assignment = ChannelAssignment::kLeftOnly;
  } else if (!config_.left_channel_enabled && config_.right_channel_enabled) {
    sine_config.channel_assignment = ChannelAssignment::kRightOnly;
  }
  sine_config.invert_right = config_.invert_right_phase;
  sine_config.right_channel_phase_offset_degrees =
      config_.right_channel_phase_offset_degrees;

  std::uint64_t remaining = total_frames;
  while (remaining > 0) {
    const std::uint32_t frames =
        static_cast<std::uint32_t>(std::min<std::uint64_t>(block_frames, remaining));
    const auto generated = generator_.GenerateInterleavedFloat32(sine_config, frames);
    (void)generated;
    stats_.frames_generated += frames;
    stats_.frames_submitted += frames;
    stats_.frames_accepted += frames;
    ++stats_.callback_count;
    remaining -= frames;
  }
  stats_.expected_callback_interval_ms =
      1000.0 * block_frames / static_cast<double>(sample_rate);
  stats_.last_callback_interval_ms = stats_.expected_callback_interval_ms;
  stats_.max_callback_delay_ms = 0.0;

  if (config_.hold_mode == HoldMode::kKeepWarmAfterStop) {
    TransitionTo(DeviceLifecycleState::kRunningSilence);
  }
  return OperationResult::Success();
}

OperationResult NullBackend::stop() {
  if (state_ == DeviceLifecycleState::kRunningSignal ||
      state_ == DeviceLifecycleState::kRunningSilence) {
    if (!TransitionTo(DeviceLifecycleState::kDraining)) {
      return OperationResult::Failure(3, last_error_.message);
    }
  }

  if (!TransitionTo(DeviceLifecycleState::kHeldOpen)) {
    return OperationResult::Failure(4, last_error_.message);
  }
  return OperationResult::Success();
}

OperationResult NullBackend::reset() {
  stats_ = {};
  generator_.ResetPhase();
  if (state_ == DeviceLifecycleState::kReleased) {
    return OperationResult::Failure(5, "released NullBackend cannot be reset");
  }
  state_ = DeviceLifecycleState::kPrepared;
  return OperationResult::Success();
}

RealtimeStats NullBackend::getRealtimeStats() const {
  return stats_;
}

BackendError NullBackend::getLastError() const {
  return last_error_;
}

OperationResult NullBackend::shutdown() {
  if (state_ == DeviceLifecycleState::kReleased) {
    return OperationResult::Success();
  }
  if (state_ != DeviceLifecycleState::kReleasing) {
    const auto decision = EvaluateTransition(state_, DeviceLifecycleState::kReleasing);
    if (!decision.allowed) {
      if (state_ == DeviceLifecycleState::kRunningSignal ||
          state_ == DeviceLifecycleState::kRunningSilence) {
        TransitionTo(DeviceLifecycleState::kDraining);
        TransitionTo(DeviceLifecycleState::kHeldOpen);
      }
    }
    if (!TransitionTo(DeviceLifecycleState::kReleasing)) {
      return OperationResult::Failure(6, last_error_.message);
    }
  }
  if (!TransitionTo(DeviceLifecycleState::kReleased)) {
    return OperationResult::Failure(7, last_error_.message);
  }
  return OperationResult::Success();
}

bool NullBackend::TransitionTo(DeviceLifecycleState next) {
  const auto decision = EvaluateTransition(state_, next);
  if (!decision.allowed) {
    last_error_ = {100, decision.reason};
    return false;
  }
  state_ = next;
  last_error_ = {};
  return true;
}

}  // namespace aptt
