//! 应用状态机：聚合数据、视图状态、弹窗、用户输入。
//!
//! 设计要点：
//! - `bindings`: 聚合三类来源（manual / default / direct）的路由，按 (Route → 命中条目列表) 组织。
//!   左侧列表按"命中服务中的最大 order（优先级最低）"升序排列（优先级低在上）。
//! - 弹窗采用有限状态 [`Mode`]，事件循环根据当前 Mode 决定按键去向。
//! - 仅 `Manual` 来源可在 TUI 内增删；default/direct 只读展示。

use std::net::Ipv4Addr;

use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

use crate::error::{AppError, AppResult};
use crate::net::{
    self,
    route::{Route, RouteSource},
    service::NetworkService,
};
// `key!` / `log!` 是 `#[macro_use]` 全局宏，通过 $crate 路径调用，无需 import。

/// 一条路由的某一次命中：在哪张网卡（服务）上、来源、网关。
#[derive(Debug, Clone)]
pub struct RouteEntry {
    /// 命中的服务（在 `App::services` 中的索引）。
    pub service_idx: usize,
    /// 来源：manual / default / direct。
    pub source: RouteSource,
    /// 网关。直连网段为 None（on-link）；默认路由与手动路由通常有网关。
    pub gateway: Option<Ipv4Addr>,
}

/// 一个 IP 段 → 它在系统里的全部命中（可能多张网卡/多种来源）。
#[derive(Debug, Clone)]
pub struct RouteBinding {
    pub route: Route,
    /// 命中条目。manual 排最前（方便增删定位），其次 default，最后 direct。
    pub entries: Vec<RouteEntry>,
}

impl RouteBinding {
    /// 计算排序键：命中服务中的"最大 order"（即优先级最低的那个）。
    /// 禁用服务 order=0，视为最低优先级。
    fn max_order(&self, services: &[NetworkService]) -> u32 {
        self.entries
            .iter()
            .map(|e| order_for_sort(&services[e.service_idx]))
            .max()
            .unwrap_or(u32::MAX)
    }

    /// 是否含至少一条 manual 来源（决定该 IP 段能否删除）。
    pub fn has_manual(&self) -> bool {
        self.entries.iter().any(|e| e.source == RouteSource::Manual)
    }
}

/// 排序用的 order：禁用服务用一个很大的值，确保排在启用服务之后。
fn order_for_sort(s: &NetworkService) -> u32 {
    if s.enabled {
        s.order
    } else {
        u32::MAX / 2
    }
}

/// binding 内部 entries 排序用的来源优先级：manual < default < direct。
fn source_rank(s: RouteSource) -> u8 {
    match s {
        RouteSource::Manual => 0,
        RouteSource::Default => 1,
        RouteSource::Direct => 2,
    }
}

/// 弹窗/焦点模式。
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum Mode {
    #[default]
    Normal,
    /// 添加路由弹窗
    AddRoute(AddRouteState),
    /// 删除确认弹窗（删除选中的某个绑定）
    DeleteConfirm {
        binding_idx: usize,
        service_idx_in_binding: usize,
    },
    /// 帮助
    Help,
    /// 错误展示
    Error,
}

/// 添加路由弹窗的状态。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddRouteState {
    /// 用户输入的 CIDR 文本
    pub cidr_input: String,
    /// 光标位置
    pub cursor: usize,
    /// 当前选中的目标服务（在 services 列表中的索引）
    pub selected_service: usize,
    /// 焦点：输入框 / 服务选择
    pub focus: AddFocus,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddFocus {
    Input,
    ServiceList,
}

impl Default for AddRouteState {
    fn default() -> Self {
        Self {
            cidr_input: String::new(),
            cursor: 0,
            selected_service: 0,
            focus: AddFocus::Input,
        }
    }
}

/// 应用主状态。
#[derive(Debug, Default)]
pub struct App {
    /// 全部网络服务（含禁用）。
    pub services: Vec<NetworkService>,
    /// 聚合后的路由绑定（已排序）。
    pub bindings: Vec<RouteBinding>,
    /// 左侧列表选中的索引。
    pub selected: usize,
    /// 右侧详情（绑定到哪些网卡）选中的索引。
    pub detail_selected: usize,
    /// 当前模式/弹窗。
    pub mode: Mode,
    /// 日志缓冲（底部状态栏循环展示最近一条）。
    pub logs: Vec<String>,
    /// 最近一次错误（错误弹窗内容）。
    pub last_error: Option<String>,
}

