#include "diagnostics/null_diagnostic_session.h"

#include <sstream>

#include "backends/null_backend.h"
#include "core/diagnostic_bundle.h"

namespace aptt {
namespace {

DiagnosticEvent MakeSessionEvent(std::uint64_t sequence,
                                 DiagnosticEventType type,
                                 std::string message) {
  DiagnosticEvent event;
  event.sequence = sequence;
  event.type = type;
  event.backend = BackendApi::kNull;
  event.device_id = "null-device";
  event.message = std::move(message);
  return event;
}

std::string BuildSummary(const RealtimeStats& stats) {
  std::ostringstream out;
  out << "Audio Path Tone Tester NullBackend diagnostic\n";
  out << "frames_generated=" << stats.frames_generated << "\n";
  out << "frames_submitted=" << stats.frames_submitted << "\n";
  out << "frames_accepted=" << stats.frames_accepted << "\n";
  out << "callback_count=" << stats.callback_count << "\n";
  out << "underrun_count=" << stats.underrun_count << "\n";
  return out.str();
}

}  // namespace

NullDiagnosticSessionResult RunNullDiagnosticSession(
    PlaybackConfig config,
    std::optional<std::filesystem::path> bundle_directory) {
  NullDiagnosticSessionResult session;
  NullBackend backend;
  auto devices = backend.enumerateDevices();
  config.backend = BackendApi::kNull;
  config.device = devices.front();
  session.config = config;

  std::uint64_t sequence = 1;
  session.events.push_back(MakeSessionEvent(
      sequence++, DiagnosticEventType::kSessionStart, "NullBackend session started"));

  if (const auto initialized = backend.initialize(config); !initialized.ok) {
    session.result = initialized;
    return session;
  }
  session.events.push_back(MakeLifecycleEvent(
      sequence++,
      BackendApi::kNull,
      config.device.stable_id,
      DeviceLifecycleState::kReserved,
      DeviceLifecycleState::kPrepared,
      "NullBackend prepared"));

  if (const auto started = backend.start(); !started.ok) {
    session.result = started;
    return session;
  }
  session.events.push_back(MakeLifecycleEvent(
      sequence++,
      BackendApi::kNull,
      config.device.stable_id,
      DeviceLifecycleState::kPrepared,
      DeviceLifecycleState::kRunningSignal,
      "NullBackend generated signal"));
  if (config.hold_mode == HoldMode::kKeepWarmAfterStop) {
    session.events.push_back(MakeLifecycleEvent(
        sequence++,
        BackendApi::kNull,
        config.device.stable_id,
        DeviceLifecycleState::kRunningSignal,
        DeviceLifecycleState::kRunningSilence,
        "NullBackend keep-warm silence"));
  }

  session.stats = backend.getRealtimeStats();
  DiagnosticEvent stats_event = MakeSessionEvent(
      sequence++, DiagnosticEventType::kRuntimeStats, "NullBackend stats");
  stats_event.format = config.format;
  stats_event.realtime_stats = session.stats;
  stats_event.backend_fields["backend_kind"] = "synthetic";
  session.events.push_back(std::move(stats_event));

  if (const auto stopped = backend.stop(); !stopped.ok) {
    session.result = stopped;
    return session;
  }
  session.events.push_back(MakeLifecycleEvent(
      sequence++,
      BackendApi::kNull,
      config.device.stable_id,
      DeviceLifecycleState::kRunningSilence,
      DeviceLifecycleState::kDraining,
      "NullBackend draining"));
  session.events.push_back(MakeLifecycleEvent(
      sequence++,
      BackendApi::kNull,
      config.device.stable_id,
      DeviceLifecycleState::kDraining,
      DeviceLifecycleState::kHeldOpen,
      "NullBackend held open"));

  if (const auto shutdown = backend.shutdown(); !shutdown.ok) {
    session.result = shutdown;
    return session;
  }
  session.events.push_back(MakeLifecycleEvent(
      sequence++,
      BackendApi::kNull,
      config.device.stable_id,
      DeviceLifecycleState::kHeldOpen,
      DeviceLifecycleState::kReleasing,
      "NullBackend releasing"));
  session.events.push_back(MakeLifecycleEvent(
      sequence++,
      BackendApi::kNull,
      config.device.stable_id,
      DeviceLifecycleState::kReleasing,
      DeviceLifecycleState::kReleased,
      "NullBackend released"));

  session.events.push_back(MakeSessionEvent(
      sequence++, DiagnosticEventType::kSessionSummary, "NullBackend session complete"));

  if (bundle_directory) {
    DiagnosticBundleInput bundle;
    bundle.config = config;
    bundle.device = config.device;
    bundle.format_probe = backend.probeFormats(config.device);
    session.format_probe = bundle.format_probe;
    bundle.events = session.events;
    bundle.final_stats = session.stats;
    bundle.pcm_summary = BuildPcmRunSummary(
        config.backend,
        config.device.stable_id,
        session.events,
        session.stats);
    bundle.summary = BuildSummary(session.stats);
    if (const auto written = WriteDiagnosticBundleDirectory(*bundle_directory, bundle);
        !written.ok) {
      session.result = written;
      return session;
    }
    session.bundle_directory = *bundle_directory;
  }

  if (session.format_probe.supported_formats.empty() &&
      session.format_probe.rejection_reasons.empty()) {
    session.format_probe = backend.probeFormats(config.device);
  }

  session.result = OperationResult::Success();
  return session;
}

}  // namespace aptt
