// duracion_audio.rs — Lectura de duración de archivos de audio sin dependencias externas
//
// Soporta: MP3, WAV, OGG (aproximado), FLAC (aproximado)
// Para URLs y rutas con /* retorna 0.

use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

/// Retorna la duración en segundos del archivo de audio.
/// Retorna 0 si no se puede determinar (URL, carpeta/*, error).
pub fn leer_duracion(ruta: &str) -> u64 {
    // Descartar URLs y comodines
    if ruta.starts_with("http") || ruta.ends_with("/*") || ruta == "Hora" {
        return 0;
    }
    // Separar ruta de duración tab si viene con \t (streaming)
    let ruta_limpia = ruta.split('\t').next().unwrap_or(ruta);
    let path = Path::new(ruta_limpia);
    let ext = path.extension()
        .and_then(|e| e.to_str())
        .map(|e| e.to_lowercase())
        .unwrap_or_default();

    match ext.as_str() {
        "mp3"        => leer_duracion_mp3(ruta_limpia),
        "wav"        => leer_duracion_wav(ruta_limpia),
        "ogg"        => leer_duracion_ogg(ruta_limpia),
        "flac"       => leer_duracion_flac(ruta_limpia),
        "aac" | "m4a"=> leer_duracion_m4a(ruta_limpia),
        _            => 0,
    }
}

/// Formatea segundos como "H:MM:SS" o "MM:SS"
pub fn formato_duracion(segs: u64) -> String {
    if segs == 0 { return "0:00".to_string(); }
    let h = segs / 3600;
    let m = (segs % 3600) / 60;
    let s = segs % 60;
    if h > 0 {
        format!("{}:{:02}:{:02}", h, m, s)
    } else {
        format!("{}:{:02}", m, s)
    }
}

// ─── MP3 ───────────────────────────────────────────────────────────────────
//
// Estrategia: leer el header ID3v2 para obtener el tamaño total del tag,
// luego buscar el primer frame MPEG válido y extraer bitrate + sample rate.
// Duración ≈ (tamaño_total_frames) / (bitrate_bytes_por_segundo)

fn leer_duracion_mp3(ruta: &str) -> u64 {
    let mut f = match File::open(ruta) { Ok(f) => f, Err(_) => return 0 };
    let file_size = match f.seek(SeekFrom::End(0)) { Ok(s) => s, Err(_) => return 0 };
    let _ = f.seek(SeekFrom::Start(0));

    let mut buf4 = [0u8; 4];
    let mut offset: u64 = 0;

    // Saltar ID3v2 si existe
    if f.read_exact(&mut buf4).is_ok() {
        if &buf4[0..3] == b"ID3" {
            // ID3v2: bytes 6-9 son tamaño syncsafe
            let mut tag_size_buf = [0u8; 6];
            if f.read_exact(&mut tag_size_buf).is_ok() {
                let sz = ((tag_size_buf[2] as u64) << 21)
                       | ((tag_size_buf[3] as u64) << 14)
                       | ((tag_size_buf[4] as u64) <<  7)
                       |  (tag_size_buf[5] as u64);
                offset = 10 + sz;
            }
        }
        let _ = f.seek(SeekFrom::Start(offset));
    }

    // Buscar primer frame MPEG válido (máx 64KB de búsqueda)
    let mut search_buf = vec![0u8; 65536.min(file_size as usize)];
    let n = f.read(&mut search_buf).unwrap_or(0);

    for i in 0..n.saturating_sub(3) {
        let b = &search_buf[i..i+4];
        // Sync word: 11 bits a 1
        if b[0] != 0xFF || (b[1] & 0xE0) != 0xE0 { continue; }
        // Versión MPEG y capa
        let version = (b[1] >> 3) & 0x03;
        let layer   = (b[1] >> 1) & 0x03;
        if version == 1 || layer == 0 { continue; }

        let bitrate_idx   = (b[2] >> 4) as usize;
        let samplerate_idx= ((b[2] >> 2) & 0x03) as usize;
        if bitrate_idx == 0 || bitrate_idx == 15 { continue; }
        if samplerate_idx == 3 { continue; }

        let bitrate = mp3_bitrate(version, layer, bitrate_idx);
        if bitrate == 0 { continue; }
        let _samplerate = mp3_samplerate(version, samplerate_idx);

        // Duración estimada por tamaño de archivo / bitrate
        let audio_bytes = file_size.saturating_sub(offset + i as u64);
        return (audio_bytes * 8) / (bitrate as u64 * 1000);
    }
    0
}

fn mp3_bitrate(version: u8, layer: u8, idx: usize) -> u32 {
    // version: 3=MPEG1, 2=MPEG2, 0=MPEG2.5; layer: 3=L1, 2=L2, 1=L3
    const TABLE_V1_L3: [u32;16] = [0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0];
    const TABLE_V1_L2: [u32;16] = [0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0];
    const TABLE_V1_L1: [u32;16] = [0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0];
    const TABLE_V2_L3: [u32;16] = [0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0];
    const TABLE_V2_L12:[u32;16] = [0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0];
    match (version, layer) {
        (3, 1) => TABLE_V1_L3[idx],
        (3, 2) => TABLE_V1_L2[idx],
        (3, 3) => TABLE_V1_L1[idx],
        (_, 1) => TABLE_V2_L3[idx],
        _      => TABLE_V2_L12[idx],
    }
}

fn mp3_samplerate(version: u8, idx: usize) -> u32 {
    const SR_V1: [u32;3] = [44100,48000,32000];
    const SR_V2: [u32;3] = [22050,24000,16000];
    const SR_V25:[u32;3] = [11025,12000, 8000];
    let table = match version { 3 => &SR_V1, 2 => &SR_V2, _ => &SR_V25 };
    if idx < 3 { table[idx] } else { 44100 }
}