impl App {
    pub fn new() -> Self {
        Self::default()
    }

    /// 向日志缓冲追加一条（供 `log!` 宏使用）。
    pub fn push_log(&mut self, msg: String) {
        // 只保留最近 100 条，避免无界增长
        if self.logs.len() >= 100 {
            self.logs.remove(0);
        }
        self.logs.push(msg);
    }

    /// 记录错误并切到错误弹窗。
    pub fn set_error(&mut self, e: AppError) {
        self.last_error = Some(e.to_string());
        self.mode = Mode::Error;
    }

    /// 重新拉取三类数据并聚合：
    /// 1. 服务列表（networksetup -listnetworkserviceorder）
    /// 2. 手动附加路由（networksetup -getadditionalroutes）—— 可增删
    /// 3. 默认路由 + 直连网段（netstat -rn）—— 只读
    pub fn refresh(&mut self) -> AppResult<()> {
        let services = net::list_services()?;

        // device -> service_idx 映射（netstat 给的是 interface 名如 en1）。
        // 同一 device 可能对应多个服务，取第一个启用的；否则取第一个。
        let mut device_to_service: std::collections::HashMap<&str, usize> =
            std::collections::HashMap::new();
        for (i, s) in services.iter().enumerate() {
            device_to_service
                .entry(s.device.as_str())
                .or_insert_with(|| {
                    // 优先选用启用的服务
                    i
                });
        }
        // 第二轮：把启用服务覆盖上去，确保映射指向可用服务。
        for (i, s) in services.iter().enumerate() {
            if s.enabled {
                device_to_service.insert(s.device.as_str(), i);
            }
        }

        let lookup_service_by_device = |dev: &str| -> Option<usize> {
            device_to_service.get(dev).copied()
        };

        // 用 (Route, source, service_idx, gateway) 作为聚合中间项。
        // key = Route；value = Vec<RouteEntry>。
        use std::collections::BTreeMap;
        let mut map: BTreeMap<Route, Vec<RouteEntry>> = BTreeMap::new();

        // ─── 1) 手动附加路由 ───
        for (i, s) in services.iter().enumerate() {
            match net::get_routes(&s.name) {
                Ok(routes) => {
                    for r in routes {
                        map.entry(r).or_default().push(RouteEntry {
                            service_idx: i,
                            source: RouteSource::Manual,
                            // getadditionalroutes 的 router 字段这里统一当 None
                            // （精确网关对增删无意义，UI 显示来源即可）
                            gateway: None,
                        });
                    }
                }
                Err(e) => log!(self, "读取 {} 附加路由失败: {e}", s.name),
            }
        }

        // ─── 2) netstat: 默认路由 + 直连网段 ───
        match net::netstat_routes() {
            Ok(ns_routes) => {
                for nr in ns_routes {
                    let Some(svc_idx) = lookup_service_by_device(&nr.interface) else {
                        // netstat 里的网卡（如 utun4）可能不在服务列表，跳过
                        continue;
                    };
                    let route = Route {
                        dest: nr.dest,
                        prefix_len: nr.prefix_len,
                    };
                    let source = if nr.is_default {
                        RouteSource::Default
                    } else {
                        RouteSource::Direct
                    };
                    map.entry(route).or_default().push(RouteEntry {
                        service_idx: svc_idx,
                        source,
                        gateway: nr.gateway,
                    });
                }
            }
            Err(e) => log!(self, "读取 netstat 路由失败: {e}"),
        }

        // 每个 binding 内部 entries 排序：manual 在前，其次 default，最后 direct；
        // 同来源按服务 order 升序。
        let mut bindings: Vec<RouteBinding> = map
            .into_iter()
            .map(|(route, mut entries)| {
                entries.sort_by(|a, b| {
                    source_rank(a.source)
                        .cmp(&source_rank(b.source))
                        .then_with(|| {
                            order_for_sort(&services[a.service_idx])
                                .cmp(&order_for_sort(&services[b.service_idx]))
                        })
                });
                RouteBinding { route, entries }
            })
            .collect();

        // 列表整体排序：优先级低（max_order 大）的在前；default/direct 优先级语义
        // 用其命中服务的 order 表示。
        bindings.sort_by(|a, b| {
            b.max_order(&services)
                .cmp(&a.max_order(&services))
                .then_with(|| a.route.cmp(&b.route))
        });

        self.services = services;
        self.bindings = bindings;
        if self.selected >= self.bindings.len() && !self.bindings.is_empty() {
            self.selected = self.bindings.len() - 1;
        }
        self.detail_selected = 0;

        let total: usize = self.bindings.iter().map(|b| b.entries.len()).sum();
        let manual_cnt = self
            .bindings
            .iter()
            .filter(|b| b.has_manual())
            .count();
        log!(
            self,
            "刷新完成: {} 服务, {} 网段, {} 命中 (其中 {} 可编辑)",
            self.services.len(),
            self.bindings.len(),
            total,
            manual_cnt
        );
        Ok(())
    }

