use std::{fs::{self, DirEntry}};

use crate::{app::App, route_builder::{config::{Config, ConfigStore, Mergeable}, Route, RouteGenerator, RouteParams}};

#[derive(Debug, Default)]
pub struct RouteManager {
    pub auth_route: Route,
    pub routes: Vec<Route>,
}

impl RouteManager {
    pub fn new() -> Self {
        Self { auth_route: Route::None, routes: vec![] }
    }

    pub fn from_dir(root_path: &str, config: Option<Config>) -> Self {
        let start_time = std::time::Instant::now();
        println!("Start - Loading routes");

        let parent_route = config.clone().unwrap_or_default()
            .route.unwrap_or_default()
            .remap.unwrap_or("".into());

        let mut manager = Self::new();
        manager.load_dir(&parent_route, root_path, config);
        manager.sort();

        println!("Finish - Loading routes. Routes loaded in {:?}", start_time.elapsed());

        manager
    }

    fn load_dir(&mut self, parent_route: &str, entries_path: &str, config: Option<Config>) {
        let config_store = ConfigStore::try_from_dir(entries_path)
            .unwrap_or_else(|err| panic!("Unable to load configs from {}. Error: {:?}", entries_path, err));

        let config = config_store.get("config").merge(config);

        let entries = fs::read_dir(entries_path).unwrap();
        for entry in entries {
            let entry = entry.unwrap();
            self.load_entry(parent_route, &entry, &config, &config_store);
        }
    }

    fn load_entry(&mut self, parent_route: &str, entry: &DirEntry, config: &Option<Config>, config_store: &ConfigStore) {
        let route_params = RouteParams::new(parent_route, entry, config.clone().unwrap_or_default(), config_store);
        if route_params.file_extension == "toml" {
            return;
        }

        let route = Route::try_parse(&route_params);

        if route.is_none() {
            if route_params.is_dir {
                self.load_dir(
                    &route_params.full_route,
                    &route_params.file_path.to_string_lossy(),
                    Some(route_params.config.clone())
                );
            }
            return;
        }

        if let Route::Auth(_) = route {
            if self.auth_route.is_some() {
                panic!("Only one auth route is allowed");
            }
            self.auth_route = route;
        } else {
            self.routes.push(route);
        }
    }

    fn sort(&mut self) {
        self.routes.sort_by(|ra, rb| {
            ra.partial_cmp(rb).unwrap_or(std::cmp::Ordering::Equal)
        });
    }

}

impl RouteGenerator for RouteManager {
    fn make_routes(&self, app: &mut App) {
        self.auth_route.make_routes_and_print(app);

        for route in self.routes.iter() {
            route.make_routes_and_print(app);
        }
    }
}
