//! Route Analysis Utilities
//!
//! This module provides shared utilities for analyzing and documenting routes
//! from both server-side API routes and client-side page routes.

use std::collections::HashMap;
use std::fs;
use std::path::Path;

use serde::{Deserialize, Serialize};

/// Source reference indicating where code is declared
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceReference {
    /// File path relative to project root
    pub path: String,
    /// Line number where declared (1-based)
    pub line: usize,
    /// Context description (e.g., "route definition", "component function")
    pub context: String,
}

/// Convert absolute path to relative path from project root
pub fn make_relative_path(absolute_path: &Path) -> String {
    // Find the project root by looking for Cargo.toml
    let mut current = absolute_path;
    while let Some(parent) = current.parent() {
        if parent.join("Cargo.toml").exists() {
            if let Ok(relative) = absolute_path.strip_prefix(parent) {
                return relative.to_string_lossy().to_string();
            }
        }
        current = parent;
    }

    // Fallback to just the file name if we can't find project root
    absolute_path
        .file_name()
        .unwrap_or_default()
        .to_string_lossy()
        .to_string()
}

/// Generate a kebab-case anchor for an item
pub fn generate_anchor(item_type: &str, name: &str) -> String {
    let clean_name = name
        .replace(['/', '{', '}', ':', ' '], "-")
        .replace("--", "-")
        .trim_matches('-')
        .to_lowercase();
    format!("{}-{}", item_type, clean_name)
}

/// Generate source link with context info
pub fn format_source_link(src_ref: &SourceReference) -> String {
    format!(
        "[source]({}:{}) `line: {}` **{}**",
        src_ref.path, src_ref.line, src_ref.line, src_ref.context
    )
}

/// Generate markdown header with anchor
pub fn generate_header_with_anchor(level: usize, title: &str, anchor: &str) -> String {
    let hashes = "#".repeat(level);
    format!("{} {}\n\n<a id=\"{}\"></a>\n", hashes, title, anchor)
}

/// Represents an API route definition from server code
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiRouteInfo {
    /// HTTP path pattern (e.g., "/api/users/{id}")
    pub path: String,
    /// HTTP methods supported (GET, POST, etc.)
    pub methods: Vec<String>,
    /// Handler function name
    pub handler: String,
    /// Module path where handler is defined
    pub module: String,
    /// Parameters extracted from path, query, or body
    pub parameters: Vec<RouteParameter>,
    /// Response type information
    pub response_type: String,
    /// Whether authentication is required
    pub requires_auth: bool,
    /// Middleware applied to this route
    pub middleware: Vec<String>,
    /// Optional description/documentation
    pub description: Option<String>,
    /// Source reference where route is declared
    pub src_ref: Option<SourceReference>,
}

/// Represents a client-side page route from TOML configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageRouteInfo {
    /// Route path pattern
    pub path: String,
    /// Component name
    pub component: String,
    /// Page component name
    pub page_component: String,
    /// Unified component name
    pub unified_component: String,
    /// Module path for the page
    pub module_path: Option<String>,
    /// Language code
    pub language: String,
    /// Whether route is enabled
    pub enabled: bool,
    /// Route priority for matching
    pub priority: f64,
    /// Authentication requirement
    pub requires_auth: Option<bool>,
    /// Menu configuration
    pub menu_group: Option<String>,
    pub menu_order: Option<i32>,
    pub menu_icon: Option<String>,
    /// SEO and metadata
    pub title_key: String,
    pub description_key: Option<String>,
    pub keywords: Vec<String>,
    /// I18n patterns
    pub i18n_patterns: Vec<String>,
    /// Route parameters
    pub parameters: Vec<RouteParameter>,
    /// Additional properties
    pub props: HashMap<String, toml::Value>,
    /// Source reference where this route is defined
    pub src_ref: Option<SourceReference>,
    /// Metadata HashMap for configuration-driven access
    pub metadata: HashMap<String, toml::Value>,
}

