//! Reverse DNS Zone types for pattern-based PTR generation

use serde::{Deserialize, Serialize};
use ipnet::IpNet;
use crate::{Error, Result};

/// A reverse DNS zone with pattern-based PTR generation
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ReverseZone {
    pub id: String,
    pub org_id: String,
    pub project_id: Option<String>,
    pub cidr: String,              // "192.168.1.0/24" or "2001:db8::/32"
    pub arpa_zone: String,         // "1.168.192.in-addr.arpa." or "...ip6.arpa."
    pub ptr_pattern: String,       // e.g., "{4}-{3}-{2}-{1}.hosts.example.com."
    pub ttl: u32,
    pub created_at: u64,
    pub updated_at: u64,
}

/// Convert CIDR to in-addr.arpa or ip6.arpa zone name
pub fn cidr_to_arpa(cidr_str: &str) -> Result<String> {
    let cidr: IpNet = cidr_str.parse()
        .map_err(|_| Error::InvalidInput(format!("Invalid CIDR: {}", cidr_str)))?;

    match cidr {
        IpNet::V4(net) => {
            let octets = net.addr().octets();
            match net.prefix_len() {
                8 => Ok(format!("{}.in-addr.arpa.", octets[0])),
                16 => Ok(format!("{}.{}.in-addr.arpa.", octets[1], octets[0])),
                24 => Ok(format!("{}.{}.{}.in-addr.arpa.", octets[2], octets[1], octets[0])),
                32 => Ok(format!("{}.{}.{}.{}.in-addr.arpa.", octets[3], octets[2], octets[1], octets[0])),
                _ => Err(Error::InvalidInput(format!("Unsupported IPv4 prefix length: /{}", net.prefix_len()))),
            }
        }
        IpNet::V6(net) => {
            // Convert to nibbles for ip6.arpa
            let addr = net.addr();
            let segments = addr.segments();
            let prefix_nibbles = (net.prefix_len() / 4) as usize;

            // Convert segments to nibbles
            let mut nibbles = Vec::new();
            for segment in &segments {
                nibbles.push((segment >> 12) & 0xF);
                nibbles.push((segment >> 8) & 0xF);
                nibbles.push((segment >> 4) & 0xF);
                nibbles.push(segment & 0xF);
            }

            let arpa_part = nibbles[..prefix_nibbles]
                .iter()
                .rev()
                .map(|n| format!("{:x}", n))
                .collect::<Vec<_>>()
                .join(".");

            Ok(format!("{}.ip6.arpa.", arpa_part))
        }
    }
}

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

    #[test]
    fn test_ipv4_cidr_to_arpa() {
        assert_eq!(cidr_to_arpa("10.0.0.0/8").unwrap(), "10.in-addr.arpa.");
        assert_eq!(cidr_to_arpa("192.168.0.0/16").unwrap(), "168.192.in-addr.arpa.");
        assert_eq!(cidr_to_arpa("172.16.1.0/24").unwrap(), "1.16.172.in-addr.arpa.");
        assert_eq!(cidr_to_arpa("192.168.1.5/32").unwrap(), "5.1.168.192.in-addr.arpa.");
    }

    #[test]
    fn test_ipv6_cidr_to_arpa() {
        assert_eq!(cidr_to_arpa("2001:db8::/32").unwrap(), "8.b.d.0.1.0.0.2.ip6.arpa.");
        assert_eq!(cidr_to_arpa("2001:db8:1234::/48").unwrap(), "4.3.2.1.8.b.d.0.1.0.0.2.ip6.arpa.");
        assert_eq!(cidr_to_arpa("fe80::/64").unwrap(), "0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.");
    }

    #[test]
    fn test_unsupported_cidr() {
        assert!(cidr_to_arpa("192.168.0.0/20").is_err());
        assert!(cidr_to_arpa("invalid").is_err());
    }
}
