//! Server-side routing implementation using trait-based dependency injection
//!
//! This module implements server-side rendering (SSR) using the pure trait
//! abstractions from rustelo-*-traits crates. This eliminates code generation
//! and provides runtime flexibility while maintaining route agnosticism and SSR
//! support.

use std::collections::HashMap;

use leptos::prelude::*;
use rustelo_core_types::{
    AppContext, DefaultLocalizationProvider, DefaultPageProvider, LanguageDetector,
    LocalizationProvider, PageProvider, RouteHandler, RouteMetadataProvider, RouteParameters,
    RouteRenderer, RouteResult, RoutingResult,
};

/// Server-side route renderer using trait-based dependency injection for SSR
pub struct ServerRouteRenderer {
    #[allow(dead_code)]
    app_context: AppContext,
    page_provider: DefaultPageProvider,
    localization_provider: DefaultLocalizationProvider,
}

impl ServerRouteRenderer {
    pub fn new() -> Self {
        Self {
            app_context: AppContext::new(),
            page_provider: DefaultPageProvider::new(),
            localization_provider: DefaultLocalizationProvider::new(),
        }
    }

    pub fn with_context(app_context: AppContext) -> Self {
        Self {
            page_provider: DefaultPageProvider::new(),
            localization_provider: DefaultLocalizationProvider::new(),
            app_context,
        }
    }
}

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

impl RouteRenderer for ServerRouteRenderer {
    type View = AnyView;
    type Component = String; // Component ID
    type Parameters = RouteParameters;

    fn render_component(
        &self,
        component: &Self::Component,
        path: &str,
        language: &str,
        parameters: &Self::Parameters,
    ) -> RoutingResult<Self::View> {
        // Create props from parameters and context
        let mut props = parameters.clone().into_hashmap();
        props.insert("path".to_string(), path.to_string());
        props.insert("language".to_string(), language.to_string());

        // Use the page provider for SSR rendering
        match self
            .page_provider
            .render_component(component, props, language)
        {
            Ok(html) => {
                // For SSR, we can render the HTML directly as a view
                // This is a simplified approach - in production we'd want more sophisticated
                // SSR
                let view = view! {
                    <div inner_html=html />
                }
                .into_any();
                Ok(view)
            }
            Err(_) => {
                // Fallback to not found
                Ok(self.render_not_found(path, language)?)
            }
        }
    }

    fn render_not_found(&self, _path: &str, language: &str) -> RoutingResult<Self::View> {
        use rustelo_pages_leptos::not_found::unified::UnifiedNotFoundPage;
        let lang = language.to_string();

        // Render UnifiedNotFoundPage without explicit lang_content
        // The component handles empty content gracefully with fallbacks
        let view = view! { <UnifiedNotFoundPage _language=lang /> }.into_any();
        Ok(view)
    }
}

impl RouteMetadataProvider for ServerRouteRenderer {
    type Component = String;

    fn get_title(&self, component: &Self::Component, language: &str) -> RoutingResult<String> {
        // Use localization provider for titles
        let title_key = format!("{}-title", component);
        let fallback_args = HashMap::new();

        match self
            .localization_provider
            .get_localized_string(&title_key, language, &fallback_args)
        {
            Ok(title) => Ok(format!("{} - {}", title, crate::utils::site_name())),
            Err(_) => {
                // Fallback to component name
                let formatted_title = component.replace("-", " ").replace("_", " ");
                Ok(format!(
                    "{} - {}",
                    formatted_title,
                    crate::utils::site_name()
                ))
            }
        }
    }

    fn get_description(
        &self,
        component: &Self::Component,
        language: &str,
    ) -> RoutingResult<String> {
        // Use localization provider for descriptions
        let desc_key = format!("{}-description", component);
        let fallback_args = HashMap::new();

        match self
            .localization_provider
            .get_localized_string(&desc_key, language, &fallback_args)
        {
            Ok(description) => Ok(description),
            Err(_) => {
                // Fallback description
                Ok(format!(
                    "Rustelo application - {}",
                    component.replace("-", " ")
                ))
            }
        }
    }