/// Represents a component definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentInfo {
    /// Component name
    pub name: String,
    /// File path relative to crate root
    pub file_path: String,
    /// Module path
    pub module_path: String,
    /// Component type (Page, Layout, Utility, etc.)
    pub component_type: String,
    /// Props interface if available
    pub props: Vec<ComponentProp>,
    /// Dependencies on other components
    pub dependencies: Vec<String>,
    /// Usage example if available
    pub usage_example: Option<String>,
    /// Description/documentation
    pub description: Option<String>,
    /// Source reference where this component is defined
    pub src_ref: Option<SourceReference>,
}

/// Represents a route parameter
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteParameter {
    /// Parameter name
    pub name: String,
    /// Parameter type (String, u32, etc.)
    pub param_type: String,
    /// Where parameter comes from (path, query, body)
    pub source: String,
    /// Whether parameter is optional
    pub optional: bool,
    /// Parameter description
    pub description: Option<String>,
}

/// Represents a component property/prop
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentProp {
    /// Property name
    pub name: String,
    /// Property type
    pub prop_type: String,
    /// Whether property is optional
    pub optional: bool,
    /// Default value if any
    pub default_value: Option<String>,
    /// Property description
    pub description: Option<String>,
}

impl std::fmt::Display for ComponentProp {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.optional {
            write!(f, "{}?: {}", self.name, self.prop_type)
        } else {
            write!(f, "{}: {}", self.name, self.prop_type)
        }
    }
}

/// Container for all route documentation
#[derive(Debug, Serialize, Deserialize)]
pub struct RouteDocumentation {
    /// Server API routes
    pub api_routes: Vec<ApiRouteInfo>,
    /// Client page routes
    pub page_routes: Vec<PageRouteInfo>,
    /// Component definitions
    pub components: Vec<ComponentInfo>,
    /// Generation timestamp
    pub generated_at: String,
}

/// Wrapper for server routes TOML serialization
#[derive(Debug, Serialize, Deserialize)]
pub struct ServerRoutesDocument {
    /// Generation timestamp
    pub generated_at: String,
    /// Server API routes
    pub api_routes: Vec<ApiRouteInfo>,
}

/// Wrapper for client routes TOML serialization
#[derive(Debug, Serialize, Deserialize)]
pub struct ClientRoutesDocument {
    /// Generation timestamp
    pub generated_at: String,
    /// Client page routes
    pub page_routes: Vec<PageRouteInfo>,
}

/// Wrapper for components TOML serialization
#[derive(Debug, Serialize, Deserialize)]
pub struct ComponentsDocument {
    /// Generation timestamp
    pub generated_at: String,
    /// Component definitions
    pub components: Vec<ComponentInfo>,
}

impl RouteDocumentation {
    /// Create a new empty route documentation
    pub fn new() -> Self {
        Self {
            api_routes: Vec::new(),
            page_routes: Vec::new(),
            components: Vec::new(),
            generated_at: chrono::Utc::now().to_rfc3339(),
        }
    }

    /// Save documentation to TOML files in the specified directory
    pub fn save_to_toml(&self, config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
        let config_dir = Path::new(config_path);
        fs::create_dir_all(config_dir)?;

        // Save server routes
        if !self.api_routes.is_empty() {
            let server_routes_path = config_dir.join("server_routes.toml");
            let server_routes_wrapper = ServerRoutesDocument {
                api_routes: self.api_routes.clone(),
                generated_at: self.generated_at.clone(),
            };
            let server_content = toml::to_string_pretty(&server_routes_wrapper)?;
            fs::write(&server_routes_path, server_content)?;
        }

        // Save client routes
        if !self.page_routes.is_empty() {
            let client_routes_path = config_dir.join("client_routes.toml");
            let client_routes_wrapper = ClientRoutesDocument {
                page_routes: self.page_routes.clone(),
                generated_at: self.generated_at.clone(),
            };
            let client_content = toml::to_string_pretty(&client_routes_wrapper)?;
            fs::write(&client_routes_path, client_content)?;
        }

        // Save components
        if !self.components.is_empty() {
            let components_path = config_dir.join("components.toml");
            let components_wrapper = ComponentsDocument {
                components: self.components.clone(),
                generated_at: self.generated_at.clone(),
            };
            let components_content = toml::to_string_pretty(&components_wrapper)?;
            fs::write(&components_path, components_content)?;
        }

        Ok(())
    }

