use crate::{Registry, Status};
use crate::table::{Entry4, Entry6};

#[derive(Debug)]
pub struct ParseError(pub String);

impl std::fmt::Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

fn parse_registry(s: &str) -> Option<Registry> {
    match s {
        "apnic"   => Some(Registry::Apnic),
        "arin"    => Some(Registry::Arin),
        "ripencc" => Some(Registry::RipeNcc),
        "lacnic"  => Some(Registry::Lacnic),
        "afrinic" => Some(Registry::Afrinic),
        _         => None,
    }
}

fn parse_status(s: &str) -> Status {
    match s {
        "allocated" => Status::Allocated,
        "assigned"  => Status::Assigned,
        "available" => Status::Available,
        "reserved"  => Status::Reserved,
        _           => Status::Other,
    }
}

/// Parse a RIR delegated stats file and return (ipv4_entries, ipv6_entries).
///
/// File format (one record per line):
///   registry|cc|type|start|value|date|status
///
/// Lines starting with `#` are comments; summary/header lines
/// (with `*` or a numeric version token) are skipped automatically
/// since they do not have `ipv4` or `ipv6` as the third field.
pub fn parse_delegated(content: &str) -> Result<(Vec<Entry4>, Vec<Entry6>), ParseError> {
    let mut v4: Vec<Entry4> = Vec::new();
    let mut v6: Vec<Entry6> = Vec::new();

    for line in content.lines() {
        let line = line.trim();
        if line.is_empty() || line.starts_with('#') {
            continue;
        }

        let fields: Vec<&str> = line.splitn(7, '|').collect();
        if fields.len() < 6 {
            continue;
        }

        let registry_str = fields[0];
        let cc           = fields[1];
        let record_type  = fields[2];
        let start_str    = fields[3];
        let value_str    = fields[4];
        let date_str     = fields[5];
        let status_str   = if fields.len() > 6 { fields[6] } else { "" };

        // Skip header/summary lines
        if cc.len() != 2 || cc == "*" {
            continue;
        }

        // Skip records from unknown registries (e.g. version/header lines)
        let Some(registry) = parse_registry(registry_str) else { continue };

        let status = parse_status(status_str);
        let date: u32 = date_str.parse().unwrap_or(0);

        let mut cc_bytes = [0u8; 2];
        cc_bytes.copy_from_slice(cc.as_bytes());

        match record_type {
            "ipv4" => {
                let start: u32 = match start_str.parse::<std::net::Ipv4Addr>() {
                    Ok(a) => u32::from(a),
                    Err(_) => continue,
                };
                let count: u32 = match value_str.parse() {
                    Ok(n) => n,
                    Err(_) => continue,
                };
                let end = start.saturating_add(count - 1);
                v4.push(Entry4 { start, end, cc: cc_bytes, registry, status, date });
            }
            "ipv6" => {
                let start: u128 = match start_str.parse::<std::net::Ipv6Addr>() {
                    Ok(a) => u128::from(a),
                    Err(_) => continue,
                };
                let prefix_len: u32 = match value_str.parse() {
                    Ok(n) => n,
                    Err(_) => continue,
                };
                if prefix_len > 128 {
                    continue;
                }
                let host_count: u128 = if prefix_len == 0 {
                    u128::MAX
                } else {
                    1u128 << (128 - prefix_len)
                };
                let end = start.saturating_add(host_count - 1);
                v6.push(Entry6 { start, end, cc: cc_bytes, registry, status, date });
            }
            _ => continue,
        }
    }

    Ok((v4, v6))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Registry, Status};

    const SAMPLE: &str = r#"
# RIR Statistics Exchange Format
apnic|JP|ipv4|1.0.0.0|256|20110811|allocated
apnic|CN|ipv4|1.0.1.0|256|20110414|assigned
apnic|JP|ipv6|2001:200::|32|19990813|allocated
apnic|*|summary|2|*|*|*|23042
"#;

    #[test]
    fn parses_ipv4_entries() {
        let (v4, _) = parse_delegated(SAMPLE).unwrap();
        assert_eq!(v4.len(), 2);
        assert_eq!(&v4[0].cc, b"JP");
        assert_eq!(v4[0].registry, Registry::Apnic);
        assert_eq!(v4[0].status, Status::Allocated);
        assert_eq!(v4[0].date, 20110811);
        assert_eq!(&v4[1].cc, b"CN");
        assert_eq!(v4[1].status, Status::Assigned);
    }

    #[test]
    fn parses_ipv6_entries() {
        let (_, v6) = parse_delegated(SAMPLE).unwrap();
        assert_eq!(v6.len(), 1);
        assert_eq!(&v6[0].cc, b"JP");
        assert_eq!(v6[0].registry, Registry::Apnic);
        assert_eq!(v6[0].status, Status::Allocated);
        assert_eq!(v6[0].date, 19990813);
    }

    #[test]
    fn ipv4_range_correct() {
        let (v4, _) = parse_delegated(SAMPLE).unwrap();
        let start = u32::from("1.0.0.0".parse::<std::net::Ipv4Addr>().unwrap());
        assert_eq!(v4[0].start, start);
        assert_eq!(v4[0].end, start + 255);
    }

    #[test]
    fn skips_summary_and_comment_lines() {
        let (v4, v6) = parse_delegated(SAMPLE).unwrap();
        assert!(v4.iter().all(|e| e.cc != *b"**"));
        assert!(v6.iter().all(|e| e.cc != *b"**"));
    }
}
