//! Cross-crate port handles.
//!
//! Exists to break the one back-edge in the audio↔analysis dependency:
//! `psysonic-analysis` needs to ask "is this track currently playing?", but
//! must not depend on `psysonic-audio` (which has the real dep on analysis,
//! not the other way around).
//!
//! Implementation note: ports are exposed as **closure handles** rather than
//! `Arc<dyn Trait>` — this avoids forcing every existing `State<AudioEngine>`
//! callsite to switch to `State<Arc<AudioEngine>>` (which Tauri State requires
//! for trait-object registration). The shell crate creates the handle by
//! capturing an `AppHandle` and looking up the audio engine at call time.

use std::sync::Arc;

/// Read-only queries about the live playback session, used by analysis-side
/// code to break the analysis→audio back-edge. The shell crate constructs an
/// instance with two closures (each capturing an `AppHandle`) and registers it
/// as Tauri State; `psysonic-analysis` looks it up via `try_state::<…>()`.
///
/// The closures are independent so each can be a no-op / always-false fallback
/// without coupling the other.
#[derive(Clone)]
pub struct PlaybackQueryHandle {
    is_playing: Arc<dyn Fn(&str) -> bool + Send + Sync + 'static>,
    should_defer_backfill: Arc<dyn Fn(&str) -> bool + Send + Sync + 'static>,
}

impl PlaybackQueryHandle {
    pub fn new<P, D>(is_playing: P, should_defer_backfill: D) -> Self
    where
        P: Fn(&str) -> bool + Send + Sync + 'static,
        D: Fn(&str) -> bool + Send + Sync + 'static,
    {
        Self {
            is_playing: Arc::new(is_playing),
            should_defer_backfill: Arc::new(should_defer_backfill),
        }
    }

    /// `true` if `track_id` is the track currently being decoded/played.
    pub fn is_track_currently_playing(&self, track_id: &str) -> bool {
        (self.is_playing)(track_id)
    }

    /// `true` if a ranged HTTP playback for `track_id` is mid-flight and will
    /// seed analysis on completion — the backfill enqueue should defer.
    pub fn ranged_loudness_backfill_should_defer(&self, track_id: &str) -> bool {
        (self.should_defer_backfill)(track_id)
    }
}