    /// Save documentation to JSON files (machine-readable format)
    pub fn save_to_json(&self, config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
        let config_dir = Path::new(config_path);
        fs::create_dir_all(config_dir)?;

        // Save server routes
        if !self.api_routes.is_empty() {
            let server_routes_path = config_dir.join("server_routes.json");
            let server_routes_wrapper = ServerRoutesDocument {
                api_routes: self.api_routes.clone(),
                generated_at: self.generated_at.clone(),
            };
            let server_content = serde_json::to_string_pretty(&server_routes_wrapper)?;
            fs::write(&server_routes_path, server_content)?;
        }

        // Save client routes
        if !self.page_routes.is_empty() {
            let client_routes_path = config_dir.join("client_routes.json");
            let client_routes_wrapper = ClientRoutesDocument {
                page_routes: self.page_routes.clone(),
                generated_at: self.generated_at.clone(),
            };
            let client_content = serde_json::to_string_pretty(&client_routes_wrapper)?;
            fs::write(&client_routes_path, client_content)?;
        }

        // Save components
        if !self.components.is_empty() {
            let components_path = config_dir.join("components.json");
            let components_wrapper = ComponentsDocument {
                components: self.components.clone(),
                generated_at: self.generated_at.clone(),
            };
            let components_content = serde_json::to_string_pretty(&components_wrapper)?;
            fs::write(&components_path, components_content)?;
        }

        Ok(())
    }

    /// Generate markdown documentation
    pub fn generate_markdown(&self, config_path: &str) -> Result<(), Box<dyn std::error::Error>> {
        let config_dir = Path::new(config_path);
        fs::create_dir_all(config_dir)?;

        // Generate server routes markdown
        if !self.api_routes.is_empty() {
            let server_md = self.generate_server_routes_markdown();
            let server_path = config_dir.join("server_routes.md");
            fs::write(&server_path, server_md)?;
        }

        // Generate client routes markdown
        if !self.page_routes.is_empty() {
            let client_md = self.generate_client_routes_markdown();
            let client_path = config_dir.join("client_routes.md");
            fs::write(&client_path, client_md)?;
        }

        // Generate components markdown
        if !self.components.is_empty() {
            let components_md = self.generate_components_markdown();
            let components_path = config_dir.join("components.md");
            fs::write(&components_path, components_md)?;
        }

        // Generate automation guide
        let automation_md = self.generate_automation_guide();
        let automation_path = config_dir.join("automation_guide.md");
        fs::write(&automation_path, automation_md)?;

        Ok(())
    }

