use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::mpsc::{Receiver, Sender};
use thiserror::Error;

/// Playback output channel count. Capture/encode/network remain mono; only the
/// listener-side playout bus is stereo.
pub const PLAYBACK_CHANNELS: usize = 2;

/// Target depth of the playback ring buffer, in interleaved samples (48kHz
/// stereo).
///
/// The playout chain is paced to keep the ring near this level: production is
/// driven by how fast PipeWire actually drains the ring (the hardware clock),
/// not by a fixed software timer — which is what eliminates the producer/
/// consumer beat that otherwise churns ~20% of audio into drops + silence.
/// 5760 = 60ms = 3×20ms stereo frames, comfortably above the 2048-frame max quantum
/// so a single hardware pull can never empty the ring before the mixer refills.
pub const PLAYBACK_TARGET_SAMPLES: usize = 2880 * PLAYBACK_CHANNELS;

#[derive(Error, Debug)]
pub enum AudioError {
    #[error("Failed to initialize audio backend: {0}")]
    Init(String),
    #[error("Audio device error: {0}")]
    Device(String),
    #[error("Stream error: {0}")]
    Stream(String),
    #[error("Audio buffer overflow/underflow")]
    BufferError,
    #[error("Other audio error: {0}")]
    Other(String),
}

pub trait AudioBackend: Send + Sync {
    /// Starts capturing raw PCM audio from the input device (microphone),
    /// sending chunks of samples (e.g. `Vec<i16>`) to the provided Sender.
    fn start_capture(
        &self,
        tx: Sender<Vec<i16>>,
        target_node: Option<String>,
    ) -> Result<(), AudioError>;

    /// Starts playing back raw PCM audio to the output device (speaker),
    /// reading mixed/incoming chunks of samples from the provided Receiver.
    ///
    /// `ring_fill` is updated with the playback ring's current occupancy (in
    /// samples) as the device drains and the worker fills it. The caller (the
    /// mixer) reads it to pace production to the hardware clock — produce only
    /// while the ring is below [`PLAYBACK_TARGET_SAMPLES`] — instead of on a
    /// fixed timer that beats against the device quantum.
    fn start_playback(
        &self,
        rx: Receiver<Vec<i16>>,
        target_node: Option<String>,
        ring_fill: Arc<AtomicUsize>,
    ) -> Result<(), AudioError>;

    /// Stops both capture and playback streams.
    fn stop(&self) -> Result<(), AudioError>;
}

pub mod clip_player;
pub mod eq;
pub mod gate;
pub mod limiter;
pub mod multitrack;
// The cross-repo ownership tag (plan §5.1). Platform-neutral on purpose: the
// carriers only matter on PipeWire, but the literals are a wire contract and
// their test must run on every platform so a rename can't pass CI elsewhere.
pub mod ownership;
pub mod pan;
// Linear resamplers used by the Windows/cpal backend (W4). Platform-neutral and
// pure, so it builds (and its tests run) everywhere even though only the cpal
// backend wires it in.
#[cfg(windows)]
pub mod cpal_impl;
#[cfg(target_os = "linux")]
pub mod echo_cancel;
#[cfg(target_os = "linux")]
pub mod pipewire_impl;
#[cfg(target_os = "linux")]
pub mod pw_cli;
pub mod recorder;
pub mod resample;

/// A selectable audio device for the input/output pickers. `name` is the stable
/// identifier the backend uses to request the device (`target_node`);
/// `description` is the human-facing label shown in the UI. The two may be equal
/// (cpal/WASAPI) or differ (PipeWire node name vs. description).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AudioDevice {
    pub name: String,
    pub description: String,
    pub is_input: bool,
}

impl std::fmt::Display for AudioDevice {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.description)
    }
}

// Enumerate audio input/output devices for the pickers (sorted by description),
// returning the same `AudioDevice` shape regardless of platform: PipeWire
// (`pw-cli`) on Linux, cpal/WASAPI on Windows.
#[cfg(windows)]
pub use cpal_impl::enumerate_audio_devices;
#[cfg(target_os = "linux")]
pub use pw_cli::enumerate_audio_devices;

/// The audio backend implementation for the current platform.
///
/// The whole app constructs and threads this alias (via
/// `PlatformAudioBackend::new()`) rather than any concrete backend type, so
/// platform selection lives entirely here. Both implementations satisfy the
/// [`AudioBackend`] trait, which is the only interface the core talks to.
///
/// - Linux → PipeWire ([`pipewire_impl::PipeWireBackend`]).
/// - Windows → cpal/WASAPI ([`cpal_impl::CpalBackend`]).
#[cfg(target_os = "linux")]
pub type PlatformAudioBackend = pipewire_impl::PipeWireBackend;
#[cfg(windows)]
pub type PlatformAudioBackend = cpal_impl::CpalBackend;
