//! Routing types and route configuration structures

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::rendering::RenderingProfile;

// Type aliases
/// Path type for routes and URLs
pub type RoutePath = String;

/// Component identifier type
pub type ComponentId = String;

/// Language code type (ISO 639-1)
pub type LanguageCode = String;

/// Core route information
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RouteInfo {
    /// The original request path
    pub path: RoutePath,
    /// The canonical path for this route
    pub canonical_path: RoutePath,
    /// The detected language
    pub language: LanguageCode,
    /// Whether this route is language-specific
    pub is_language_specific: bool,
    /// Parameters extracted from the route
    pub parameters: HashMap<String, String>,
    /// Route priority for matching
    pub priority: f32,
}

/// Route metadata for SEO and navigation
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RouteMetadata {
    /// Page title
    pub title: String,
    /// Page description
    pub description: String,
    /// Page keywords
    pub keywords: String,
    /// Canonical URL
    pub canonical_url: String,
    /// Language code
    pub language: LanguageCode,
    /// Additional meta tags
    pub meta_tags: HashMap<String, String>,
    /// Open Graph data
    pub og_data: HashMap<String, String>,
}

/// Navigation item for menus and breadcrumbs
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NavigationItem {
    /// Display label (localized)
    pub label: String,
    /// Target URL or path
    pub url: String,
    /// Whether this item is currently active
    pub is_active: bool,
    /// Icon identifier (optional)
    pub icon: Option<String>,
    /// Item order/weight
    pub order: i32,
    /// Child navigation items
    pub children: Vec<NavigationItem>,
}

/// Route configuration from TOML file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteConfigToml {
    pub path: String,
    pub component: String,
    pub title_key: String,
    pub language: String,
    pub enabled: bool,

    // Enhanced metadata for navigation and menus
    #[serde(default)]
    pub description_key: Option<String>,
    #[serde(default)]
    pub keywords: Option<Vec<String>>,
    #[serde(default)]
    pub priority: Option<f32>, // For sitemap priority
    #[serde(default)]
    pub menu_group: Option<String>, // Menu grouping (e.g., "main", "footer", "admin")
    #[serde(default)]
    pub menu_order: Option<i32>, // Order within menu group
    #[serde(default)]
    pub menu_icon: Option<String>, // Icon identifier for menu item
    #[serde(default)]
    pub is_external: Option<bool>, // Whether this is an external URL
    #[serde(default)]
    pub requires_auth: Option<bool>, // Whether authentication is required
    #[serde(default)]
    pub show_in_sitemap: Option<bool>, // Whether to include in sitemap
    #[serde(default)]
    pub alternate_paths: Option<Vec<String>>, // Alternative paths that redirect here
    #[serde(default)]
    pub canonical_path: Option<String>, // Canonical version for this language
    #[serde(default)]
    pub content_type: Option<String>, // Content type for dynamic routing (from content-kinds.toml)
    /// Per-route client interactivity strategy; `None` defers to the workspace
    /// default.
    #[serde(default)]
    pub rendering_profile: Option<RenderingProfile>,
    /// Template file name (relative to `templates_root`) for `kind =
    /// "template"` routes. When set, the request is rendered by the
    /// framework's Minijinja engine instead of the Leptos component tree,
    /// fully bypassing the shell.
    #[serde(default)]
    pub template: Option<String>,
    #[serde(default)]
    pub props: Option<HashMap<String, toml::Value>>, /* Additional properties from
                                                      * [routes.props] section */

    // Component generation and i18n fields
    #[serde(default)]
    pub unified_component: Option<String>, // Unified component name (e.g., "UnifiedHomePage")
    #[serde(default)]
    pub lang_prefixes: Option<Vec<String>>, /* Language prefixes for i18n patterns (e.g.,
                                             * ["home-", "welcome-"]) */
    #[serde(default)]
    pub params_component: Option<HashMap<String, String>>, /* Component parameters (e.g.,
                                                            * {"content-type": "blog"}) - can be
                                                            * empty */
}

impl RouteConfigToml {
    /// Get title key (backward compatibility method)
    pub fn get_title_key(&self) -> Option<String> {
        if self.title_key.is_empty() {
            None
        } else {
            Some(self.title_key.clone())
        }
    }

    /// Get description key
    pub fn get_description_key(&self) -> Option<String> {
        self.description_key.clone()
    }