    fn generate_server_routes_markdown(&self) -> String {
        let mut md = String::new();
        md.push_str("# Server API Routes Reference\n\n");
        md.push_str(&format!("*Generated at: {}*\n\n", self.generated_at));

        // Add summary section
        md.push_str("## Summary\n\n");
        let route_count = self.api_routes.len();
        let auth_required = self.api_routes.iter().filter(|r| r.requires_auth).count();
        let static_routes = self
            .api_routes
            .iter()
            .filter(|r| r.response_type == "Static Files")
            .count();

        md.push_str("| Metric | Count |\n");
        md.push_str("|--------|-------|\n");
        md.push_str(&format!("| **Total Routes** | {} |\n", route_count));
        md.push_str(&format!(
            "| **Authentication Required** | {} |\n",
            auth_required
        ));
        md.push_str(&format!("| **Static File Routes** | {} |\n", static_routes));
        md.push('\n');

        md.push_str("## Table of Contents\n\n");
        for route in &self.api_routes {
            let anchor = generate_anchor(
                "route",
                &format!("{}-{}", route.methods.join("-"), route.path),
            );
            md.push_str(&format!("- [{}](#{})\n", route.path, anchor));
        }
        md.push('\n');

        md.push_str("## Route Details\n\n");

        for route in &self.api_routes {
            let anchor = generate_anchor(
                "route",
                &format!("{}-{}", route.methods.join("-"), route.path),
            );
            let title = format!("{} {}", route.methods.join(", "), route.path);
            md.push_str(&generate_header_with_anchor(3, &title, &anchor));

            // Add source link if available
            if let Some(src_ref) = &route.src_ref {
                md.push_str(&format!("{}\n\n", format_source_link(src_ref)));
            }

            if let Some(description) = &route.description {
                md.push_str(&format!("**Description:** {}\n\n", description));
            }

            md.push_str(&format!("**Handler:** `{}`\n\n", route.handler));
            md.push_str(&format!("**Module:** `{}`\n\n", route.module));
            md.push_str(&format!(
                "**Authentication Required:** {}\n\n",
                route.requires_auth
            ));

            if !route.parameters.is_empty() {
                md.push_str("**Parameters:**\n\n");
                for param in &route.parameters {
                    md.push_str(&format!(
                        "- `{}` ({}): {} - {}\n",
                        param.name,
                        param.param_type,
                        if param.optional {
                            "optional"
                        } else {
                            "required"
                        },
                        param.source
                    ));
                }
                md.push('\n');
            }

            if !route.middleware.is_empty() {
                md.push_str(&format!(
                    "**Middleware:** {}\n\n",
                    route.middleware.join(", ")
                ));
            }

            md.push_str(&format!("**Response:** `{}`\n\n", route.response_type));
            md.push_str("---\n\n");
        }

        md
    }

    fn generate_client_routes_markdown(&self) -> String {
        let mut md = String::new();
        md.push_str("# Client Page Routes\n\n");
        md.push_str(&format!("Generated at: {}\n\n", self.generated_at));

        // Group routes by language
        let mut routes_by_lang: HashMap<String, Vec<&PageRouteInfo>> = HashMap::new();
        for route in &self.page_routes {
            let language = route
                .metadata
                .get("language")
                .and_then(|v| v.as_str())
                .unwrap_or("en")
                .to_string();
            routes_by_lang.entry(language).or_default().push(route);
        }

        for (lang, routes) in routes_by_lang {
            md.push_str(&format!("## {} Routes\n\n", lang.to_uppercase()));

            for route in routes {
                md.push_str(&format!("### {} ({})\n\n", route.path, route.component));

                let page_component = route
                    .metadata
                    .get("page_component")
                    .and_then(|v| v.as_str())
                    .unwrap_or(&route.page_component);
                md.push_str(&format!("**Page Component:** `{}`\n\n", page_component));
                let unified_component = route
                    .metadata
                    .get("unified_component")
                    .and_then(|v| v.as_str())
                    .unwrap_or(&route.unified_component);
                md.push_str(&format!(
                    "**Unified Component:** `{}`\n\n",
                    unified_component
                ));

                if let Some(module) = &route.module_path {
                    md.push_str(&format!("**Module Path:** `{}`\n\n", module));
                }

                let enabled = route
                    .metadata
                    .get("enabled")
                    .and_then(|v| v.as_bool())
                    .unwrap_or(route.enabled);
                md.push_str(&format!("**Enabled:** {}\n\n", enabled));
                let priority = route
                    .metadata
                    .get("priority")
                    .and_then(|v| v.as_float())
                    .unwrap_or(route.priority);
                md.push_str(&format!("**Priority:** {}\n\n", priority));

                if let Some(auth) = route.requires_auth {
                    md.push_str(&format!("**Authentication Required:** {}\n\n", auth));
                }

                let menu_group = route
                    .metadata
                    .get("menu_group")
                    .and_then(|v| v.as_str())
                    .or(route.menu_group.as_deref());
                if let Some(menu_group) = menu_group {
                    md.push_str(&format!("**Menu Group:** {}\n\n", menu_group));
                    if let Some(order) = route.menu_order {
                        md.push_str(&format!("**Menu Order:** {}\n\n", order));
                    }
                    if let Some(icon) = &route.menu_icon {
                        md.push_str(&format!("**Menu Icon:** {}\n\n", icon));
                    }
                }

                if !route.keywords.is_empty() {
                    md.push_str(&format!("**Keywords:** {}\n\n", route.keywords.join(", ")));
                }

                if !route.i18n_patterns.is_empty() {
                    md.push_str(&format!(
                        "**I18n Patterns:** {}\n\n",
                        route.i18n_patterns.join(", ")
                    ));
                }

                md.push_str("---\n\n");
            }
        }

        md
    }

