//! Core runtime template using trait-based dependency injection
//!
//! This template provides the core runtime for Rustelo applications using pure
//! trait-based abstractions instead of code generation. Implementations clone
//! this template and provide concrete implementations.
//!
//! ## Key Principles
//!
//! - **Zero code generation**: No build.rs dependencies on environment variables
//! - **Pure dependency injection**: Uses traits from the pure traits layer
//! - **Implementation flexibility**: Cloned templates can be fully customized
//! - **No circular dependencies**: Clean dependency graph through trait abstractions

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

// Re-export pure traits for convenience
pub use rustelo_routing_traits::*;
pub use rustelo_component_traits::*;
pub use rustelo_core_types::*;

pub mod routing;
pub mod components;
pub mod content;
pub mod i18n;
pub mod errors;
pub mod utils;

// Optional modules behind feature flags
#[cfg(feature = "wasm")]
pub mod wasm;

#[cfg(feature = "time")]
pub mod time;

#[cfg(feature = "ids")]
pub mod ids;

/// Core application context using dependency injection
///
/// This struct uses trait-based dependency injection instead of concrete types,
/// allowing implementations to provide their own page providers, content loaders, etc.
#[derive(Clone)]
pub struct AppContext<P, C, L>
where
    P: PageProvider,
    C: ContentProvider,
    L: LocalizationProvider,
{
    /// Page provider for component resolution
    pub page_provider: P,
    /// Content provider for data access
    pub content_provider: C,
    /// Localization provider for i18n
    pub localization_provider: L,
    /// Site configuration
    pub site_config: SiteConfig,
}

impl<P, C, L> AppContext<P, C, L>
where
    P: PageProvider,
    C: ContentProvider,
    L: LocalizationProvider,
{
    /// Create new application context with dependency injection
    pub fn new(
        page_provider: P,
        content_provider: C,
        localization_provider: L,
        site_config: SiteConfig,
    ) -> Self {
        Self {
            page_provider,
            content_provider,
            localization_provider,
            site_config,
        }
    }

    /// Get available languages from localization provider
    pub fn available_languages(&self) -> Vec<String> {
        self.localization_provider.available_languages()
    }

    /// Get default language from localization provider
    pub fn default_language(&self) -> String {
        self.localization_provider.default_language()
    }
}

/// Generic route handler using trait-based dependency injection
///
/// Instead of hardcoded generated components, this uses the PageProvider trait
/// to resolve components at runtime, allowing implementations to provide their own.
pub struct GenericRouteHandler<P, R, M>
where
    P: PageProvider,
    R: RouteRenderer<Component = P::Component, View = P::View>,
    M: RouteMetadataProvider<Component = P::Component>,
{
    /// Page provider for component resolution
    pub page_provider: P,
    /// Route renderer for view generation
    pub route_renderer: R,
    /// Metadata provider for SEO data
    pub metadata_provider: M,
}

impl<P, R, M> GenericRouteHandler<P, R, M>
where
    P: PageProvider,
    R: RouteRenderer<Component = P::Component, View = P::View>,
    M: RouteMetadataProvider<Component = P::Component>,
{
    /// Create new route handler with injected dependencies
    pub fn new(page_provider: P, route_renderer: R, metadata_provider: M) -> Self {
        Self {
            page_provider,
            route_renderer,
            metadata_provider,
        }
    }
}

impl<P, R, M> RouteHandler for GenericRouteHandler<P, R, M>
where
    P: PageProvider,
    R: RouteRenderer<Component = P::Component, View = P::View>,
    M: RouteMetadataProvider<Component = P::Component>,
    R::Parameters: AsRef<std::collections::HashMap<String, String>>,
{
    type View = P::View;
    type Component = P::Component;
    type Parameters = R::Parameters;

    fn handle_route(
        &self,
        component: Option<&Self::Component>,
        path: &str,
        language: &str,
        parameters: &Self::Parameters,
    ) -> RoutingResult<RouteResult<Self::View>> {
        let view = match component {
            Some(comp) => self.route_renderer.render_component(comp, path, language, parameters)?,
            None => self.route_renderer.render_not_found(path, language)?,
        };

        let (title, description, keywords) = if let Some(comp) = component {
            let title = self.metadata_provider.get_title(comp, language)?;
            let description = self.metadata_provider.get_description(comp, language)?;
            let keywords = self.metadata_provider.get_keywords(comp, language)?;
            (title, description, keywords)
        } else {
            (
                "Page Not Found".to_string(),
                "The requested page could not be found".to_string(),
                "error, not found".to_string(),
            )
        };

        let metadata = if let Some(comp) = component {
            self.metadata_provider.get_metadata(comp, language)?
        } else {
            std::collections::HashMap::new()
        };

        Ok(RouteResult {
            view,
            title,
            description,
            keywords,
            language: language.to_string(),
            canonical_path: path.to_string(),
            is_not_found: component.is_none(),
            metadata,
        })
    }
}

