//! Pure routing traits for Rustelo framework
//!
//! This crate provides the core abstractions for routing without any
//! dependencies or code generation. These traits define the contracts that
//! implementations must fulfill to work with the Rustelo routing system.
//!
//! ## Key Principles
//!
//! - **Zero dependencies**: No external crates, no std beyond core
//! - **No code generation**: Pure trait definitions
//! - **No environment variables**: No build-time configuration
//! - **Generic by design**: Uses associated types and generic parameters
//!
//! ## Architecture
//!
//! The routing system is built around three main abstractions:
//!
//! 1. **RouteRenderer**: Renders route components to views
//! 2. **RouteMetadataProvider**: Provides SEO and meta information
//! 3. **RouteHandler**: Combines rendering and metadata for complete route
//!    handling

#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![warn(clippy::all)]

use core::fmt;
use std::collections::HashMap;

/// Core result type for routing operations
pub type RoutingResult<T> = Result<T, RoutingError>;

/// Errors that can occur during routing operations
#[derive(Debug, Clone, PartialEq)]
pub enum RoutingError {
    /// Route not found
    NotFound(String),
    /// Invalid route format
    InvalidRoute(String),
    /// Component rendering failed
    RenderError(String),
    /// Metadata extraction failed
    MetadataError(String),
}

impl fmt::Display for RoutingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RoutingError::NotFound(path) => write!(f, "Route not found: {}", path),
            RoutingError::InvalidRoute(route) => write!(f, "Invalid route format: {}", route),
            RoutingError::RenderError(msg) => write!(f, "Render error: {}", msg),
            RoutingError::MetadataError(msg) => write!(f, "Metadata error: {}", msg),
        }
    }
}

/// Trait for rendering route components to views
///
/// This is the core abstraction that allows different rendering contexts
/// (client-side WASM, server-side SSR) to implement their own view types
/// while sharing the same routing logic.
pub trait RouteRenderer {
    /// The view type that this renderer produces
    type View;

    /// The component type that this renderer works with
    type Component;

    /// Parameters extracted from the route
    type Parameters: AsRef<HashMap<String, String>>;

    /// Render a route component to a view
    ///
    /// # Arguments
    /// * `component` - The component to render
    /// * `path` - The original path that was matched
    /// * `language` - The language code for this request
    /// * `parameters` - Parameters extracted from the route
    fn render_component(
        &self,
        component: &Self::Component,
        path: &str,
        language: &str,
        parameters: &Self::Parameters,
    ) -> RoutingResult<Self::View>;

    /// Render a not found page
    ///
    /// # Arguments
    /// * `path` - The path that was not found
    /// * `language` - The language code for this request
    fn render_not_found(&self, path: &str, language: &str) -> RoutingResult<Self::View>;
}

/// Trait for providing metadata about routes
///
/// This abstraction allows different systems to provide SEO metadata,
/// page titles, descriptions, and other meta information in their own way.
pub trait RouteMetadataProvider {
    /// The component type that this provider works with
    type Component;

    /// Get the page title for a route
    ///
    /// # Arguments
    /// * `component` - The component being rendered
    /// * `language` - The language code for this request
    fn get_title(&self, component: &Self::Component, language: &str) -> RoutingResult<String>;

    /// Get the page description for a route
    ///
    /// # Arguments
    /// * `component` - The component being rendered
    /// * `language` - The language code for this request
    fn get_description(&self, component: &Self::Component, language: &str)
        -> RoutingResult<String>;

    /// Get the page keywords for a route
    ///
    /// # Arguments
    /// * `component` - The component being rendered
    /// * `language` - The language code for this request
    fn get_keywords(&self, component: &Self::Component, language: &str) -> RoutingResult<String>;