    fn generate_components_markdown(&self) -> String {
        let mut md = String::new();
        md.push_str("# Components Reference\n\n");
        md.push_str(&format!("*Generated at: {}*\n\n", self.generated_at));

        // Add summary section
        md.push_str("## Summary\n\n");
        let total_components = self.components.len();
        let total_props = self.components.iter().map(|c| c.props.len()).sum::<usize>();

        // Group components by type for summary
        let mut components_by_type: HashMap<String, Vec<&ComponentInfo>> = HashMap::new();
        for component in &self.components {
            components_by_type
                .entry(component.component_type.clone())
                .or_default()
                .push(component);
        }

        md.push_str("| Metric | Count |\n");
        md.push_str("|--------|-------|\n");
        md.push_str(&format!(
            "| **Total Components** | {} |\n",
            total_components
        ));
        md.push_str(&format!("| **Total Props** | {} |\n", total_props));
        for (comp_type, components) in &components_by_type {
            md.push_str(&format!(
                "| **{} Components** | {} |\n",
                comp_type,
                components.len()
            ));
        }
        md.push('\n');

        md.push_str("## Table of Contents\n\n");
        for (comp_type, components) in &components_by_type {
            md.push_str(&format!("### {} Components\n", comp_type));
            for component in components {
                let anchor = generate_anchor("component", &component.name);
                md.push_str(&format!("- [{}](#{})\n", component.name, anchor));
            }
            md.push('\n');
        }

        md.push_str("## Component Details\n\n");

        for (comp_type, components) in components_by_type {
            md.push_str(&format!("### {} Components\n\n", comp_type));

            for component in components {
                let anchor = generate_anchor("component", &component.name);
                md.push_str(&generate_header_with_anchor(4, &component.name, &anchor));

                // Add source link if available
                if let Some(src_ref) = &component.src_ref {
                    md.push_str(&format!("{}\n\n", format_source_link(src_ref)));
                }

                if let Some(description) = &component.description {
                    md.push_str(&format!("**Description:** {}\n\n", description));
                }

                md.push_str(&format!("**File:** `{}`\n\n", component.file_path));
                md.push_str(&format!("**Module:** `{}`\n\n", component.module_path));

                if !component.props.is_empty() {
                    md.push_str("**Props:**\n\n");
                    for prop in &component.props {
                        md.push_str(&format!(
                            "- `{}`: {} - {}\n",
                            prop.name,
                            prop.prop_type,
                            if prop.optional {
                                "optional"
                            } else {
                                "required"
                            }
                        ));
                        if let Some(default) = &prop.default_value {
                            md.push_str(&format!("  - Default: `{}`\n", default));
                        }
                        if let Some(desc) = &prop.description {
                            md.push_str(&format!("  - {}\n", desc));
                        }
                    }
                    md.push('\n');
                }

                if !component.dependencies.is_empty() {
                    md.push_str(&format!(
                        "**Dependencies:** {}\n\n",
                        component.dependencies.join(", ")
                    ));
                }

                if let Some(usage) = &component.usage_example {
                    md.push_str("**Usage Example:**\n\n");
                    md.push_str("```rust\n");
                    md.push_str(usage);
                    md.push_str("\n```\n\n");
                }

                md.push_str("---\n\n");
            }
        }

        md
    }

