use std::fs::File;
use std::path::Path;

use symphonia::core::audio::SampleBuffer;
use symphonia::core::codecs::DecoderOptions;
use symphonia::core::formats::FormatOptions;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;

use kon_core::error::{KonError, Result};
use kon_core::types::AudioSamples;

/// Decode an audio file to mono f32 PCM samples.
/// Supports all formats symphonia handles: mp3, aac, flac, wav, ogg, etc.
pub fn decode_audio_file(path: &Path) -> Result<AudioSamples> {
    let file = File::open(path)
        .map_err(|e| KonError::AudioDecodeFailed(format!("Cannot open file: {e}")))?;
    let mss = MediaSourceStream::new(Box::new(file), Default::default());

    let mut hint = Hint::new();
    if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
        hint.with_extension(ext);
    }

    let probed = symphonia::default::get_probe()
        .format(
            &hint,
            mss,
            &FormatOptions::default(),
            &MetadataOptions::default(),
        )
        .map_err(|e| KonError::AudioDecodeFailed(format!("Unsupported format: {e}")))?;

    let mut format = probed.format;

    let track = format
        .default_track()
        .ok_or_else(|| KonError::AudioDecodeFailed("No audio track found".into()))?;
    let sample_rate = track
        .codec_params
        .sample_rate
        .ok_or_else(|| KonError::AudioDecodeFailed("Unknown sample rate".into()))?;

    if sample_rate == 0 {
        return Err(KonError::AudioDecodeFailed("Invalid sample rate: 0".into()));
    }

    let track_id = track.id;

    let mut decoder = symphonia::default::get_codecs()
        .make(&track.codec_params, &DecoderOptions::default())
        .map_err(|e| KonError::AudioDecodeFailed(format!("Codec error: {e}")))?;

    let mut samples: Vec<f32> = Vec::new();
    let mut decode_errors = 0u32;

    loop {
        let packet = match format.next_packet() {
            Ok(p) => p,
            Err(symphonia::core::errors::Error::IoError(ref e))
                if e.kind() == std::io::ErrorKind::UnexpectedEof =>
            {
                break;
            }
            Err(symphonia::core::errors::Error::ResetRequired) => break,
            Err(_) => break,
        };

        if packet.track_id() != track_id {
            continue;
        }

        let decoded = match decoder.decode(&packet) {
            Ok(d) => d,
            Err(_) => {
                decode_errors += 1;
                continue;
            }
        };

        let spec = *decoded.spec();
        let channels = spec.channels.count();
        let mut sample_buf = SampleBuffer::<f32>::new(decoded.capacity() as u64, spec);
        sample_buf.copy_interleaved_ref(decoded);

        let buf = sample_buf.samples();
        if channels == 1 {
            samples.extend_from_slice(buf);
        } else {
            for chunk in buf.chunks(channels) {
                let sum: f32 = chunk.iter().sum();
                samples.push(sum / channels as f32);
            }
        }
    }

    if samples.is_empty() {
        if decode_errors > 0 {
            return Err(KonError::AudioDecodeFailed(format!(
                "No audio decoded ({decode_errors} packets failed — file may be corrupt)"
            )));
        }
        return Err(KonError::AudioDecodeFailed("No audio data decoded".into()));
    }

    Ok(AudioSamples::new(samples, sample_rate, 1))
}
