//! Server Route Analysis
//!
//! This module analyzes server-side API routes by parsing Rust source code
//! to extract route information, handlers, parameters, and other metadata.

use crate::route_analysis::{ApiRouteInfo, RouteDocumentation};
use regex::Regex;
use std::collections::HashMap;
use std::fs;
use std::path::Path;

/// Generate server route documentation from source code analysis
pub fn generate_server_route_documentation(
    server_crate_path: &str,
    config_path: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut documentation = RouteDocumentation::new();

    // Analyze main routes.rs file
    let routes_path = Path::new(server_crate_path).join("src/routes.rs");
    if routes_path.exists() {
        let api_routes = analyze_routes_file(&routes_path)?;
        documentation.api_routes.extend(api_routes);
    }

    // Analyze auth routes if they exist
    let auth_routes_path = Path::new(server_crate_path).join("src/auth/routes.rs");
    if auth_routes_path.exists() {
        let auth_routes = analyze_auth_routes_file(&auth_routes_path)?;
        documentation.api_routes.extend(auth_routes);
    }

    // Analyze content routes if they exist
    let content_routes_path = Path::new(server_crate_path).join("src/content/routes.rs");
    if content_routes_path.exists() {
        let content_routes = analyze_content_routes_file(&content_routes_path)?;
        documentation.api_routes.extend(content_routes);
    }

    // Analyze handlers directory for additional route information
    let handlers_dir = Path::new(server_crate_path).join("src/handlers");
    if handlers_dir.exists() {
        let handler_routes = analyze_handlers_directory(&handlers_dir)?;
        documentation.api_routes.extend(handler_routes);
    }

    // Save documentation files
    documentation.save_to_toml(config_path)?;
    documentation.generate_markdown(config_path)?;

    Ok(())
}

/// Analyze the main routes.rs file
pub fn analyze_routes_file(
    routes_path: &Path,
) -> Result<Vec<ApiRouteInfo>, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(routes_path)?;
    let mut routes = Vec::new();

    // Split content into lines for line number calculation
    let _lines: Vec<&str> = content.lines().collect();

    // Look for Router::new() chains and .route() calls
    let route_regex =
        Regex::new(r#"\.route\s*\(\s*"([^"]+)"\s*,\s*(\w+)\s*\(\s*([^)]+)\s*\)\s*\)"#)?;

    for cap in route_regex.captures_iter(&content) {
        if let (Some(path), Some(method), Some(handler)) = (cap.get(1), cap.get(2), cap.get(3)) {
            // Find line number for this match
            let line_number = content[..cap.get(0).unwrap().start()].matches('\n').count() + 1;

            let src_ref = Some(crate::route_analysis::SourceReference {
                path: crate::route_analysis::make_relative_path(routes_path),
                line: line_number,
                context: "route definition".to_string(),
            });

            let route_info = ApiRouteInfo {
                path: path.as_str().to_string(),
                methods: vec![method.as_str().to_uppercase()],
                handler: handler.as_str().to_string(),
                module: "crates/server/src/routes.rs".to_string(),
                parameters: crate::route_analysis::extract_path_parameters(path.as_str()),
                response_type: "Unknown".to_string(), // Would need more sophisticated analysis
                requires_auth: false,                 // Would need middleware analysis
                middleware: vec![],
                description: None,
                src_ref,
            };
            routes.push(route_info);
        }
    }

    // Look for function definitions that might be handlers
    let handler_regex = Regex::new(r"pub\s+async\s+fn\s+(\w+)\s*\([^)]*\)\s*->\s*([^{]+)")?;
    let mut handlers: HashMap<String, String> = HashMap::new();

    for cap in handler_regex.captures_iter(&content) {
        if let (Some(name), Some(return_type)) = (cap.get(1), cap.get(2)) {
            handlers.insert(
                name.as_str().to_string(),
                return_type.as_str().trim().to_string(),
            );
        }
    }

    // Update return types for routes where we found the handler
    for route in &mut routes {
        if let Some(return_type) = handlers.get(&route.handler) {
            route.response_type = return_type.clone();
        }
    }

    Ok(routes)
}