    /// 处理一个按键事件。返回 `false` 表示应退出程序。
    pub fn handle_event(&mut self, ev: KeyEvent) -> anyhow::Result<bool> {
        // 全局快捷键
        if key!(ev, ctrl => 'c') || key!(ev, ctrl => 'z') {
            return Ok(false);
        }

        match &mut self.mode {
            Mode::Normal => self.handle_normal(ev),
            Mode::AddRoute(_) => self.handle_add_route(ev),
            Mode::DeleteConfirm { .. } => self.handle_delete_confirm(ev),
            Mode::Help => {
                if key!(ev, Esc) || key!(ev, Enter) || key!(ev, 'q') {
                    self.mode = Mode::Normal;
                }
                Ok(true)
            }
            Mode::Error => {
                if key!(ev, Esc) || key!(ev, Enter) || key!(ev, 'q') || key!(ev, ' ') {
                    self.mode = Mode::Normal;
                    self.last_error = None;
                }
                Ok(true)
            }
        }
    }

    // ─────────────── Normal 模式 ───────────────

    fn handle_normal(&mut self, ev: KeyEvent) -> anyhow::Result<bool> {
        if key!(ev, 'q') || key!(ev, Esc) {
            return Ok(false);
        }
        if key!(ev, 'r') {
            if let Err(e) = self.refresh() {
                self.set_error(e);
            }
            return Ok(true);
        }
        if key!(ev, '?') {
            self.mode = Mode::Help;
            return Ok(true);
        }
        if key!(ev, 'a') {
            let mut st = AddRouteState::default();
            // 默认选中第一个启用的服务
            st.selected_service = self
                .services
                .iter()
                .position(|s| s.enabled)
                .unwrap_or(0);
            self.mode = Mode::AddRoute(st);
            return Ok(true);
        }
        if key!(ev, 'd') {
            if let Some(b) = self.bindings.get(self.selected) {
                // 只允许删除 manual 来源的命中。在当前选中的 entry 上查找最近的 manual。
                let pos = (0..b.entries.len())
                    // 从 detail_selected 开始向前后找 manual entry
                    .min_by_key(|&i| {
                        let is_manual = b.entries[i].source == RouteSource::Manual;
                        let dist = (i as isize - self.detail_selected as isize).abs();
                        (!is_manual, dist)
                    })
                    .filter(|&i| b.entries[i].source == RouteSource::Manual);
                if let Some(pos) = pos {
                    self.mode = Mode::DeleteConfirm {
                        binding_idx: self.selected,
                        service_idx_in_binding: pos,
                    };
                } else {
                    self.set_error(AppError::other(
                        "该路由不是手动附加路由（manual），无法在此删除。\n默认路由/直连网段由网卡配置或服务优先级决定。",
                    ));
                }
            }
            return Ok(true);
        }

        // 列表导航
        if key!(ev, Up) || key!(ev, 'k') {
            if self.selected > 0 {
                self.selected -= 1;
                self.detail_selected = 0;
            }
            return Ok(true);
        }
        if key!(ev, Down) || key!(ev, 'j') {
            if self.selected + 1 < self.bindings.len() {
                self.selected += 1;
                self.detail_selected = 0;
            }
            return Ok(true);
        }
        // 右侧详情导航
        if key!(ev, Left) || key!(ev, 'h') {
            if self.detail_selected > 0 {
                self.detail_selected -= 1;
            }
            return Ok(true);
        }
        if key!(ev, Right) || key!(ev, 'l') {
            if let Some(b) = self.bindings.get(self.selected) {
                if self.detail_selected + 1 < b.entries.len() {
                    self.detail_selected += 1;
                }
            }
            return Ok(true);
        }
        Ok(true)
    }

