use std::collections::HashMap;

use axum::routing::MethodRouter;
use serde::{Deserialize, Serialize};
use tracing::info;
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
pub enum WebPathType {
    #[default]
    None,
    Get,
    Post,
    Put,
    Delete,
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct WebPath {
    pub final_path: String,
    pub webmethod: WebPathType,
    #[serde(skip)]
    pub method_router: Option<MethodRouter>,
    sub_paths: HashMap<String, WebPath>,
    pub apiname: Option<String>,
}

impl WebPath {
    pub fn new() -> Self {
        WebPath::default()
    }
    pub fn nest(mut self, path: &str, web_path: WebPath) -> Self {
        self.sub_paths.insert(String::from(path), web_path);
        self
    }

    pub fn merge(mut self, web_path: WebPath) -> Self {
        for (sub_key, sub_path) in web_path.sub_paths {
            self.sub_paths.insert(sub_key, sub_path);
        }
        self
    }
    pub fn route(
        mut self,
        path: &str,
        method: WebPathType,
        apiname: Option<&str>,
        method_router: MethodRouter,
    ) -> Self {
        self.sub_paths.insert(
            String::from(path),
            WebPath {
                webmethod: method,
                apiname: apiname.map(String::from),
                method_router: Some(method_router),
                sub_paths: HashMap::new(),
                ..Default::default()
            },
        );
        self
    }

    fn concat_sub_paths_final_paths(&mut self, parent_path: &str) {
        for (sub_key, sub_path) in &mut self.sub_paths {
            let f_path = format!("{}{}", parent_path, sub_key);
            sub_path.concat_sub_paths_final_paths(&f_path);
            if sub_path.is_last_level() {
                sub_path.final_path = f_path;
            }
        }
    }
    pub fn final_to_path(mut self) -> Self {
        self.concat_sub_paths_final_paths("");
        self
    }

    fn is_last_level(&self) -> bool {
        self.sub_paths.is_empty()
    }

    pub fn get_last_level_paths(&self) -> Vec<&WebPath> {
        let mut last_level_paths = Vec::new();
        if self.is_last_level() && self.webmethod != WebPathType::None {
            last_level_paths.push(self);
        }
        for sub_path in self.sub_paths.values() {
            last_level_paths.extend(sub_path.get_last_level_paths());
        }
        last_level_paths
    }

    pub fn print_all_paths(&self) {
        for sub_path_data in self.sub_paths.values() {
            if sub_path_data.is_last_level() {
                info!("{}", sub_path_data.final_path);
            }

            sub_path_data.print_all_paths();
        }
    }
}

impl WebPathType {
    pub fn as_str(&self) -> &'static str {
        match self {
            WebPathType::None => "None",
            WebPathType::Get => "Get",
            WebPathType::Post => "Post",
            WebPathType::Put => "Put",
            WebPathType::Delete => "Delete",
        }
    }
}