/// Minimal test program to validate FFmpeg audio encoding workflow
///
/// This program tests encoding raw PCM samples to MP3 using ffmpeg-next.
/// Run with: cargo run --example ffmpeg_test

use std::path::Path;

fn main() -> Result<(), String> {
    println!("Testing FFmpeg audio encoding...");

    // Initialize FFmpeg
    ffmpeg_next::init().map_err(|e| format!("Failed to initialize FFmpeg: {}", e))?;

    // Test 1: List available encoders
    println!("\nAvailable MP3 encoders:");
    if let Some(encoder) = ffmpeg_next::encoder::find(ffmpeg_next::codec::Id::MP3) {
        println!("  - Found MP3 encoder: {}", encoder.name());
    } else {
        println!("  - No MP3 encoder found!");
    }

    println!("\nAvailable AAC encoders:");
    if let Some(encoder) = ffmpeg_next::encoder::find(ffmpeg_next::codec::Id::AAC) {
        println!("  - Found AAC encoder: {}", encoder.name());
    } else {
        println!("  - No AAC encoder found!");
    }

    // Test 2: Create a simple MP3 encoder and encode silence
    test_mp3_encoding()?;

    // Test 3: Create a simple AAC encoder and encode silence
    test_aac_encoding()?;

    println!("\n✅ All tests passed!");
    Ok(())
}

fn test_mp3_encoding() -> Result<(), String> {
    println!("\nTest: Encoding 1 second of silence to MP3...");

    // Output file
    let output_path = "/tmp/test_silence.mp3";

    // Generate 1 second of stereo silence at 44.1 kHz
    let sample_rate = 44100;
    let channels = 2;
    let duration_secs = 1.0;
    let num_samples = (sample_rate as f64 * duration_secs * channels as f64) as usize;
    let pcm_samples: Vec<f32> = vec![0.0; num_samples]; // Silence

    println!("  Generated {} PCM samples ({}Hz, {} channels, {:.1}s)",
             num_samples, sample_rate, channels, duration_secs);

    // Encode to MP3
    encode_pcm_to_mp3(&pcm_samples, sample_rate, channels, 320, output_path)?;

    // Check output file exists
    if Path::new(output_path).exists() {
        let metadata = std::fs::metadata(output_path).unwrap();
        println!("  ✅ Created MP3 file: {} ({} bytes)", output_path, metadata.len());
    } else {
        return Err("MP3 file was not created!".to_string());
    }

    Ok(())
}

fn test_aac_encoding() -> Result<(), String> {
    println!("\nTest: Encoding 1 second of silence to AAC...");

    // Output file
    let output_path = "/tmp/test_silence.m4a";

    // Generate 1 second of stereo silence at 44.1 kHz
    let sample_rate = 44100;
    let channels = 2;
    let duration_secs = 1.0;
    let num_samples = (sample_rate as f64 * duration_secs * channels as f64) as usize;
    let pcm_samples: Vec<f32> = vec![0.0; num_samples]; // Silence

    println!("  Generated {} PCM samples ({}Hz, {} channels, {:.1}s)",
             num_samples, sample_rate, channels, duration_secs);

    // Encode to AAC
    encode_pcm_to_aac(&pcm_samples, sample_rate, channels, 192, output_path)?;

    // Check output file exists
    if Path::new(output_path).exists() {
        let metadata = std::fs::metadata(output_path).unwrap();
        println!("  ✅ Created AAC file: {} ({} bytes)", output_path, metadata.len());
    } else {
        return Err("AAC file was not created!".to_string());
    }

    Ok(())
}