    // ─────────────── 添加路由 ───────────────

    fn handle_add_route(&mut self, ev: KeyEvent) -> anyhow::Result<bool> {
        // 取出当前弹窗状态（借用分离）
        let st = match &mut self.mode {
            Mode::AddRoute(st) => st,
            _ => unreachable!(),
        };

        if key!(ev, Esc) {
            self.mode = Mode::Normal;
            return Ok(true);
        }

        // Tab / Shift-Tab 切换焦点
        if matches!(ev.code, KeyCode::Tab) {
            st.focus = match st.focus {
                AddFocus::Input => AddFocus::ServiceList,
                AddFocus::ServiceList => AddFocus::Input,
            };
            return Ok(true);
        }
        if matches!(ev.code, KeyCode::BackTab) {
            st.focus = match st.focus {
                AddFocus::Input => AddFocus::ServiceList,
                AddFocus::ServiceList => AddFocus::Input,
            };
            return Ok(true);
        }

        match st.focus {
            AddFocus::Input => {
                match ev.code {
                    KeyCode::Char(c) if ev.modifiers == KeyModifiers::NONE => {
                        st.cidr_input.insert(st.cursor, c);
                        st.cursor += c.len_utf8();
                    }
                    KeyCode::Backspace => {
                        if st.cursor > 0 {
                            // 按字符边界退格
                            let prev = st.cidr_input[..st.cursor]
                                .char_indices()
                                .last()
                                .map(|(i, _)| i)
                                .unwrap_or(0);
                            st.cidr_input.replace_range(prev..st.cursor, "");
                            st.cursor = prev;
                        }
                    }
                    KeyCode::Left => {
                        if let Some((i, _)) = st.cidr_input[..st.cursor].char_indices().last() {
                            st.cursor = i;
                        }
                    }
                    KeyCode::Right => {
                        if let Some((_, c)) = st.cidr_input[st.cursor..].char_indices().next() {
                            st.cursor += c.len_utf8();
                        }
                    }
                    KeyCode::Enter => {
                        // 切到服务列表继续
                        st.focus = AddFocus::ServiceList;
                    }
                    _ => {}
                }
                Ok(true)
            }
            AddFocus::ServiceList => {
                if key!(ev, Up) || key!(ev, 'k') {
                    if st.selected_service > 0 {
                        st.selected_service -= 1;
                    }
                    return Ok(true);
                }
                if key!(ev, Down) || key!(ev, 'j') {
                    if st.selected_service + 1 < self.services.len() {
                        st.selected_service += 1;
                    }
                    return Ok(true);
                }
                if key!(ev, Enter) {
                    // 提交：复制出状态再调用 do_add_route（避免借用冲突）
                    let cidr = st.cidr_input.clone();
                    let svc_idx = st.selected_service;
                    // 先退出弹窗，再做实际操作
                    self.mode = Mode::Normal;
                    self.do_add_route(&cidr, svc_idx)?;
                    return Ok(true);
                }
                Ok(true)
            }
        }
    }

    fn do_add_route(&mut self, cidr: &str, service_idx: usize) -> anyhow::Result<()> {
        let route = match Route::parse(cidr) {
            Ok(r) => r,
            Err(e) => {
                self.set_error(e);
                return Ok(());
            }
        };
        let service = match self.services.get(service_idx) {
            Some(s) => s.clone(),
            None => {
                self.set_error(AppError::other("未选择有效的服务"));
                return Ok(());
            }
        };

        // 读现有路由（不提权）
        let mut routes = match net::get_routes(&service.name) {
            Ok(r) => r,
            Err(e) => {
                self.set_error(e);
                return Ok(());
            }
        };
        // 去重追加
        if !routes.contains(&route) {
            routes.push(route);
            routes.sort();
        }
        // 提权回写
        if let Err(e) = net::set_routes(&service.name, &routes) {
            self.set_error(e);
            return Ok(());
        }
        log!(
            self,
            "已添加路由 {} 到 {}",
            route,
            service.device
        );
        // 刷新
        if let Err(e) = self.refresh() {
            self.set_error(e);
        }
        Ok(())
    }

