use std::net::{Ipv4Addr, Ipv6Addr, IpAddr};

/// Apply pattern substitution to generate PTR record from IP address
pub fn apply_pattern(pattern: &str, ip: IpAddr) -> String {
    match ip {
        IpAddr::V4(addr) => apply_ipv4_pattern(pattern, addr),
        IpAddr::V6(addr) => apply_ipv6_pattern(pattern, addr),
    }
}

/// Apply pattern substitution for IPv4
fn apply_ipv4_pattern(pattern: &str, addr: Ipv4Addr) -> String {
    let octets = addr.octets();
    let ip_dashed = format!("{}-{}-{}-{}", octets[0], octets[1], octets[2], octets[3]);

    pattern
        .replace("{1}", &octets[0].to_string())
        .replace("{2}", &octets[1].to_string())
        .replace("{3}", &octets[2].to_string())
        .replace("{4}", &octets[3].to_string())
        .replace("{ip}", &ip_dashed)
}

/// Apply pattern substitution for IPv6
fn apply_ipv6_pattern(pattern: &str, addr: Ipv6Addr) -> String {
    // Convert to full expanded form (all segments, no compression)
    let segments = addr.segments();
    let full = format!("{:04x}-{:04x}-{:04x}-{:04x}-{:04x}-{:04x}-{:04x}-{:04x}",
        segments[0], segments[1], segments[2], segments[3],
        segments[4], segments[5], segments[6], segments[7]);
    let short = addr.to_string().replace(':', "-");

    pattern
        .replace("{full}", &full)
        .replace("{short}", &short)
}

/// Parse PTR query name to IP address
/// Example: "5.1.168.192.in-addr.arpa." -> 192.168.1.5
pub fn parse_ptr_query_to_ip(qname: &str) -> Option<IpAddr> {
    let qname_lower = qname.to_lowercase();

    if let Some(ipv4_part) = qname_lower.strip_suffix(".in-addr.arpa.") {
        parse_ipv4_arpa(ipv4_part)
    } else if let Some(ipv4_part) = qname_lower.strip_suffix(".in-addr.arpa") {
        parse_ipv4_arpa(ipv4_part)
    } else if let Some(ipv6_part) = qname_lower.strip_suffix(".ip6.arpa.") {
        parse_ipv6_arpa(ipv6_part)
    } else if let Some(ipv6_part) = qname_lower.strip_suffix(".ip6.arpa") {
        parse_ipv6_arpa(ipv6_part)
    } else {
        None
    }
}

fn parse_ipv4_arpa(arpa: &str) -> Option<IpAddr> {
    let parts: Vec<&str> = arpa.split('.').collect();
    if parts.len() != 4 {
        return None;
    }

    // Reverse order: "5.1.168.192" -> [192, 168, 1, 5]
    let octets: Vec<u8> = parts
        .iter()
        .rev()
        .filter_map(|s| s.parse::<u8>().ok())
        .collect();

    if octets.len() == 4 {
        Some(IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])))
    } else {
        None
    }
}

fn parse_ipv6_arpa(arpa: &str) -> Option<IpAddr> {
    // IPv6 reverse: nibbles separated by dots, reversed
    // e.g., "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2"
    let nibbles: Vec<&str> = arpa.split('.').collect();
    if nibbles.len() != 32 {
        return None;
    }

    // Reverse nibbles and group into segments
    let mut segments = Vec::new();
    for chunk in nibbles.chunks(4).rev() {
        let hex_str: String = chunk.iter().rev().copied().collect();
        if let Ok(segment) = u16::from_str_radix(&hex_str, 16) {
            segments.push(segment);
        } else {
            return None;
        }
    }

    if segments.len() == 8 {
        Some(IpAddr::V6(Ipv6Addr::new(
            segments[0], segments[1], segments[2], segments[3],
            segments[4], segments[5], segments[6], segments[7],
        )))
    } else {
        None
    }
}

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

    #[test]
    fn test_parse_ipv4_arpa() {
        assert_eq!(
            parse_ptr_query_to_ip("5.1.168.192.in-addr.arpa."),
            Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 5)))
        );
        assert_eq!(
            parse_ptr_query_to_ip("10.0.0.10.in-addr.arpa"),
            Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 10)))
        );
    }

    #[test]
    fn test_apply_ipv4_pattern() {
        let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 5));
        assert_eq!(
            apply_pattern("{4}-{3}-{2}-{1}.hosts.example.com.", ip),
            "5-1-168-192.hosts.example.com."
        );
        assert_eq!(
            apply_pattern("host-{ip}.cloud.local.", ip),
            "host-192-168-1-5.cloud.local."
        );
        assert_eq!(
            apply_pattern("{4}.{3}.net.example.com.", ip),
            "5.1.net.example.com."
        );
    }
}
