//! 路由（CIDR 表示）及其与 netmask 的转换。
//!
//! `networksetup -getadditionalroutes` 返回 `dest mask router` 三元组，
//! 而 `setadditionalroutes` 接收相同格式。内部统一存 `{dest, prefix_len}` 便于排序/去重/展示，
//! 在调用命令前再做 netmask↔prefix_len 转换。

use std::net::Ipv4Addr;

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

/// 路由来源。决定能否在 TUI 内增删：
/// - `Manual`：手动附加路由（networksetup -getadditionalroutes），**可增删**。
/// - `Default`：默认路由（netstat default），只读展示。
/// - `Direct`：直连网段（网卡 IP/掩码决定的本地网段），只读展示。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RouteSource {
    Manual,
    Default,
    Direct,
}

impl RouteSource {
    /// 中文短标签，用于 UI 展示。
    pub fn label(self) -> &'static str {
        match self {
            RouteSource::Manual => "manual",
            RouteSource::Default => "default",
            RouteSource::Direct => "direct",
        }
    }

    /// 是否可在 TUI 内删除/修改。
    pub fn editable(self) -> bool {
        matches!(self, RouteSource::Manual)
    }
}

/// 一条 CIDR 路由，例如 `10.0.0.0/8`。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Route {
    pub dest: Ipv4Addr,
    pub prefix_len: u8,
}

impl Route {
    /// 从 `dest/prefix` 字符串解析，如 `"10.0.0.0/8"`。
    pub fn parse(s: &str) -> AppResult<Self> {
        let (d, p) = s
            .split_once('/')
            .ok_or_else(|| AppError::InvalidInput(format!("缺少 '/' 分隔符: {s:?}")))?;
        let dest: Ipv4Addr = d
            .parse()
            .map_err(|_| AppError::InvalidInput(format!("非法 IPv4 地址: {d:?}")))?;
        let prefix_len: u8 = p
            .parse()
            .map_err(|_| AppError::InvalidInput(format!("非法前缀长度: {p:?}")))?;
        if prefix_len > 32 {
            return Err(AppError::InvalidInput(format!(
                "前缀长度 {prefix_len} 超出 0..=32"
            )));
        }
        Ok(Self {
            dest,
            prefix_len,
        })
    }

    /// 从 networksetup 输出的 `dest mask` 两个字段构造。
    pub fn from_dest_mask(dest: &str, mask: &str) -> AppResult<Self> {
        let d: Ipv4Addr = dest
            .parse()
            .map_err(|_| AppError::Parse(format!("非法 IPv4 dest: {dest:?}")))?;
        let m: Ipv4Addr = mask
            .parse()
            .map_err(|_| AppError::Parse(format!("非法 netmask: {mask:?}")))?;
        let prefix_len = netmask_to_prefix_len(m)?;
        Ok(Self {
            dest: d,
            prefix_len,
        })
    }

    /// 格式化为 CIDR 字符串 `10.0.0.0/8`。
    pub fn to_cidr(self) -> String {
        format!("{}/{}", self.dest, self.prefix_len)
    }

    /// 返回 `(dest, mask, router)` 三元组，用于 `setadditionalroutes`。
    /// 这里 router 用 `0.0.0.0` 表示 on-link 路由（最常见的场景）。
    pub fn to_set_args(self) -> (String, String, String) {
        let mask = prefix_len_to_netmask(self.prefix_len);
        (
            self.dest.to_string(),
            mask.to_string(),
            "0.0.0.0".to_string(),
        )
    }
}

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

impl Ord for Route {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        // 先按 dest，再按 prefix_len，得到稳定且符合直觉的排序。
        self.dest
            .cmp(&other.dest)
            .then(self.prefix_len.cmp(&other.prefix_len))
    }
}

impl PartialOrd for Route {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

/// 前缀长度 → 点分 netmask。`/8` -> `255.0.0.0`。
pub fn prefix_len_to_netmask(prefix_len: u8) -> Ipv4Addr {
    debug_assert!(prefix_len <= 32);
    if prefix_len == 0 {
        return Ipv4Addr::new(0, 0, 0, 0);
    }
    let bits: u32 = (!0u32) << (32 - prefix_len as u32);
    Ipv4Addr::from(bits)
}

/// 点分 netmask → 前缀长度。校验必须是连续高位 1 的合法掩码。
pub fn netmask_to_prefix_len(mask: Ipv4Addr) -> AppResult<u8> {
    let bits = u32::from(mask);
    // 合法 netmask：高位连续 1，低位连续 0。形如 000...000111...1（反序）。
    // 即 `(bits | (bits - 1))` 全 1 时合法（等价于 bits+1 是 2 的幂 或 bits==0）。
    if bits != 0 && (bits | (bits.wrapping_sub(1))).count_ones() != 32 {
        return Err(AppError::Parse(format!(
            "非法 netmask（非连续高位 1）: {mask}"
        )));
    }
    Ok(bits.count_ones() as u8)
}

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

    #[test]
    fn parse_cidr_ok() {
        let r = Route::parse("10.0.0.0/8").unwrap();
        assert_eq!(r.dest, Ipv4Addr::new(10, 0, 0, 0));
        assert_eq!(r.prefix_len, 8);
    }

    #[test]
    fn parse_cidr_bad() {
        assert!(Route::parse("10.0.0.0").is_err());
        assert!(Route::parse("10.0.0.0/33").is_err());
        assert!(Route::parse("999.0.0.0/8").is_err());
        assert!(Route::parse("10.0.0.0/abc").is_err());
    }

    #[test]
    fn prefix_to_netmask() {
        assert_eq!(prefix_len_to_netmask(0), Ipv4Addr::new(0, 0, 0, 0));
        assert_eq!(prefix_len_to_netmask(8), Ipv4Addr::new(255, 0, 0, 0));
        assert_eq!(prefix_len_to_netmask(16), Ipv4Addr::new(255, 255, 0, 0));
        assert_eq!(prefix_len_to_netmask(24), Ipv4Addr::new(255, 255, 255, 0));
        assert_eq!(prefix_len_to_netmask(32), Ipv4Addr::new(255, 255, 255, 255));
    }

    #[test]
    fn netmask_to_prefix_roundtrip() {
        for p in 0..=32u8 {
            let m = prefix_len_to_netmask(p);
            assert_eq!(netmask_to_prefix_len(m).unwrap(), p);
        }
    }

    #[test]
    fn netmask_to_prefix_invalid() {
        // 255.0.255.0 不是合法掩码
        let bad = Ipv4Addr::new(255, 0, 255, 0);
        assert!(netmask_to_prefix_len(bad).is_err());
    }

    #[test]
    fn from_dest_mask_works() {
        let r = Route::from_dest_mask("172.16.0.0", "255.240.0.0").unwrap();
        assert_eq!(r.to_cidr(), "172.16.0.0/12");
    }

    #[test]
    fn to_set_args_on_link() {
        let r = Route::parse("192.168.1.0/24").unwrap();
        let (d, m, gw) = r.to_set_args();
        assert_eq!(d, "192.168.1.0");
        assert_eq!(m, "255.255.255.0");
        assert_eq!(gw, "0.0.0.0");
    }
}
