use crate::error::RariError;
use crate::server::ServerState;
use cow_utils::CowUtils;
use regex::Regex;
use rustc_hash::FxHashMap;
use tracing::error;

pub struct CacheLoader;

impl CacheLoader {
    pub async fn load_page_cache_configs(state: &ServerState) -> Result<(), RariError> {
        let pages_dir = std::path::Path::new("src/pages");
        if !pages_dir.exists() {
            return Ok(());
        }

        let mut loaded_count = 0;
        Self::scan_pages_directory(pages_dir, state, &mut loaded_count).await?;

        Ok(())
    }

    async fn scan_pages_directory(
        dir: &std::path::Path,
        state: &ServerState,
        loaded_count: &mut usize,
    ) -> Result<(), RariError> {
        let mut dirs_to_scan = vec![dir.to_path_buf()];

        while let Some(current_dir) = dirs_to_scan.pop() {
            let entries = std::fs::read_dir(&current_dir)
                .map_err(|e| RariError::io(format!("Failed to read pages directory: {e}")))?;

            for entry in entries {
                let entry = entry
                    .map_err(|e| RariError::io(format!("Failed to read directory entry: {e}")))?;
                let path = entry.path();

                if path.is_dir() {
                    dirs_to_scan.push(path);
                } else if path.is_file()
                    && let Some(extension) = path.extension()
                    && (extension == "tsx"
                        || extension == "jsx"
                        || extension == "ts"
                        || extension == "js")
                {
                    if let Err(e) = Self::load_page_cache_config(&path, state).await {
                        error!("Failed to load page cache config for {:?}: {}", path, e);
                    }
                    *loaded_count += 1;
                }
            }
        }

        Ok(())
    }

    async fn load_page_cache_config(
        page_path: &std::path::Path,
        state: &ServerState,
    ) -> Result<(), RariError> {
        let content = std::fs::read_to_string(page_path)
            .map_err(|e| RariError::io(format!("Failed to read page file: {e}")))?;

        if let Some(cache_config) = Self::extract_cache_config_from_content(&content) {
            let route_path = Self::page_path_to_route(page_path)?;

            let mut page_configs = state.page_cache_configs.write().await;
            page_configs.insert(route_path.clone(), cache_config);
        }

        Ok(())
    }

    fn page_path_to_route(page_path: &std::path::Path) -> Result<String, RariError> {
        let pages_dir = std::path::Path::new("src/pages");
        let relative_path = page_path.strip_prefix(pages_dir).map_err(|_| {
            RariError::configuration("Page path is not within pages directory".to_string())
        })?;

        let route =
            relative_path.with_extension("").to_string_lossy().cow_replace('\\', "/").into_owned();

        let route = if route == "index" { "/".to_string() } else { format!("/{}", route) };

        Ok(route)
    }

    fn extract_cache_config_from_content(content: &str) -> Option<FxHashMap<String, String>> {
        let cache_config_regex =
            Regex::new(r"export\s+const\s+cacheConfig\s*:\s*\w+\s*=\s*\{([^}]+)\}").ok()?;

        if let Some(captures) = cache_config_regex.captures(content) {
            let config_content = captures.get(1)?.as_str();
            let mut config = FxHashMap::default();

            let cache_control_regex = Regex::new(r"'cache-control'\s*:\s*'([^']+)'").ok()?;
            if let Some(cache_control_match) = cache_control_regex.captures(config_content) {
                config.insert(
                    "cache-control".to_string(),
                    cache_control_match.get(1)?.as_str().to_string(),
                );
            }

            let vary_regex = Regex::new(r"'vary'\s*:\s*'([^']+)'").ok()?;
            if let Some(vary_match) = vary_regex.captures(config_content) {
                config.insert("vary".to_string(), vary_match.get(1)?.as_str().to_string());
            }

            if !config.is_empty() {
                return Some(config);
            }
        }

        None
    }

    pub fn find_matching_cache_config<'a>(
        page_configs: &'a FxHashMap<String, FxHashMap<String, String>>,
        route_path: &str,
    ) -> Option<&'a FxHashMap<String, String>> {
        if let Some(config) = page_configs.get(route_path) {
            return Some(config);
        }

        for (pattern, config) in page_configs {
            if Self::matches_route_pattern(pattern, route_path) {
                return Some(config);
            }
        }

        None
    }

    fn matches_route_pattern(pattern: &str, path: &str) -> bool {
        if pattern == path {
            return true;
        }

        if let Some(prefix) = pattern.strip_suffix("/*") {
            return path.starts_with(prefix)
                && (path.len() == prefix.len() || path.chars().nth(prefix.len()) == Some('/'));
        }

        if pattern.contains('*') {
            let regex_pattern = pattern.cow_replace('*', ".*").cow_replace('/', "\\/").into_owned();
            if let Ok(regex) = regex::Regex::new(&format!("^{}$", regex_pattern)) {
                return regex.is_match(path);
            }
        }

        false
    }
}
