stt-cli 0.2.1

Speech to text Cli using Groq API and OpenAI API
use anyhow::{Context, Result};
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc, Mutex as StdMutex,
};
use thiserror::Error;
use tokio::sync::{broadcast, Mutex as TokioMutex};
use tracing::{error, info};

use super::buffer::AudioBuffer;
use super::constants::{CHUNK_DURATION_MS, SAMPLE_RATE};
use super::device::AudioDevice;
use super::stream::AudioStream;
use crate::audio_state::RecordingState;
use crate::providers::TranscriptionProvider;
use crate::shutdown_handler::ShutdownManager;

#[derive(Debug, thiserror::Error)]
pub enum AudioStreamError {
    #[error("Failed to initialize stream: {0}")]
    InitializationError(String),
    #[error("Stream disconnected: {0}")]
    DisconnectionError(String),
    #[error("Buffer error: {0}")]
    BufferError(String),
}

/// Manages audio stream lifecycle and error handling
pub struct AudioStreamManager {
    stream: Option<AudioStream>,
    buffer: Arc<StdMutex<AudioBuffer>>,
    recording_state: RecordingState,
    device: Arc<AudioDevice>,
    provider: Arc<TokioMutex<Box<dyn TranscriptionProvider + Send + Sync>>>,
    chunk_receiver: Option<broadcast::Receiver<Vec<f32>>>,
}

impl AudioStreamManager {
    /// Create a new AudioStreamManager
    pub fn new(
        device: Arc<AudioDevice>,
        provider: Arc<TokioMutex<Box<dyn TranscriptionProvider + Send + Sync>>>,
    ) -> Result<Self> {
        let recording_state = RecordingState::new(false);
        let buffer = Arc::new(StdMutex::new(AudioBuffer::new(
            recording_state.clone(),
            SAMPLE_RATE,
            std::time::Duration::from_millis(CHUNK_DURATION_MS),
        )));

        Ok(Self {
            stream: None,
            buffer,
            recording_state,
            device,
            provider,
            chunk_receiver: None,
        })
    }

    /// Start the audio stream
    pub async fn start_stream(&mut self) -> Result<()> {
        if self.stream.is_some() {
            info!("Stream already running");
            return Ok(());
        }

        let recording = Arc::new(AtomicBool::new(true));
        let stream = AudioStream::from_device(self.device.clone(), recording)
            .await
            .context("Failed to create audio stream")?;

        self.chunk_receiver = Some(stream.subscribe().await);
        self.stream = Some(stream);
        info!("Audio stream started successfully");
        Ok(())
    }

    /// Stop the audio stream
    pub async fn stop_stream(&mut self) -> Result<()> {
        if let Some(stream) = self.stream.take() {
            stream
                .stop()
                .await
                .with_context(|| "Failed to stop audio stream")?;
            info!("Audio stream stopped successfully");
        }
        self.chunk_receiver = None;
        Ok(())
    }

    /// Get the chunk receiver for the audio stream
    pub fn get_receiver(&self) -> Option<broadcast::Receiver<Vec<f32>>> {
        self.chunk_receiver.as_ref().map(|r| r.resubscribe())
    }

    // /// Handle stream errors
    // pub fn handle_stream_error(&self, error: AudioStreamError) {
    //     error!("Audio stream error: {}", error);
    //     // Implement error recovery logic here
    //     // For now, just log the error
    // }

    // /// Check if the stream is currently active
    // pub fn is_active(&self) -> bool {
    //     self.stream.is_some()
    // }

    // /// Check if the stream is disconnected
    // pub fn is_disconnected(&self) -> bool {
    //     self.stream.as_ref().map_or(false, |s| s.is_disconnected())
    // }

    // /// Get a reference to the current buffer
    // pub fn get_buffer(&self) -> Arc<StdMutex<AudioBuffer>> {
    //     self.buffer.clone()
    // }

    // /// Get a reference to the recording state
    // pub fn get_recording_state(&self) -> RecordingState {
    //     self.recording_state.clone()
    // }

    /// Register a shutdown handler for the audio stream
    pub fn register_shutdown_handler(
        manager: Arc<TokioMutex<Self>>,
        shutdown_manager: &ShutdownManager,
    ) {
        shutdown_manager.register(
            "Stop audio stream manager",
            crate::shutdown_handler::ExitPriority::Normal,
            move || async move {
                let mut mgr = manager.lock().await;
                let _ = mgr.stop_stream().await;
            },
        );
    }
}