    /// Get keywords as comma-separated string
    pub fn get_keywords(&self) -> Option<String> {
        self.keywords.as_ref().map(|k| k.join(", "))
    }

    /// Get priority for SEO/sitemap
    pub fn get_priority(&self) -> f32 {
        self.priority.unwrap_or(0.5)
    }

    /// Get canonical path
    pub fn get_canonical_path(&self) -> Option<String> {
        self.canonical_path.clone()
    }

    /// Check if should show in sitemap
    pub fn get_show_in_sitemap(&self) -> bool {
        self.show_in_sitemap.unwrap_or(true)
    }

    /// Get enabled status
    pub fn get_enabled(&self) -> bool {
        self.enabled
    }

    /// Get language
    pub fn get_language(&self) -> Option<String> {
        Some(self.language.clone())
    }

    /// Convert string component name to RouteComponent enum
    pub fn to_route_component(&self) -> RouteComponent {
        RouteComponent::from_str(&self.component).unwrap_or_default()
    }

    /// Check if this route should be shown in sitemap
    pub fn is_visible_in_sitemap(&self) -> bool {
        self.show_in_sitemap.unwrap_or(true) && self.enabled
    }

    /// Check if this route requires authentication
    pub fn needs_authentication(&self) -> bool {
        self.requires_auth.unwrap_or(false)
    }

    /// Check if this is an external URL
    pub fn is_external_url(&self) -> bool {
        self.is_external.unwrap_or(false)
    }

    /// Get menu order for sorting
    pub fn get_menu_order(&self) -> i32 {
        self.menu_order.unwrap_or(999) // High number = lower priority
    }

    /// Check if this route belongs to a specific menu group
    pub fn is_in_menu_group(&self, group: &str) -> bool {
        self.menu_group.as_ref().is_some_and(|g| g == group)
    }

    /// Get content_type from direct field or props section
    pub fn get_content_type(&self) -> Option<String> {
        // First try direct content_type field
        if let Some(ref content_type) = self.content_type {
            return Some(content_type.clone());
        }

        // Fallback to props.content_type
        if let Some(ref props) = self.props {
            if let Some(content_type) = props.get("content_type") {
                if let Some(content_type_str) = content_type.as_str() {
                    return Some(content_type_str.to_string());
                }
            }
        }

        None
    }

    /// Get the unified component name, fallback to component if not specified
    pub fn get_unified_component(&self) -> String {
        self.unified_component
            .as_ref()
            .unwrap_or(&self.component)
            .clone()
    }

    /// Get language prefixes for i18n, defaulting to empty vec if not specified
    pub fn get_lang_prefixes(&self) -> Vec<String> {
        self.lang_prefixes.as_ref().unwrap_or(&Vec::new()).clone()
    }

    /// Get component parameters, defaulting to empty map if not specified
    pub fn get_params_component(&self) -> HashMap<String, String> {
        self.params_component
            .as_ref()
            .unwrap_or(&HashMap::new())
            .clone()
    }

    /// Check if route has component parameters
    pub fn has_component_params(&self) -> bool {
        self.params_component
            .as_ref()
            .is_some_and(|params| !params.is_empty())
    }

    /// Get a specific component parameter
    pub fn get_component_param(&self, key: &str) -> Option<String> {
        self.params_component.as_ref()?.get(key).cloned()
    }
}

/// Container for all routes from TOML
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutesConfig {
    pub routes: Vec<RouteConfigToml>,
}

impl RoutesConfig {
    /// Create new routes config
    pub fn new() -> Self {
        Self { routes: Vec::new() }
    }

    /// Get all routes for a specific language
    pub fn get_routes_for_language(&self, language: &str) -> Vec<&RouteConfigToml> {
        self.routes
            .iter()
            .filter(|route| route.enabled && route.language == language)
            .collect()
    }

    /// Get routes for a specific menu group and language, sorted by menu order
    pub fn get_menu_routes(&self, menu_group: &str, language: &str) -> Vec<&RouteConfigToml> {
        let mut routes: Vec<&RouteConfigToml> = self
            .routes
            .iter()
            .filter(|route| {
                route.enabled && route.language == language && route.is_in_menu_group(menu_group)
            })
            .collect();

        // Sort by menu_order
        routes.sort_by_key(|a| a.get_menu_order());
        routes
    }