    // ─────────────── 删除路由 ───────────────

    fn handle_delete_confirm(&mut self, ev: KeyEvent) -> anyhow::Result<bool> {
        let (binding_idx, pos) = match self.mode {
            Mode::DeleteConfirm {
                binding_idx,
                service_idx_in_binding,
            } => (binding_idx, service_idx_in_binding),
            _ => unreachable!(),
        };

        if key!(ev, Esc) || key!(ev, 'n') {
            self.mode = Mode::Normal;
            return Ok(true);
        }
        if key!(ev, Enter) || key!(ev, 'y') {
            self.mode = Mode::Normal;
            self.do_delete_route(binding_idx, pos)?;
            return Ok(true);
        }
        Ok(true)
    }

    fn do_delete_route(&mut self, binding_idx: usize, pos: usize) -> anyhow::Result<()> {
        let (route, service) = match self.bindings.get(binding_idx) {
            Some(b) => {
                let entry = match b.entries.get(pos) {
                    Some(e) => e,
                    None => {
                        self.set_error(AppError::other("无效的删除目标"));
                        return Ok(());
                    }
                };
                if entry.source != RouteSource::Manual {
                    self.set_error(AppError::other(format!(
                        "无法删除：该命中来源为 {:?}（仅 manual 可删除）",
                        entry.source
                    )));
                    return Ok(());
                }
                (b.route, self.services.get(entry.service_idx).cloned())
            }
            None => {
                self.set_error(AppError::other("无效的删除目标"));
                return Ok(());
            }
        };
        let service = match service {
            Some(s) => s,
            None => {
                self.set_error(AppError::other("服务信息缺失"));
                return Ok(());
            }
        };

        // 读现有路由，移除该条
        let mut routes = match net::get_routes(&service.name) {
            Ok(r) => r,
            Err(e) => {
                self.set_error(e);
                return Ok(());
            }
        };
        routes.retain(|r| *r != route);
        if let Err(e) = net::set_routes(&service.name, &routes) {
            self.set_error(e);
            return Ok(());
        }
        log!(self, "已从 {} 删除路由 {}", service.device, route);
        if let Err(e) = self.refresh() {
            self.set_error(e);
        }
        Ok(())
    }
}

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

    #[test]
    fn add_route_focus_toggle() {
        let mut st = AddRouteState::default();
        assert_eq!(st.focus, AddFocus::Input);
        // 模拟 Tab（这里直接操作状态结构，因为按键测试需要 crossterm 构造）
        st.focus = AddFocus::ServiceList;
        assert_eq!(st.focus, AddFocus::ServiceList);
    }

    #[test]
    fn binding_max_order_uses_enabled() {
        let services = vec![
            NetworkService {
                order: 1,
                name: "Wi-Fi".into(),
                hardware_port: "Wi-Fi".into(),
                device: "en1".into(),
                enabled: true,
            },
            NetworkService {
                order: 0,
                name: "Eth".into(),
                hardware_port: "Ethernet".into(),
                device: "en0".into(),
                enabled: false,
            },
        ];
        let b = RouteBinding {
            route: Route {
                dest: Ipv4Addr::new(10, 0, 0, 0),
                prefix_len: 8,
            },
            entries: vec![
                RouteEntry {
                    service_idx: 0,
                    source: RouteSource::Manual,
                    gateway: None,
                },
                RouteEntry {
                    service_idx: 1,
                    source: RouteSource::Direct,
                    gateway: None,
                },
            ],
        };
        // 禁用服务排到很后面
        assert!(b.max_order(&services) >= u32::MAX / 2);
        assert!(b.has_manual());
    }

    #[test]
    fn route_sorting() {
        let mut r = vec![
            Route {
                dest: Ipv4Addr::new(192, 168, 0, 0),
                prefix_len: 16,
            },
            Route {
                dest: Ipv4Addr::new(10, 0, 0, 0),
                prefix_len: 8,
            },
        ];
        r.sort();
        assert_eq!(r[0].dest, Ipv4Addr::new(10, 0, 0, 0));
    }
}