/// Analyze auth routes file
fn analyze_auth_routes_file(
    auth_path: &Path,
) -> Result<Vec<ApiRouteInfo>, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(auth_path)?;
    let mut routes = Vec::new();

    // Split content into lines for line number calculation
    let _lines: Vec<&str> = content.lines().collect();

    // Auth routes typically require authentication
    let route_regex =
        Regex::new(r#"\.route\s*\(\s*"([^"]+)"\s*,\s*(\w+)\s*\(\s*([^)]+)\s*\)\s*\)"#)?;

    for cap in route_regex.captures_iter(&content) {
        if let (Some(path), Some(method), Some(handler)) = (cap.get(1), cap.get(2), cap.get(3)) {
            // Find line number for this match
            let line_number = content[..cap.get(0).unwrap().start()].matches('\n').count() + 1;

            let src_ref = Some(crate::route_analysis::SourceReference {
                path: crate::route_analysis::make_relative_path(auth_path),
                line: line_number,
                context: "auth route definition".to_string(),
            });

            let route_info = ApiRouteInfo {
                path: path.as_str().to_string(),
                methods: vec![method.as_str().to_uppercase()],
                handler: handler.as_str().to_string(),
                module: "crates/server/src/auth/routes.rs".to_string(),
                parameters: crate::route_analysis::extract_path_parameters(path.as_str()),
                response_type: "Unknown".to_string(),
                requires_auth: true, // Auth routes typically require auth
                middleware: vec!["auth".to_string()],
                description: Some("Authentication endpoint".to_string()),
                src_ref,
            };
            routes.push(route_info);
        }
    }

    Ok(routes)
}

/// Analyze content routes file
fn analyze_content_routes_file(
    content_path: &Path,
) -> Result<Vec<ApiRouteInfo>, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(content_path)?;
    let mut routes = Vec::new();

    // Split content into lines for line number calculation
    let _lines: Vec<&str> = content.lines().collect();

    let route_regex =
        Regex::new(r#"\.route\s*\(\s*"([^"]+)"\s*,\s*(\w+)\s*\(\s*([^)]+)\s*\)\s*\)"#)?;

    for cap in route_regex.captures_iter(&content) {
        if let (Some(path), Some(method), Some(handler)) = (cap.get(1), cap.get(2), cap.get(3)) {
            // Find line number for this match
            let line_number = content[..cap.get(0).unwrap().start()].matches('\n').count() + 1;

            let src_ref = Some(crate::route_analysis::SourceReference {
                path: crate::route_analysis::make_relative_path(content_path),
                line: line_number,
                context: "content route definition".to_string(),
            });

            let route_info = ApiRouteInfo {
                path: path.as_str().to_string(),
                methods: vec![method.as_str().to_uppercase()],
                handler: handler.as_str().to_string(),
                module: "crates/server/src/content/routes.rs".to_string(),
                parameters: crate::route_analysis::extract_path_parameters(path.as_str()),
                response_type: "Unknown".to_string(),
                requires_auth: false,
                middleware: vec![],
                description: Some("Content management endpoint".to_string()),
                src_ref,
            };
            routes.push(route_info);
        }
    }

    Ok(routes)
}

/// Analyze handlers directory for route information
fn analyze_handlers_directory(
    handlers_dir: &Path,
) -> Result<Vec<ApiRouteInfo>, Box<dyn std::error::Error>> {
    let mut routes = Vec::new();

    // Recursively walk the handlers directory
    for entry in walkdir::WalkDir::new(handlers_dir) {
        let entry = entry?;
        let path = entry.path();

        if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {
            let handler_routes = analyze_handler_file(path, handlers_dir)?;
            routes.extend(handler_routes);
        }
    }

    Ok(routes)
}

/// Analyze a single handler file
fn analyze_handler_file(
    file_path: &Path,
    handlers_base: &Path,
) -> Result<Vec<ApiRouteInfo>, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(file_path)?;
    let mut routes = Vec::new();

    // Split content into lines for line number calculation
    let _lines: Vec<&str> = content.lines().collect();

    // Get relative path for module name
    let relative_path = file_path
        .strip_prefix(handlers_base)
        .unwrap_or(file_path)
        .to_string_lossy();
    let module_path = format!("crates/server/src/handlers/{}", relative_path);

    // Look for handler functions with axum extractors
    let handler_regex = Regex::new(
        r"pub\s+async\s+fn\s+(\w+)\s*\([^)]*(?:Path\([^)]*\)|Query\([^)]*\)|Json\([^)]*\))[^)]*\)\s*->\s*([^{]+)",
    )?;

    for cap in handler_regex.captures_iter(&content) {
        if let (Some(handler_name), Some(return_type)) = (cap.get(1), cap.get(2)) {
            // Try to infer route path from handler name and file structure
            let inferred_path =
                infer_route_path_from_handler(handler_name.as_str(), &relative_path);

            // Find line number for this match
            let line_number = content[..cap.get(0).unwrap().start()].matches('\n').count() + 1;

            let src_ref = Some(crate::route_analysis::SourceReference {
                path: crate::route_analysis::make_relative_path(file_path),
                line: line_number,
                context: "handler function".to_string(),
            });

            let route_info = ApiRouteInfo {
                path: inferred_path,
                methods: infer_http_methods_from_handler(handler_name.as_str()),
                handler: format!("handlers::{}", handler_name.as_str()),
                module: module_path.clone(),
                parameters: vec![], // Would need more sophisticated parameter extraction
                response_type: return_type.as_str().trim().to_string(),
                requires_auth: content.contains("RequireAuth") || content.contains("auth"),
                middleware: infer_middleware_from_content(&content),
                description: extract_doc_comment(&content, handler_name.as_str()),
                src_ref,
            };
            routes.push(route_info);
        }
    }

    Ok(routes)
}