/// Encode raw PCM samples to MP3 using ffmpeg-next
fn encode_pcm_to_mp3(
    samples: &[f32],
    sample_rate: u32,
    channels: u32,
    bitrate_kbps: u32,
    output_path: &str,
) -> Result<(), String> {
    use ffmpeg_next as ffmpeg;

    // Find MP3 encoder
    let encoder_codec = ffmpeg::encoder::find(ffmpeg::codec::Id::MP3)
        .ok_or("MP3 encoder not found")?;

    println!("  Using encoder: {}", encoder_codec.name());

    // Create output format context FIRST (like transcode example)
    let mut output = ffmpeg::format::output(&output_path)
        .map_err(|e| format!("Failed to create output file: {}", e))?;

    // Don't use stream parameters - create encoder directly
    // The stream was just added but has no parameters set yet
    let mut encoder = ffmpeg::codec::Context::new_with_codec(encoder_codec)
        .encoder()
        .audio()
        .map_err(|e| format!("Failed to create encoder: {}", e))?;

    println!("  Created encoder directly from codec");

    // Determine channel layout first
    let channel_layout = match channels {
        1 => ffmpeg::channel_layout::ChannelLayout::MONO,
        2 => ffmpeg::channel_layout::ChannelLayout::STEREO,
        _ => return Err(format!("Unsupported channel count: {}", channels)),
    };

    // Configure encoder with explicit format (required in ffmpeg-next 8.0)
    encoder.set_rate(sample_rate as i32);
    encoder.set_channel_layout(channel_layout);

    // Set format to S16 Planar (s16p) which libmp3lame supports
    use ffmpeg_next::format::sample::Type;
    use ffmpeg_next::format::Sample;
    encoder.set_format(Sample::I16(Type::Planar));

    encoder.set_bit_rate((bitrate_kbps * 1000) as usize);
    encoder.set_time_base(ffmpeg::Rational(1, sample_rate as i32));

    println!("  Encoder configured: {}Hz, {} channels, {} kbps",
             sample_rate, channels, bitrate_kbps);
    println!("  Format before open: {:?}", encoder.format());

    // Open encoder (like transcode-audio example)
    let mut encoder = encoder.open_as(encoder_codec)
        .map_err(|e| format!("Failed to open encoder: {}", e))?;

    println!("  ✅ Encoder opened successfully!");
    println!("  Opened encoder format: {:?}", encoder.format());

    // Now add stream and set its parameters from the opened encoder
    let mut stream = output.add_stream(encoder_codec)
        .map_err(|e| format!("Failed to add stream: {}", e))?;
    stream.set_parameters(&encoder);

    // Write header
    output.write_header()
        .map_err(|e| format!("Failed to write header: {}", e))?;

    println!("  Encoding {} samples...", samples.len());

    // Convert interleaved f32 to planar i16
    let num_frames = samples.len() / channels as usize;
    let planar_samples = convert_to_planar_i16(samples, channels);

    // Get encoder frame size
    let frame_size = encoder.frame_size();
    let samples_per_frame = if frame_size > 0 {
        frame_size as usize
    } else {
        1152 // Default MP3 frame size
    };

    println!("  Frame size: {} samples", samples_per_frame);

    // Encode in chunks
    let mut samples_encoded = 0;
    while samples_encoded < num_frames {
        let samples_remaining = num_frames - samples_encoded;
        let chunk_size = samples_remaining.min(samples_per_frame);

        // Create audio frame
        let mut frame = ffmpeg::frame::Audio::new(
            ffmpeg::format::Sample::I16(ffmpeg::format::sample::Type::Planar),
            chunk_size,
            channel_layout,
        );
        frame.set_rate(sample_rate);

        // Copy planar samples to frame
        unsafe {
            for ch in 0..channels as usize {
                let plane = frame.data_mut(ch);
                let offset = samples_encoded;
                let src = &planar_samples[ch][offset..offset + chunk_size];

                std::ptr::copy_nonoverlapping(
                    src.as_ptr() as *const u8,
                    plane.as_mut_ptr(),
                    chunk_size * std::mem::size_of::<i16>(),
                );
            }
        }

        // Send frame to encoder
        encoder.send_frame(&frame)
            .map_err(|e| format!("Failed to send frame: {}", e))?;

        // Receive and write packets
        receive_and_write_packets(&mut encoder, &mut output)?;

        samples_encoded += chunk_size;
    }

    // Flush encoder
    encoder.send_eof()
        .map_err(|e| format!("Failed to send EOF: {}", e))?;
    receive_and_write_packets(&mut encoder, &mut output)?;

    // Write trailer
    output.write_trailer()
        .map_err(|e| format!("Failed to write trailer: {}", e))?;

    println!("  Encoding complete - {} frames encoded", num_frames);

    Ok(())
}

/// Convert interleaved f32 samples to planar i16 format
fn convert_to_planar_i16(interleaved: &[f32], channels: u32) -> Vec<Vec<i16>> {
    let num_frames = interleaved.len() / channels as usize;
    let mut planar = vec![vec![0i16; num_frames]; channels as usize];

    for (i, chunk) in interleaved.chunks(channels as usize).enumerate() {
        for (ch, &sample) in chunk.iter().enumerate() {
            // Clamp and convert f32 (-1.0 to 1.0) to i16
            let clamped = sample.max(-1.0).min(1.0);
            planar[ch][i] = (clamped * 32767.0) as i16;
        }
    }

    planar
}

/// Receive encoded packets and write to output
fn receive_and_write_packets(
    encoder: &mut ffmpeg_next::encoder::Audio,
    output: &mut ffmpeg_next::format::context::Output,
) -> Result<(), String> {
    let mut encoded = ffmpeg_next::Packet::empty();

    while encoder.receive_packet(&mut encoded).is_ok() {
        encoded.set_stream(0);
        encoded.write_interleaved(output)
            .map_err(|e| format!("Failed to write packet: {}", e))?;
    }

    Ok(())
}