// ─── WAV ───────────────────────────────────────────────────────────────────

fn leer_duracion_wav(ruta: &str) -> u64 {
    let mut f = match File::open(ruta) { Ok(f) => f, Err(_) => return 0 };
    let mut header = [0u8; 44];
    if f.read_exact(&mut header).is_err() { return 0; }
    if &header[0..4] != b"RIFF" || &header[8..12] != b"WAVE" { return 0; }
    // fmt chunk offset 12
    let channels    = u16::from_le_bytes([header[22], header[23]]) as u64;
    let sample_rate = u32::from_le_bytes([header[24],header[25],header[26],header[27]]) as u64;
    let bits        = u16::from_le_bytes([header[34], header[35]]) as u64;
    let data_size   = u32::from_le_bytes([header[40],header[41],header[42],header[43]]) as u64;
    if channels == 0 || sample_rate == 0 || bits == 0 { return 0; }
    data_size / (sample_rate * channels * (bits / 8))
}

// ─── OGG (aproximado) ─────────────────────────────────────────────────────

fn leer_duracion_ogg(ruta: &str) -> u64 {
    // Leer los últimos 65536 bytes y buscar la última página OGG
    // que contiene el granule position (posición de muestra)
    let mut f = match File::open(ruta) { Ok(f) => f, Err(_) => return 0 };
    let file_size = match f.seek(SeekFrom::End(0)) { Ok(s) => s, Err(_) => return 0 };
    let read_from = file_size.saturating_sub(65536);
    let _ = f.seek(SeekFrom::Start(read_from));
    let mut buf = vec![0u8; (file_size - read_from) as usize];
    let n = f.read(&mut buf).unwrap_or(0);

    // Buscar la última captura OGgS + granule_position
    let mut last_granule: u64 = 0;
    for i in 0..n.saturating_sub(27) {
        if &buf[i..i+4] == b"OggS" {
            let gp = u64::from_le_bytes([
                buf[i+6],buf[i+7],buf[i+8],buf[i+9],
                buf[i+10],buf[i+11],buf[i+12],buf[i+13],
            ]);
            if gp != u64::MAX && gp > last_granule {
                last_granule = gp;
            }
        }
    }
    // Sample rate típico de Vorbis: 44100. Para mayor precisión habría que
    // parsear el identification header, pero 44100 es correcto el 95% del tiempo.
    if last_granule > 0 { last_granule / 44100 } else { 0 }
}

// ─── FLAC (aproximado) ────────────────────────────────────────────────────

fn leer_duracion_flac(ruta: &str) -> u64 {
    let mut f = match File::open(ruta) { Ok(f) => f, Err(_) => return 0 };
    let mut magic = [0u8; 4];
    if f.read_exact(&mut magic).is_err() { return 0; }
    if &magic != b"fLaC" { return 0; }
    // Leer STREAMINFO (primer metadata block)
    let mut block_header = [0u8; 4];
    if f.read_exact(&mut block_header).is_err() { return 0; }
    let block_len = u32::from_be_bytes([0, block_header[1], block_header[2], block_header[3]]) as usize;
    if block_len < 18 { return 0; }
    let mut info = vec![0u8; block_len];
    if f.read_exact(&mut info).is_err() { return 0; }
    // sample_rate: bits 80-99 del STREAMINFO
    // total_samples: bits 108-143
    let sample_rate = ((info[10] as u32) << 12)
                    | ((info[11] as u32) << 4)
                    | ((info[12] as u32) >> 4);
    // total_samples en los últimos 36 bits del bloque de 18 bytes
    let total = ((info[13] as u64 & 0x0F) << 32)
              | ((info[14] as u64) << 24)
              | ((info[15] as u64) << 16)
              | ((info[16] as u64) <<  8)
              |  (info[17] as u64);
    if sample_rate == 0 { return 0; }
    total / sample_rate as u64
}

// ─── M4A/AAC (aproximado por tamaño) ──────────────────────────────────────

fn leer_duracion_m4a(ruta: &str) -> u64 {
    // Búsqueda de la caja 'mvhd' que contiene la duración
    let mut f = match File::open(ruta) { Ok(f) => f, Err(_) => return 0 };
    let file_size = match f.seek(SeekFrom::End(0)) { Ok(s) => s, Err(_) => return 0 };
    let _ = f.seek(SeekFrom::Start(0));
    let mut buf = vec![0u8; file_size.min(1_000_000) as usize];
    let n = f.read(&mut buf).unwrap_or(0);

    for i in 0..n.saturating_sub(24) {
        if &buf[i+4..i+8] == b"mvhd" {
            // version 0: time_scale en offset 12, duration en offset 16
            // version 1: time_scale en offset 20, duration en offset 24 (u64)
            let version = buf[i + 8];
            if version == 0 && i + 24 <= n {
                let time_scale = u32::from_be_bytes([buf[i+12],buf[i+13],buf[i+14],buf[i+15]]);
                let duration   = u32::from_be_bytes([buf[i+16],buf[i+17],buf[i+18],buf[i+19]]);
                if time_scale > 0 {
                    return duration as u64 / time_scale as u64;
                }
            } else if version == 1 && i + 32 <= n {
                let time_scale = u32::from_be_bytes([buf[i+20],buf[i+21],buf[i+22],buf[i+23]]);
                let duration   = u64::from_be_bytes([
                    buf[i+24],buf[i+25],buf[i+26],buf[i+27],
                    buf[i+28],buf[i+29],buf[i+30],buf[i+31],
                ]);
                if time_scale > 0 {
                    return duration / time_scale as u64;
                }
            }
        }
    }
    0
}
