//! `networksetup -getinfo <service>` 解析。
//!
//! 用于推算每张网卡的 IPv4 地址、子网掩码、网关，
//! 从而得到「直连网段」和「默认路由网关」。
//!
//! 输出示例：
//! ```text
//! DHCP Configuration
//! IP address: 192.168.108.120
//! Subnet mask: 255.255.252.0
//! Router: 192.168.110.1
//! ...
//! ```
//! Router 为 `(null)` 时表示该网卡无网关（纯内网网卡）。

use std::net::Ipv4Addr;

use crate::error::{AppError, AppResult};

/// 一张网卡的 IPv4 配置（来自 getinfo）。
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceIpv4 {
    /// 如 `192.168.108.120`。无 IP 时为 None（DHCP 未获取到等）。
    pub ip: Option<Ipv4Addr>,
    /// 如 `255.255.252.0`。无掩码时为 None。
    pub mask: Option<Ipv4Addr>,
    /// 网关。`Router: (null)` → None。
    pub router: Option<Ipv4Addr>,
}

/// 解析 `networksetup -getinfo <service>` 输出。
pub fn parse_getinfo(raw: &str) -> AppResult<ServiceIpv4> {
    let mut ip = None;
    let mut mask = None;
    let mut router = None;

    for line in raw.lines() {
        let line = line.trim();
        if let Some(v) = line.strip_prefix("IP address:") {
            ip = parse_ipv4_opt(v.trim());
        } else if let Some(v) = line.strip_prefix("Subnet mask:") {
            mask = parse_ipv4_opt(v.trim());
        } else if let Some(v) = line.strip_prefix("Router:") {
            router = parse_ipv4_opt(v.trim());
        }
    }

    Ok(ServiceIpv4 { ip, mask, router })
}

/// 解析 IPv4，`(null)` / `none` / 空串 → None，非法 → Err。
fn parse_ipv4_opt(s: &str) -> Option<Ipv4Addr> {
    let s = s.trim();
    if s.is_empty() || s == "(null)" || s.eq_ignore_ascii_case("none") {
        return None;
    }
    Some(
        s.parse::<Ipv4Addr>()
            .map_err(|_| AppError::Parse(format!("非法 IPv4: {s:?}")))
            .ok()?,
    )
}

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

    const DHCP: &str = "DHCP Configuration
IP address: 192.168.108.120
Subnet mask: 255.255.252.0
Router: 192.168.110.1
Client ID:
IPv6: Automatic
IPv6 IP address: none
IPv6 Router: none
";

    const MANUAL_NO_ROUTER: &str = "Manual Configuration
IP address: 10.88.21.101
Subnet mask: 255.255.0.0
Router: (null)
IPv6: Automatic
";

    #[test]
    fn parse_dhcp() {
        let r = parse_getinfo(DHCP).unwrap();
        assert_eq!(r.ip, Some(Ipv4Addr::new(192, 168, 108, 120)));
        assert_eq!(r.mask, Some(Ipv4Addr::new(255, 255, 252, 0)));
        assert_eq!(r.router, Some(Ipv4Addr::new(192, 168, 110, 1)));
    }

    #[test]
    fn parse_manual_no_router() {
        let r = parse_getinfo(MANUAL_NO_ROUTER).unwrap();
        assert_eq!(r.ip, Some(Ipv4Addr::new(10, 88, 21, 101)));
        assert_eq!(r.mask, Some(Ipv4Addr::new(255, 255, 0, 0)));
        assert_eq!(r.router, None);
    }

    #[test]
    fn null_router_becomes_none() {
        assert_eq!(parse_ipv4_opt("(null)"), None);
        assert_eq!(parse_ipv4_opt("none"), None);
        assert_eq!(parse_ipv4_opt(""), None);
        assert_eq!(parse_ipv4_opt("  1.2.3.4  "), Some(Ipv4Addr::new(1, 2, 3, 4)));
    }
}