    fn get_keywords(&self, component: &Self::Component, language: &str) -> RoutingResult<String> {
        // Use localization provider for keywords
        let keywords_key = format!("{}-keywords", component);
        let fallback_args = HashMap::new();

        match self.localization_provider.get_localized_string(
            &keywords_key,
            language,
            &fallback_args,
        ) {
            Ok(keywords) => Ok(format!("rustelo, web development, {}", keywords)),
            Err(_) => {
                // Fallback keywords
                Ok(format!(
                    "rustelo, web development, {}",
                    component.replace("-", " ")
                ))
            }
        }
    }
}

impl RouteHandler for ServerRouteRenderer {
    type View = AnyView;
    type Component = String;
    type Parameters = RouteParameters;

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

        let title = component
            .map(|c| {
                self.get_title(c, language)
                    .unwrap_or_else(|_| crate::utils::site_name())
            })
            .unwrap_or_else(|| format!("Page Not Found - {}", crate::utils::site_name()));

        let description = component
            .map(|c| {
                self.get_description(c, language)
                    .unwrap_or_else(|_| "Rustelo Web Application".to_string())
            })
            .unwrap_or_else(|| "The requested page was not found".to_string());

        let keywords = component
            .map(|c| {
                self.get_keywords(c, language)
                    .unwrap_or_else(|_| "rustelo".to_string())
            })
            .unwrap_or_else(|| "rustelo, not found".to_string());

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

/// Render page content using trait-based SSR routing system
pub fn render_page_content_ssr(path: &str) -> AnyView {
    render_page_content_ssr_with_language(path, "en")
}

/// Render page content with explicit language using trait-based SSR routing
pub fn render_page_content_ssr_with_language(path: &str, language: &str) -> AnyView {
    let renderer = ServerRouteRenderer::new();
    let app_context = AppContext::new();

    // Use trait-based language detection
    let (detected_lang, clean_path) = app_context.language_detector.extract_language(path);
    let final_language = if language != "auto" {
        language
    } else {
        &detected_lang
    };

    // Normalize path with leading slash for route lookup
    // clean_path is the path without language prefix, but may lack leading slash
    let lookup_path = if clean_path.starts_with('/') {
        clean_path.to_string()
    } else {
        format!("/{}", clean_path)
    };

    // Use actual route configuration from rustelo_core_lib instead of hardcoded
    // resolver
    let component =
        rustelo_core_lib::routing::config::get_route_for_language(&lookup_path, final_language)
            .map(|route_config| route_config.component.clone());

    // Handle the resolved component
    match renderer.handle_route(
        component.as_ref(),
        &lookup_path,
        final_language,
        &RouteParameters::new(),
    ) {
        Ok(route_result) => route_result.view,
        Err(_) => {
            // Fallback to not found
            renderer
                .render_not_found(path, final_language)
                .unwrap_or_else(|_| view! { <div>"Error loading page"</div> }.into_any())
        }
    }
}

/// Get page metadata for server-side rendering using trait-based routing
pub fn get_page_metadata(path: &str) -> (String, String, String) {
    get_page_metadata_with_language(path, "en")
}

/// Get page metadata with explicit language using trait-based routing
pub fn get_page_metadata_with_language(path: &str, language: &str) -> (String, String, String) {
    let renderer = ServerRouteRenderer::new();
    let app_context = AppContext::new();

    // Use trait-based language detection
    let (detected_lang, clean_path) = app_context.language_detector.extract_language(path);
    let final_language = if language != "auto" {
        language
    } else {
        &detected_lang
    };

    // Normalize path with leading slash for route lookup
    // clean_path is the path without language prefix, but may lack leading slash
    let lookup_path = if clean_path.starts_with('/') {
        clean_path.to_string()
    } else {
        format!("/{}", clean_path)
    };

    // Use actual route configuration from rustelo_core_lib instead of hardcoded
    // resolver
    let component =
        rustelo_core_lib::routing::config::get_route_for_language(&lookup_path, final_language)
            .map(|route_config| route_config.component.clone());

    // Get metadata for the resolved component
    match renderer.handle_route(
        component.as_ref(),
        &lookup_path,
        final_language,
        &RouteParameters::new(),
    ) {
        Ok(route_result) => (
            route_result.title,
            route_result.description,
            route_result.keywords,
        ),
        Err(_) => {
            // Fallback metadata
            (
                "Page Not Found - Rustelo".to_string(),
                "The requested page was not found".to_string(),
                "rustelo, not found".to_string(),
            )
        }
    }
}