    fn generate_automation_guide(&self) -> String {
        let mut md = String::new();
        md.push_str("# Route Documentation Automation Guide\n\n");
        md.push_str(&format!("Generated at: {}\n\n", self.generated_at));

        md.push_str("## Overview\n\n");
        md.push_str(
            "This documentation is automatically generated during the build process from the \
             actual route definitions and component code. The generated TOML files can be \
             consumed by external tools for automation purposes.\n\n",
        );

        md.push_str("## Generated Files\n\n");
        md.push_str("- `server_routes.toml` - API endpoint definitions in TOML format\n");
        md.push_str("- `client_routes.toml` - Page route definitions in TOML format\n");
        md.push_str("- `components.toml` - Component documentation in TOML format\n");
        md.push_str("- `*.md` files - Human-readable documentation\n\n");

        md.push_str("## TOML Schema\n\n");
        md.push_str("### Server Routes\n\n");
        md.push_str("```toml\n");
        md.push_str("[[api_routes]]\n");
        md.push_str("path = \"/api/example\"\n");
        md.push_str("methods = [\"GET\", \"POST\"]\n");
        md.push_str("handler = \"handlers::example_handler\"\n");
        md.push_str("module = \"crates/server/src/handlers/mod.rs\"\n");
        md.push_str("response_type = \"Json<ExampleResponse>\"\n");
        md.push_str("requires_auth = false\n");
        md.push_str("middleware = [\"cors\"]\n");
        md.push_str("description = \"Example API endpoint\"\n");
        md.push_str("```\n\n");

        md.push_str("### Client Routes\n\n");
        md.push_str("```toml\n");
        md.push_str("[[page_routes]]\n");
        md.push_str("path = \"/example\"\n");
        md.push_str("component = \"Example\"\n");
        md.push_str("page_component = \"ExamplePage\"\n");
        md.push_str("unified_component = \"UnifiedExamplePage\"\n");
        md.push_str("language = \"en\"\n");
        md.push_str("enabled = true\n");
        md.push_str("priority = 0.8\n");
        md.push_str("```\n\n");

        md.push_str("## Usage in External Tools\n\n");
        md.push_str("These TOML files can be consumed by:\n");
        md.push_str("- API client generators\n");
        md.push_str("- Testing frameworks\n");
        md.push_str("- Documentation generators\n");
        md.push_str("- CI/CD pipelines\n");
        md.push_str("- Monitoring and alerting systems\n\n");

        md.push_str("## Configuration\n\n");
        md.push_str("The documentation generation is controlled by:\n");
        md.push_str("- `SITE_CONFIG_PATH` environment variable (default: `site/config`)\n");
        md.push_str("- Build system integration in `crates/*/build.rs`\n");
        md.push_str("- Route configuration files in `site/config/routes/`\n\n");

        md
    }
}

impl Default for RouteDocumentation {
    fn default() -> Self {
        Self::new()
    }
}

/// Utility function to extract route parameters from path patterns
pub fn extract_path_parameters(path: &str) -> Vec<RouteParameter> {
    let mut parameters = Vec::new();

    // Find parameters in {param} format
    let param_regex = regex::Regex::new(r"\{([^}]+)\}").unwrap();

    for cap in param_regex.captures_iter(path) {
        if let Some(param_name) = cap.get(1) {
            parameters.push(RouteParameter {
                name: param_name.as_str().to_string(),
                param_type: "String".to_string(), // Default to String, could be enhanced
                source: "path".to_string(),
                optional: false,
                description: None,
            });
        }
    }

    parameters
}

/// Utility function to determine component type from file path
pub fn determine_component_type(file_path: &str) -> String {
    if file_path.contains("/pages/") {
        "Page".to_string()
    } else if file_path.contains("/components/") {
        "Component".to_string()
    } else if file_path.contains("/admin/") {
        "Admin".to_string()
    } else if file_path.contains("/auth/") {
        "Auth".to_string()
    } else {
        "Utility".to_string()
    }
}
