use screencapturekit_sys::{
    os_types::rc::Id,
    shareable_content::UnsafeSCShareableContent,
    content_filter::{UnsafeContentFilter, UnsafeInitParams},
    stream_configuration::UnsafeStreamConfiguration,
    stream::UnsafeSCStream,
    stream_error_handler::UnsafeSCStreamError,
    stream_output_handler::UnsafeSCStreamOutput,
    cm_sample_buffer_ref::CMSampleBufferRef,
};

pub struct SystemAudioCapture {
    stream: Option<Id<UnsafeSCStream>>,
    sample_rate: u32,
}

struct AudioOutputWrapper {
    callback: Box<dyn Fn(&[f32]) + Send + Sync>,
}

impl UnsafeSCStreamOutput for AudioOutputWrapper {
    fn did_output_sample_buffer(&self, sample: Id<CMSampleBufferRef>, of_type: u8) {
        if of_type == 1 { // Audio
            let buffers = sample.get_av_audio_buffer_list();
            for buffer in buffers {
                // Buffer data is u8, we usually get F32 from SCK if configured.
                // Assuming f32 (Floating Point) based on our config.
                // We need to convert [u8] to [f32].
                let data_u8 = buffer.data;
                let data_f32: &[f32] = unsafe {
                    std::slice::from_raw_parts(
                        data_u8.as_ptr() as *const f32,
                        data_u8.len() / 4,
                    )
                };
                
                (self.callback)(data_f32);
            }
        }
    }
}

struct ErrorHandler;
impl UnsafeSCStreamError for ErrorHandler {
    fn handle_error(&self) {
        // eprintln!("Stream Error");
    }
}

pub async fn check_permissions() -> bool {
    UnsafeSCShareableContent::get().is_ok()
}

impl SystemAudioCapture {
    pub fn new(sample_rate: u32) -> Self {
        Self { stream: None, sample_rate }
    }

    pub async fn start<F>(&mut self, callback: F) -> Result<(), String> 
    where F: Fn(&[f32]) + Send + Sync + 'static {
        
        let content = UnsafeSCShareableContent::get().map_err(|e| format!("Failed to get content"))?;
        let displays = content.displays();
        let display = displays.first().ok_or("No display found")?;

        let filter_init = UnsafeInitParams::Display(display.clone());
        let filter = UnsafeContentFilter::init(filter_init);

        // Wait, 'pixel_format' is OSType. b"BGRA" is &[u8;4].
        // FourCharCode::from_chars exists in crate::os_types::four_char_code but we didn't import it.
        // Actually, we can just use the Default and overwrite fields.
        // But better: use Default and only set what we need.
        
        let mut config = UnsafeStreamConfiguration::default();
        config.width = 100;
        config.height = 100;
        config.captures_audio = 1;
        config.sample_rate = self.sample_rate;
        config.channel_count = 2;
        config.excludes_current_process_audio = 0;

        let output_wrapper = AudioOutputWrapper {
            callback: Box::new(callback), 
        };

        // Convert config to Id<UnsafeStreamConfigurationRef> using Into
        let stream = UnsafeSCStream::init(filter, config.into(), ErrorHandler);
        
        stream.add_stream_output(output_wrapper, 1); // 1 = Audio
        
        stream.start_capture().map_err(|e| "Failed to start capture".to_string())?;
        
        self.stream = Some(stream);
        Ok(())
    }
    
    pub fn stop(&mut self) {
        if let Some(stream) = &self.stream {
            stream.stop_capture();
        }
        self.stream = None;
    }
}
