Documentation
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::Debug;

/// 路由类型
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Ord, Eq)]
pub enum RouteType {
    /// 静态路径: /home/sub
    Static,
    /// 动态路径: /home/:page
    Dynamic,
    /// 通配符: /home/*
    Wildcard,
}

/// 路由匹配结果
#[derive(Clone)]
pub enum RouteMatch<T: Clone> {
    Matched {
        handler: T,
        params: HashMap<String, String>,
    },
    NotMatched,
}

/// 路由规则
#[derive(Clone)]
pub struct Route<T: Clone> {
    path: String,
    parts: Vec<String>,
    handler: T,
    route_type: RouteType,
}

/// 路由
pub struct Router<T: Clone> {
    routes: Vec<Route<T>>,
}

impl<T: Clone> Router<T> {
    pub fn new() -> Self {
        Router { routes: vec![] }
    }

    /// 添加路由规则
    /// 支持以下路由规则
    ///     1.静态路径匹配
    ///     2.动态路径匹配,
    ///     3.通配符匹配
    pub fn add(&mut self, path: impl Into<String>, handler: T) {
        let path = path.into();

        let route_type = if path.contains("*") {
            RouteType::Wildcard
        } else if path.contains(":") {
            RouteType::Dynamic
        } else {
            RouteType::Static
        };

        let parts = path
            .trim_matches('/')
            .split('/')
            .map(|s| s.to_string())
            .collect();

        self.routes.push(Route {
            path,
            parts,
            handler,
            route_type,
        });

        // 按优先级排序(Static > Dynamic > Wildcard)
        self.routes.sort_by(|a, b| a.route_type.cmp(&b.route_type))
    }

    /// 移除路由规则
    pub fn remove(&mut self, path: &str) -> Option<Route<T>> {
        for i in (0..self.routes.len()).rev() {
            if path.cmp(&self.routes[i].path) == Ordering::Equal {
                return Some(self.routes.remove(i));
            }
        }

        None
    }

    /// 匹配路由规则
    /// 支持以下路由规则
    ///     1.静态路径匹配
    ///     2.动态路径匹配,
    ///     3.通配符匹配
    pub fn go_match(&self, path: &str) -> RouteMatch<T> {
        let parts: Vec<&str> = path.trim_matches('/').split('/').collect();

        for route in &self.routes {
            let route_parts: Vec<&str> = route.parts.iter().map(|s| s as &str).collect();
            let mut params = HashMap::new();

            if route_parts.len() != parts.len()
                && !route_parts.iter().any(|part| part.starts_with('*'))
            {
                continue;
            }

            let mut matched = true;

            for (i, route_part) in route_parts.iter().enumerate() {
                if *route_part == "*" || route_part.starts_with('*') {
                    // wildcard match
                    let key = route_part.trim_start_matches('*');
                    params.insert(key.to_string(), parts[i..].join("/"));
                    break;
                } else if route_part.starts_with(':') {
                    // dynamic match
                    let key = route_part.trim_start_matches(':');
                    if let Some(val) = parts.get(i) {
                        params.insert(key.to_string(), val.to_string());
                    } else {
                        matched = false;
                        break;
                    }
                } else if let Some(req_part) = parts.get(i) {
                    if route_part != req_part {
                        matched = false;
                        break;
                    }
                } else {
                    matched = false;
                    break;
                }
            }

            if matched {
                return RouteMatch::Matched {
                    handler: route.handler.clone(),
                    params,
                };
            }
        }

        RouteMatch::NotMatched
    }
}