/// Generic component handler using trait-based dependency injection
///
/// Combines page provision, content loading, and localization through traits
/// instead of hardcoded implementations.
pub struct GenericComponentHandler<P, C, L>
where
    P: PageProvider,
    C: ContentProvider,
    L: LocalizationProvider,
{
    /// Page provider for component access
    pub page_provider: P,
    /// Content provider for data access
    pub content_provider: C,
    /// Localization provider for i18n
    pub localization_provider: L,
}

impl<P, C, L> GenericComponentHandler<P, C, L>
where
    P: PageProvider,
    C: ContentProvider,
    L: LocalizationProvider,
{
    /// Create new component handler with injected dependencies
    pub fn new(page_provider: P, content_provider: C, localization_provider: L) -> Self {
        Self {
            page_provider,
            content_provider,
            localization_provider,
        }
    }
}

impl<P, C, L> ComponentHandler for GenericComponentHandler<P, C, L>
where
    P: PageProvider,
    C: ContentProvider,
    L: LocalizationProvider,
{
    type Component = P::Component;
    type View = P::View;
    type Content = C::ContentItem;
    type Props = P::Props;

    fn handle_component(
        &self,
        component_id: &str,
        props: Self::Props,
        language: &str,
    ) -> ComponentResult<Self::View> {
        let component = self.page_provider.get_component(component_id)?;
        self.page_provider.render_component(&component, props, language)
    }

    fn preload_content(&self, component_id: &str, language: &str) -> ComponentResult<Self::Content> {
        // Use component_id as content_id by default - implementations can override
        self.content_provider
            .get_content(component_id, language)
            .map_err(|e| ComponentError::ContentError(format!("Failed to preload content: {}", e)))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    // Mock implementations for testing
    struct MockPageProvider;
    struct MockContentProvider;
    struct MockLocalizationProvider;
    struct MockComponent;
    struct MockView;
    struct MockContent;

    impl PageProvider for MockPageProvider {
        type Component = MockComponent;
        type View = MockView;
        type Props = ();

        fn get_component(&self, _component_id: &str) -> ComponentResult<Self::Component> {
            Ok(MockComponent)
        }

        fn render_component(
            &self,
            _component: &Self::Component,
            _props: Self::Props,
            _language: &str,
        ) -> ComponentResult<Self::View> {
            Ok(MockView)
        }

        fn list_components(&self) -> Vec<String> {
            vec!["test".to_string()]
        }
    }

    impl ContentProvider for MockContentProvider {
        type ContentItem = MockContent;
        type ContentCollection = Vec<MockContent>;
        type Query = String;

        fn get_content(&self, _content_id: &str, _language: &str) -> ComponentResult<Self::ContentItem> {
            Ok(MockContent)
        }

        fn get_content_collection(
            &self,
            _query: &Self::Query,
            _language: &str,
        ) -> ComponentResult<Self::ContentCollection> {
            Ok(vec![MockContent])
        }

        fn list_content(&self, _language: &str) -> ComponentResult<Vec<String>> {
            Ok(vec!["test_content".to_string()])
        }
    }

    impl LocalizationProvider for MockLocalizationProvider {
        fn get_localized_string(
            &self,
            _key: &str,
            _language: &str,
            _args: &HashMap<String, String>,
        ) -> ComponentResult<String> {
            Ok("test_string".to_string())
        }

        fn available_languages(&self) -> Vec<String> {
            vec!["en".to_string(), "es".to_string()]
        }

        fn default_language(&self) -> String {
            "en".to_string()
        }
    }

    #[test]
    fn test_app_context_creation() {
        let page_provider = MockPageProvider;
        let content_provider = MockContentProvider;
        let localization_provider = MockLocalizationProvider;
        let site_config = SiteConfig {
            name: "Test Site".to_string(),
            description: "Test Description".to_string(),
            base_url: "https://test.com".to_string(),
            default_language: "en".to_string(),
            languages: vec![],
            default_theme: "default".to_string(),
            themes: vec![],
            contact_email: None,
            metadata: HashMap::new(),
        };

        let context = AppContext::new(
            page_provider,
            content_provider,
            localization_provider,
            site_config,
        );

        assert_eq!(context.default_language(), "en");
        assert_eq!(context.available_languages(), vec!["en", "es"]);
    }

    #[test]
    fn test_generic_component_handler() {
        let page_provider = MockPageProvider;
        let content_provider = MockContentProvider;
        let localization_provider = MockLocalizationProvider;

        let handler = GenericComponentHandler::new(
            page_provider,
            content_provider,
            localization_provider,
        );

        let result = handler.handle_component("test", (), "en");
        assert!(result.is_ok());

        let preload_result = handler.preload_content("test", "en");
        assert!(preload_result.is_ok());
    }
}
