//! Gestion des requêtes HTTP Range (RFC 7233)
//!
//! Ce module fournit les outils pour parser, valider et calculer les intervalles de bytes
//! pour le streaming partiel. Il est conçu pour être robuste, performant et sécurisé.

use std::str::FromStr;
use thiserror::Error;

/// Représente une erreur liée au traitement des ranges
#[derive(Debug, Error, PartialEq, Eq)]
pub enum RangeError {
    #[error("Format de header Range invalide")]
    InvalidFormat,
    #[error("Unité de range non supportée (seul 'bytes' est supporté)")]
    UnsupportedUnit,
    #[error("La range demandée ne peut pas être satisfaite")]
    NotSatisfiable,
    #[error("Valeurs de range invalides (start > end)")]
    InvalidValues,
}

/// Type de range demandé par le client
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ByteRange {
    /// Range exact: `bytes=0-499` (start, end_inclusive)
    Exact(u64, u64),
    /// Range ouvert: `bytes=500-` (start)
    From(u64),
    /// Range suffixe: `bytes=-500` (last n bytes)
    Suffix(u64),
}

/// Résultat résolu d'une requête de range par rapport à un fichier
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedRange {
    /// Offset de début absolu dans le fichier
    pub start: u64,
    /// Offset de fin absolu (inclusif) dans le fichier
    pub end: u64,
    /// Longueur totale du contenu à renvoyer
    pub length: u64,
    /// Taille totale de la ressource
    pub total_size: u64,
}

impl FromStr for ByteRange {
    type Err = RangeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s.trim();
        
        // Vérifier le préfixe
        if !s.starts_with("bytes=") {
            return Err(RangeError::UnsupportedUnit);
        }

        // Extraire la partie valeur
        let value = &s[6..];
        
        // RFC 7233 ne supporte pas explicitement les ranges multiples pour ce cas d'usage simple
        // Si une virgule est présente, on rejette pour simplifier (ou on prend le premier)
        // Pour un serveur de streaming audio, on se concentre sur le single range.
        if value.contains(',') {
            // Pour l'instant, on rejette les multipart ranges pour simplifier la logique de réponse
            return Err(RangeError::InvalidFormat); 
        }

        if let Some(suffix_val) = value.strip_prefix('-') {
            // Cas Suffix: bytes=-500
            let len = suffix_val.parse::<u64>().map_err(|_| RangeError::InvalidFormat)?;
            if len == 0 {
                return Err(RangeError::InvalidFormat); // Suffixe 0 n'a pas de sens
            }
            return Ok(ByteRange::Suffix(len));
        }

        let parts: Vec<&str> = value.split('-').collect();
        match parts.as_slice() {
            [start_str, ""] => {
                // Cas From: bytes=500-
                let start = start_str.parse::<u64>().map_err(|_| RangeError::InvalidFormat)?;
                Ok(ByteRange::From(start))
            }
            [start_str, end_str] => {
                // Cas Exact: bytes=0-499
                let start = start_str.parse::<u64>().map_err(|_| RangeError::InvalidFormat)?;
                let end = end_str.parse::<u64>().map_err(|_| RangeError::InvalidFormat)?;

                if start > end {
                    return Err(RangeError::InvalidValues);
                }
                Ok(ByteRange::Exact(start, end))
            }
            _ => Err(RangeError::InvalidFormat),
        }
    }
}

impl ByteRange {
    /// Résout la range par rapport à une taille totale de fichier
    ///
    /// Retourne `None` si la range est `NotSatisfiable` (ex: start >= total_size)
    pub fn resolve(&self, total_size: u64) -> Result<ResolvedRange, RangeError> {
        if total_size == 0 {
            return Err(RangeError::NotSatisfiable);
        }

        let (start, end) = match *self {
            ByteRange::Exact(s, e) => {
                if s >= total_size {
                    return Err(RangeError::NotSatisfiable);
                }
                // Clamper la fin à la taille du fichier - 1
                (s, e.min(total_size - 1))
            }
            ByteRange::From(s) => {
                if s >= total_size {
                    return Err(RangeError::NotSatisfiable);
                }
                (s, total_size - 1)
            }
            ByteRange::Suffix(len) => {
                if len == 0 {
                    return Err(RangeError::NotSatisfiable);
                }
                if len >= total_size {
                    (0, total_size - 1)
                } else {
                    (total_size - len, total_size - 1)
                }
            }
        };

        Ok(ResolvedRange {
            start,
            end,
            length: end - start + 1,
            total_size,
        })
    }
}