/// Encode raw PCM samples to AAC using ffmpeg-next
fn encode_pcm_to_aac(
    samples: &[f32],
    sample_rate: u32,
    channels: u32,
    bitrate_kbps: u32,
    output_path: &str,
) -> Result<(), String> {
    use ffmpeg_next as ffmpeg;

    // Find AAC encoder
    let encoder_codec = ffmpeg::encoder::find(ffmpeg::codec::Id::AAC)
        .ok_or("AAC encoder not found")?;

    println!("  Using encoder: {}", encoder_codec.name());

    // Create output format context
    let mut output = ffmpeg::format::output(&output_path)
        .map_err(|e| format!("Failed to create output file: {}", e))?;

    // Create encoder directly from codec
    let mut encoder = ffmpeg::codec::Context::new_with_codec(encoder_codec)
        .encoder()
        .audio()
        .map_err(|e| format!("Failed to create encoder: {}", e))?;

    println!("  Created encoder directly from codec");

    // Determine channel layout
    let channel_layout = match channels {
        1 => ffmpeg::channel_layout::ChannelLayout::MONO,
        2 => ffmpeg::channel_layout::ChannelLayout::STEREO,
        _ => return Err(format!("Unsupported channel count: {}", channels)),
    };

    // Configure encoder - AAC supports F32 Planar (fltp)
    encoder.set_rate(sample_rate as i32);
    encoder.set_channel_layout(channel_layout);
    encoder.set_format(ffmpeg::format::Sample::F32(ffmpeg::format::sample::Type::Planar));
    encoder.set_bit_rate((bitrate_kbps * 1000) as usize);
    encoder.set_time_base(ffmpeg::Rational(1, sample_rate as i32));

    println!("  Encoder configured: {}Hz, {} channels, {} kbps",
             sample_rate, channels, bitrate_kbps);
    println!("  Format before open: {:?}", encoder.format());

    // Open encoder
    let mut encoder = encoder.open_as(encoder_codec)
        .map_err(|e| format!("Failed to open encoder: {}", e))?;

    println!("  ✅ Encoder opened successfully!");
    println!("  Opened encoder format: {:?}", encoder.format());

    // Add stream and set parameters
    {
        let mut stream = output.add_stream(encoder_codec)
            .map_err(|e| format!("Failed to add stream: {}", e))?;
        stream.set_parameters(&encoder);
    }

    // Write header
    output.write_header()
        .map_err(|e| format!("Failed to write header: {}", e))?;

    println!("  Encoding {} samples...", samples.len());

    // Convert interleaved f32 to planar f32
    let num_frames = samples.len() / channels as usize;
    let planar_samples = convert_to_planar_f32(samples, channels);

    // Get encoder frame size
    let frame_size = encoder.frame_size();
    let samples_per_frame = if frame_size > 0 {
        frame_size as usize
    } else {
        1024 // Default AAC frame size
    };

    println!("  Frame size: {} samples", samples_per_frame);

    // Encode in chunks
    let mut samples_encoded = 0;
    while samples_encoded < num_frames {
        let samples_remaining = num_frames - samples_encoded;
        let chunk_size = samples_remaining.min(samples_per_frame);

        // Create audio frame
        let mut frame = ffmpeg::frame::Audio::new(
            ffmpeg::format::Sample::F32(ffmpeg::format::sample::Type::Planar),
            chunk_size,
            channel_layout,
        );
        frame.set_rate(sample_rate);

        // Copy planar samples to frame
        unsafe {
            for ch in 0..channels as usize {
                let plane = frame.data_mut(ch);
                let offset = samples_encoded;
                let src = &planar_samples[ch][offset..offset + chunk_size];

                std::ptr::copy_nonoverlapping(
                    src.as_ptr() as *const u8,
                    plane.as_mut_ptr(),
                    chunk_size * std::mem::size_of::<f32>(),
                );
            }
        }

        // Send frame to encoder
        encoder.send_frame(&frame)
            .map_err(|e| format!("Failed to send frame: {}", e))?;

        // Receive and write packets
        receive_and_write_packets(&mut encoder, &mut output)?;

        samples_encoded += chunk_size;
    }

    // Flush encoder
    encoder.send_eof()
        .map_err(|e| format!("Failed to send EOF: {}", e))?;
    receive_and_write_packets(&mut encoder, &mut output)?;

    // Write trailer
    output.write_trailer()
        .map_err(|e| format!("Failed to write trailer: {}", e))?;

    println!("  Encoding complete - {} frames encoded", num_frames);

    Ok(())
}

/// Convert interleaved f32 samples to planar f32 format
fn convert_to_planar_f32(interleaved: &[f32], channels: u32) -> Vec<Vec<f32>> {
    let num_frames = interleaved.len() / channels as usize;
    let mut planar = vec![vec![0.0f32; num_frames]; channels as usize];

    for (i, chunk) in interleaved.chunks(channels as usize).enumerate() {
        for (ch, &sample) in chunk.iter().enumerate() {
            planar[ch][i] = sample;
        }
    }

    planar
}
