#pragma once

#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "core/audio_types.h"

namespace aptt {

inline constexpr int kDeviceDiscoverySchemaVersion = 1;

enum class DeviceAvailability {
  kUnknown,
  kActive,
  kDisabled,
  kNotPresent,
  kUnplugged,
  kUnavailable,
};

enum class CapabilitySupport {
  kUnknown,
  kSupported,
  kUnsupported,
  kClosestMatch,
  kFailed,
};

enum class CapabilityProbeKind {
  kDiscoveryOnly,
  kFormatSupport,
  kBufferRange,
  kLatency,
  kDriverInfo,
};

struct ApiErrorInfo {
  std::int64_t raw_code = 0;
  std::string raw_code_hex;
  std::string symbol;
  std::string message;
};

struct CapabilityProbeRecord {
  CapabilityProbeKind kind = CapabilityProbeKind::kDiscoveryOnly;
  CapabilitySupport support = CapabilitySupport::kUnknown;
  std::optional<FormatDescriptor> requested_format;
  std::optional<FormatDescriptor> accepted_format;
  std::string detail;
  std::optional<ApiErrorInfo> error;
  std::map<std::string, std::string> backend_fields;
};

struct DeviceCapabilitySnapshot {
  DeviceInfo device;
  DeviceAvailability availability = DeviceAvailability::kUnknown;
  std::vector<std::string> default_roles;
  std::vector<CapabilityProbeRecord> probes;
  std::optional<ApiErrorInfo> discovery_error;
  std::map<std::string, std::string> backend_fields;
};

struct DiscoverySnapshot {
  int schema_version = kDeviceDiscoverySchemaVersion;
  std::string capture_utc;
  BackendApi backend = BackendApi::kNull;
  bool opens_audio_device = false;
  bool initializes_stream = false;
  bool starts_stream = false;
  std::vector<DeviceCapabilitySnapshot> devices;
  std::optional<ApiErrorInfo> enumeration_error;
};

const char* ToString(DeviceAvailability availability);
const char* ToString(CapabilitySupport support);
const char* ToString(CapabilityProbeKind kind);

DeviceAvailability DeviceAvailabilityFromString(const std::string& value);
CapabilitySupport CapabilitySupportFromString(const std::string& value);
CapabilityProbeKind CapabilityProbeKindFromString(const std::string& value);

std::string HexCode(std::int64_t raw_code);
std::string HexCode32ForDomain(std::int64_t raw_code,
                               std::string_view code_domain);
ApiErrorInfo MakeApiError(std::int64_t raw_code,
                          std::string symbol,
                          std::string message);
ApiErrorInfo MakeApiErrorForDomain(std::int64_t raw_code,
                                   std::string symbol,
                                   std::string message,
                                   std::string_view code_domain);

DeviceCapabilitySnapshot MakeDiscoveredDeviceSnapshot(
    DeviceInfo device,
    DeviceAvailability availability);

bool DiscoverySnapshotIsSafeDryRun(const DiscoverySnapshot& snapshot);
std::optional<DeviceCapabilitySnapshot> FindDeviceByStableId(
    const DiscoverySnapshot& snapshot,
    const std::string& stable_id);

std::string SerializeDiscoverySnapshotJson(const DiscoverySnapshot& snapshot);
std::optional<DiscoverySnapshot> ParseDiscoverySnapshotJson(const std::string& json);

}  // namespace aptt