    /// Get additional metadata as key-value pairs
    ///
    /// # Arguments
    /// * `component` - The component being rendered
    /// * `language` - The language code for this request
    fn get_metadata(
        &self,
        component: &Self::Component,
        language: &str,
    ) -> RoutingResult<HashMap<String, String>> {
        // Default implementation provides empty metadata
        let _ = (component, language);
        Ok(HashMap::new())
    }
}

/// Combined trait for complete route handling
///
/// This trait combines rendering and metadata provision for complete route
/// handling. Most implementations will implement this trait by composing a
/// renderer and metadata provider.
pub trait RouteHandler {
    /// The view type produced by rendering
    type View;

    /// The component type this handler works with
    type Component;

    /// Parameters extracted from routes
    type Parameters: AsRef<HashMap<String, String>>;

    /// Handle a complete route request
    ///
    /// # Arguments
    /// * `component` - The component to render (None for not found)
    /// * `path` - The original path
    /// * `language` - The language code
    /// * `parameters` - Route parameters
    fn handle_route(
        &self,
        component: Option<&Self::Component>,
        path: &str,
        language: &str,
        parameters: &Self::Parameters,
    ) -> RoutingResult<RouteResult<Self::View>>;
}

/// Result of handling a complete route
#[derive(Debug, Clone)]
pub struct RouteResult<V> {
    /// The rendered view
    pub view: V,
    /// Page title for SEO
    pub title: String,
    /// Page description for SEO
    pub description: String,
    /// Page keywords for SEO
    pub keywords: String,
    /// Language code for this result
    pub language: String,
    /// Canonical path for this route
    pub canonical_path: String,
    /// Whether this represents a not found page
    pub is_not_found: bool,
    /// Additional metadata
    pub metadata: HashMap<String, String>,
}

/// Trait for route resolution
///
/// This abstraction allows different routing engines to resolve paths
/// to components in their own way, while maintaining a consistent interface.
pub trait RouteResolver {
    /// The component type this resolver produces
    type Component;

    /// Parameters type for this resolver
    type Parameters: AsRef<HashMap<String, String>>;

    /// Resolve a path to a route resolution
    ///
    /// # Arguments
    /// * `path` - The path to resolve
    fn resolve_route(
        &self,
        path: &str,
    ) -> RoutingResult<RouteResolution<Self::Component, Self::Parameters>>;

    /// Get all available routes for a language
    ///
    /// # Arguments
    /// * `language` - The language to get routes for
    fn get_routes_for_language(&self, language: &str) -> RoutingResult<Vec<String>>;

    /// Check if a route exists
    ///
    /// # Arguments
    /// * `path` - The path to check
    fn route_exists(&self, path: &str) -> bool {
        self.resolve_route(path).is_ok()
    }
}

/// Result of route resolution
#[derive(Debug, Clone)]
pub struct RouteResolution<C, P> {
    /// The resolved component (None if not found)
    pub component: Option<C>,
    /// The cleaned/canonical path
    pub path: String,
    /// The detected language
    pub language: String,
    /// The canonical path for this route
    pub canonical_path: String,
    /// Parameters extracted from the route
    pub parameters: P,
    /// Whether this is a language-specific route
    pub is_language_specific: bool,
}

/// Trait for language detection from paths
///
/// This abstraction allows different language detection strategies
/// while maintaining a consistent interface.
pub trait LanguageDetector {
    /// Extract language and clean path from a request path
    ///
    /// # Arguments
    /// * `path` - The original request path
    ///
    /// # Returns
    /// A tuple of (language, cleaned_path) where language is the detected
    /// language code and cleaned_path is the path without language prefix
    fn extract_language(&self, path: &str) -> (String, String);

    /// Get the default language for the system
    fn default_language(&self) -> &str;

    /// Get all supported languages
    fn supported_languages(&self) -> &[String];

    /// Check if a language is supported
    ///
    /// # Arguments
    /// * `language` - The language code to check
    fn is_supported_language(&self, language: &str) -> bool {
        self.supported_languages().contains(&language.to_string())
    }
}