/// Helper pour générer le header Content-Range
pub fn format_content_range(range: &ResolvedRange) -> String {
    format!("bytes {}-{}/{}", range.start, range.end, range.total_size)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_exact() {
        assert_eq!("bytes=0-499".parse(), Ok(ByteRange::Exact(0, 499)));
        assert_eq!("bytes=100-200".parse(), Ok(ByteRange::Exact(100, 200)));
    }

    #[test]
    fn test_parse_from() {
        assert_eq!("bytes=500-".parse(), Ok(ByteRange::From(500)));
        assert_eq!("bytes=0-".parse(), Ok(ByteRange::From(0)));
    }

    #[test]
    fn test_parse_suffix() {
        assert_eq!("bytes=-500".parse(), Ok(ByteRange::Suffix(500)));
    }

    #[test]
    fn test_parse_errors() {
        assert_eq!("".parse::<ByteRange>(), Err(RangeError::UnsupportedUnit));
        assert_eq!("bits=0-1".parse::<ByteRange>(), Err(RangeError::UnsupportedUnit));
        assert_eq!("bytes=abc".parse::<ByteRange>(), Err(RangeError::InvalidFormat));
        assert_eq!("bytes=100-50".parse::<ByteRange>(), Err(RangeError::InvalidValues));
        assert_eq!("bytes=-".parse::<ByteRange>(), Err(RangeError::InvalidFormat));
    }

    #[test]
    fn test_resolve_exact() {
        let total = 1000;
        let range = ByteRange::Exact(0, 499);
        let resolved = range.resolve(total).unwrap();
        assert_eq!(resolved, ResolvedRange { start: 0, end: 499, length: 500, total_size: 1000 });

        // Clamping
        let range = ByteRange::Exact(500, 2000);
        let resolved = range.resolve(total).unwrap();
        assert_eq!(resolved, ResolvedRange { start: 500, end: 999, length: 500, total_size: 1000 });
    }

    #[test]
    fn test_resolve_from() {
        let total = 1000;
        let range = ByteRange::From(900);
        let resolved = range.resolve(total).unwrap();
        assert_eq!(resolved, ResolvedRange { start: 900, end: 999, length: 100, total_size: 1000 });
    }

    #[test]
    fn test_resolve_suffix() {
        let total = 1000;
        
        // Normal suffix
        let range = ByteRange::Suffix(100);
        let resolved = range.resolve(total).unwrap();
        assert_eq!(resolved, ResolvedRange { start: 900, end: 999, length: 100, total_size: 1000 });

        // Oversized suffix
        let range = ByteRange::Suffix(2000);
        let resolved = range.resolve(total).unwrap();
        assert_eq!(resolved, ResolvedRange { start: 0, end: 999, length: 1000, total_size: 1000 });
    }

    #[test]
    fn test_resolve_errors() {
        let total = 100;
        let range = ByteRange::From(100); // Start == Total is invalid (0-indexed)
        assert_eq!(range.resolve(total), Err(RangeError::NotSatisfiable));

        let range = ByteRange::Exact(150, 200);
        assert_eq!(range.resolve(total), Err(RangeError::NotSatisfiable));
    }
    
    #[test]
    fn test_format_header() {
        let resolved = ResolvedRange { start: 0, end: 499, length: 500, total_size: 1000 };
        assert_eq!(format_content_range(&resolved), "bytes 0-499/1000");
    }
}