/// Infer route path from handler function name and file location
fn infer_route_path_from_handler(handler_name: &str, file_path: &str) -> String {
    let base_path = file_path
        .trim_end_matches(".rs")
        .replace(['/', '\\'], "/")
        .replace("mod", "");

    // Convert handler naming patterns to route paths
    let route_segment = if handler_name.ends_with("_handler") {
        handler_name.trim_end_matches("_handler")
    } else if handler_name.starts_with("get_") {
        handler_name.trim_start_matches("get_")
    } else if handler_name.starts_with("post_") {
        handler_name.trim_start_matches("post_")
    } else if handler_name.starts_with("put_") {
        handler_name.trim_start_matches("put_")
    } else if handler_name.starts_with("delete_") {
        handler_name.trim_start_matches("delete_")
    } else {
        handler_name
    };

    if base_path.is_empty() || base_path == "/" {
        format!("/api/{}", route_segment.replace('_', "-"))
    } else {
        format!(
            "/api/{}/{}",
            base_path.trim_start_matches('/'),
            route_segment.replace('_', "-")
        )
    }
}

/// Infer HTTP methods from handler function name
fn infer_http_methods_from_handler(handler_name: &str) -> Vec<String> {
    if handler_name.starts_with("get_") || handler_name.ends_with("_get") {
        vec!["GET".to_string()]
    } else if handler_name.starts_with("post_") || handler_name.ends_with("_post") {
        vec!["POST".to_string()]
    } else if handler_name.starts_with("put_") || handler_name.ends_with("_put") {
        vec!["PUT".to_string()]
    } else if handler_name.starts_with("delete_") || handler_name.ends_with("_delete") {
        vec!["DELETE".to_string()]
    } else if handler_name.contains("create") {
        vec!["POST".to_string()]
    } else if handler_name.contains("update") {
        vec!["PUT".to_string()]
    } else if handler_name.contains("delete") || handler_name.contains("remove") {
        vec!["DELETE".to_string()]
    } else {
        vec!["GET".to_string()] // Default to GET
    }
}

/// Infer middleware from file content
fn infer_middleware_from_content(content: &str) -> Vec<String> {
    let mut middleware = Vec::new();

    if content.contains("RequireAuth") || content.contains("AuthLayer") {
        middleware.push("auth".to_string());
    }
    if content.contains("CorsLayer") || content.contains("cors") {
        middleware.push("cors".to_string());
    }
    if content.contains("RateLimitLayer") || content.contains("rate_limit") {
        middleware.push("rate_limit".to_string());
    }
    if content.contains("CSRFLayer") || content.contains("csrf") {
        middleware.push("csrf".to_string());
    }

    middleware
}

/// Extract doc comment for a function
fn extract_doc_comment(content: &str, function_name: &str) -> Option<String> {
    let lines: Vec<&str> = content.lines().collect();

    for (i, line) in lines.iter().enumerate() {
        if line.contains(&format!("fn {}", function_name)) {
            // Look backwards for doc comments
            let mut doc_lines = Vec::new();
            let mut j = i;

            while j > 0 {
                j -= 1;
                let prev_line = lines[j].trim();
                if prev_line.starts_with("///") {
                    doc_lines.insert(0, prev_line.trim_start_matches("///").trim());
                } else if prev_line.is_empty() {
                    continue;
                } else {
                    break;
                }
            }

            if !doc_lines.is_empty() {
                return Some(doc_lines.join(" "));
            }
        }
    }

    None
}