    /// Get all unique menu groups for a language
    pub fn get_menu_groups(&self, language: &str) -> Vec<String> {
        let mut groups: Vec<String> = self
            .routes
            .iter()
            .filter(|route| route.enabled && route.language == language)
            .filter_map(|route| route.menu_group.as_ref())
            .cloned()
            .collect();

        groups.sort();
        groups.dedup();
        groups
    }

    /// Get routes visible in sitemap for a language
    pub fn get_sitemap_routes(&self, language: &str) -> Vec<&RouteConfigToml> {
        self.routes
            .iter()
            .filter(|route| {
                route.enabled
                    && route.language == language
                    && route.is_visible_in_sitemap()
                    && !route.is_external_url()
            })
            .collect()
    }

    /// Find route by path and language
    pub fn find_route(&self, path: &str, language: &str) -> Option<&RouteConfigToml> {
        self.routes
            .iter()
            .find(|route| route.enabled && route.path == path && route.language == language)
    }

    /// Get all external links for a language
    pub fn get_external_links(&self, language: &str) -> Vec<&RouteConfigToml> {
        self.routes
            .iter()
            .filter(|route| route.enabled && route.language == language && route.is_external_url())
            .collect()
    }

    /// Get routes that require authentication
    pub fn get_protected_routes(&self, language: &str) -> Vec<&RouteConfigToml> {
        self.routes
            .iter()
            .filter(|route| {
                route.enabled && route.language == language && route.needs_authentication()
            })
            .collect()
    }

    /// Get canonical route (preferred route) for a component and language
    pub fn get_canonical_route(&self, component: &str, language: &str) -> Option<&RouteConfigToml> {
        // First, try to find a route with canonical_path set to itself (self-canonical)
        let canonical = self
            .routes
            .iter()
            .filter(|route| {
                route.enabled
                    && route.component == component
                    && route.language == language
                    && route.get_canonical_path() == Some(route.path.clone())
            })
            .min_by(|a, b| {
                // Prefer routes without /lang/ prefix
                let a_is_prefix = a.path.starts_with(&format!("/{language}/"));
                let b_is_prefix = b.path.starts_with(&format!("/{language}/"));
                a_is_prefix.cmp(&b_is_prefix)
            });

        // If no self-canonical route found, get the first enabled route for this
        // component
        canonical.or_else(|| {
            self.routes.iter().find(|route| {
                route.enabled && route.component == component && route.language == language
            })
        })
    }
}

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

use std::str::FromStr;

/// Fallback page component enum for development
/// This will be replaced by generated code in implementation projects
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PageComponent {
    /// Home page
    HomePage,
    /// Default page fallback
    #[default]
    DefaultPage,
    /// Not found page
    NotFoundPage,
}

impl FromStr for PageComponent {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "HomePage" | "homepage" | "home" => Ok(PageComponent::HomePage),
            "DefaultPage" | "default" => Ok(PageComponent::DefaultPage),
            "NotFoundPage" | "notfound" | "404" | "NotFound" => Ok(PageComponent::NotFoundPage),
            _ => Ok(PageComponent::DefaultPage), // Fallback to default for unknown components
        }
    }
}

impl PageComponent {
    /// Backward compatibility alias for NotFound
    pub const NOT_FOUND: Self = Self::NotFoundPage;

    pub fn as_str(&self) -> &'static str {
        match self {
            PageComponent::HomePage => "HomePage",
            PageComponent::DefaultPage => "DefaultPage",
            PageComponent::NotFoundPage => "NotFoundPage",
        }
    }

    pub fn title_key(&self) -> &'static str {
        match self {
            PageComponent::HomePage => "page-home-title",
            PageComponent::DefaultPage => "page-default-title",
            PageComponent::NotFoundPage => "page-notfound-title",
        }
    }

    pub fn description_key(&self) -> &'static str {
        match self {
            PageComponent::HomePage => "page-home-description",
            PageComponent::DefaultPage => "page-default-description",
            PageComponent::NotFoundPage => "page-notfound-description",
        }
    }
}

/// Alias for backward compatibility
pub type RouteComponent = PageComponent;

/// Components module for compatibility
pub mod components {
    pub use super::{PageComponent, RouteComponent, RouteConfigToml, RoutesConfig};
}
