//! Server template using trait-based dependency injection
//!
//! This template provides a server-side 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::*;

// Re-export core functionality
pub use core_lib_template::*;

pub mod server;
pub mod handlers;
pub mod routes;
pub mod middleware;
pub mod config;
pub mod error;

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

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

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

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

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

// Essential re-exports for server functionality
pub use axum::{
    extract::{State, Path, Query, Json},
    response::{Html, Json as JsonResponse, Response, IntoResponse},
    routing::{get, post, put, delete},
    Router,
    serve,
};

pub use leptos::*;
pub use leptos_axum::{generate_route_list, LeptosRoutes};
pub use tokio;
pub use tower::ServiceBuilder;
pub use tower_http::{
    cors::CorsLayer,
    services::ServeDir,
    trace::TraceLayer,
};

use std::sync::Arc;

/// Server application state using dependency injection
///
/// This struct uses trait-based dependency injection instead of concrete types,
/// allowing implementations to provide their own services, repositories, etc.
#[derive(Clone)]
pub struct ServerState<P, C, L, R>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    /// Page provider for component resolution
    pub page_provider: Arc<P>,
    /// Content provider for data access
    pub content_provider: Arc<C>,
    /// Localization provider for i18n
    pub localization_provider: Arc<L>,
    /// Route resolver for path handling
    pub route_resolver: Arc<R>,
    /// Site configuration
    pub site_config: Arc<SiteConfig>,
    /// Framework configuration
    pub framework_config: Arc<FrameworkConfig>,
}

impl<P, C, L, R> ServerState<P, C, L, R>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    /// Create new server state with dependency injection
    pub fn new(
        page_provider: P,
        content_provider: C,
        localization_provider: L,
        route_resolver: R,
        site_config: SiteConfig,
        framework_config: FrameworkConfig,
    ) -> Self {
        Self {
            page_provider: Arc::new(page_provider),
            content_provider: Arc::new(content_provider),
            localization_provider: Arc::new(localization_provider),
            route_resolver: Arc::new(route_resolver),
            site_config: Arc::new(site_config),
            framework_config: Arc::new(framework_config),
        }
    }

    /// Get app context for requests
    pub fn app_context(&self) -> AppContext<Arc<P>, Arc<C>, Arc<L>> {
        AppContext::new(
            Arc::clone(&self.page_provider),
            Arc::clone(&self.content_provider),
            Arc::clone(&self.localization_provider),
            (*self.site_config).clone(),
        )
    }
}

/// Server builder using trait-based dependency injection
///
/// Instead of hardcoded services, this uses generic parameters that implementations
/// can provide their own concrete types for.
pub struct ServerBuilder<P, C, L, R>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    state: Option<ServerState<P, C, L, R>>,
    leptos_options: Option<leptos::LeptosOptions>,
}

impl<P, C, L, R> ServerBuilder<P, C, L, R>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    /// Create new server builder
    pub fn new() -> Self {
        Self {
            state: None,
            leptos_options: None,
        }
    }

    /// Set server state with dependency injection
    pub fn state(mut self, state: ServerState<P, C, L, R>) -> Self {
        self.state = Some(state);
        self
    }

    /// Set Leptos configuration
    pub fn leptos_options(mut self, options: leptos::LeptosOptions) -> Self {
        self.leptos_options = Some(options);
        self
    }

    /// Build the Axum router
    pub fn build_router(self) -> Result<Router, ServerError> {
        let state = self.state.ok_or(ServerError::Config("Missing server state".to_string()))?;
        let leptos_options = self.leptos_options.ok_or(ServerError::Config("Missing Leptos options".to_string()))?;

        // Build the router with trait-based routing
        let router = Router::new()
            // API routes
            .nest("/api", self.build_api_routes())
            // Static file serving
            .nest_service("/pkg", ServeDir::new(&leptos_options.site_pkg_dir))
            .nest_service("/assets", ServeDir::new(&leptos_options.site_root))
            // Leptos routes with trait-based rendering
            .leptos_routes(&leptos_options, generate_route_list(move || view! { }), move || view! { })
            // State and middleware
            .with_state(state)
            .layer(
                ServiceBuilder::new()
                    .layer(TraceLayer::new_for_http())
                    .layer(CorsLayer::permissive())
            );

        Ok(router)
    }

    /// Build API routes using trait-based handlers
    fn build_api_routes(&self) -> Router<ServerState<P, C, L, R>> {
        Router::new()
            .route("/health", get(handlers::health))
            .route("/content/:id", get(handlers::get_content::<P, C, L, R>))
            .route("/routes/:lang", get(handlers::get_routes::<P, C, L, R>))
            .route("/localization/:lang/:key", get(handlers::get_localization::<P, C, L, R>))
    }
}

impl<P, C, L, R> Default for ServerBuilder<P, C, L, R>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    fn default() -> Self {
        Self::new()
    }
}

/// Server error types
#[derive(Debug, thiserror::Error)]
pub enum ServerError {
    /// Configuration error
    #[error("Configuration error: {0}")]
    Config(String),

    /// Routing error
    #[error("Routing error: {0}")]
    Routing(#[from] RoutingError),

    /// Component error
    #[error("Component error: {0}")]
    Component(#[from] ComponentError),

    /// IO error
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    /// Axum error
    #[error("Server error: {0}")]
    Axum(#[from] axum::Error),

    /// JSON error
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),
}

impl IntoResponse for ServerError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            ServerError::Config(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
            ServerError::Routing(RoutingError::NotFound(_)) => (axum::http::StatusCode::NOT_FOUND, self.to_string()),
            ServerError::Routing(_) => (axum::http::StatusCode::BAD_REQUEST, self.to_string()),
            ServerError::Component(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
            ServerError::Io(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
            ServerError::Axum(_) => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
            ServerError::Json(_) => (axum::http::StatusCode::BAD_REQUEST, self.to_string()),
        };

        let body = serde_json::json!({
            "error": error_message
        });

        (status, JsonResponse(body)).into_response()
    }
}

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

    // Mock implementations for testing
    struct MockPageProvider;
    struct MockContentProvider;
    struct MockLocalizationProvider;
    struct MockRouteResolver;

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

        fn get_component(&self, _component_id: &str) -> ComponentResult<Self::Component> {
            Ok("mock_component".to_string())
        }

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

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

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

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

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

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

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

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

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

    impl RouteResolver for MockRouteResolver {
        type Component = String;
        type Parameters = HashMap<String, String>;

        fn resolve_route(&self, _path: &str) -> RoutingResult<RouteResolution<Self::Component, Self::Parameters>> {
            Ok(RouteResolution {
                component: Some("mock_component".to_string()),
                path: "/mock".to_string(),
                language: "en".to_string(),
                canonical_path: "/mock".to_string(),
                parameters: HashMap::new(),
                is_language_specific: false,
            })
        }

        fn get_routes_for_language(&self, _language: &str) -> RoutingResult<Vec<String>> {
            Ok(vec!["/mock".to_string()])
        }
    }

    fn create_mock_site_config() -> SiteConfig {
        SiteConfig {
            name: "Test Site".to_string(),
            description: "Test".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(),
        }
    }

    #[test]
    fn test_server_state_creation() {
        let page_provider = MockPageProvider;
        let content_provider = MockContentProvider;
        let localization_provider = MockLocalizationProvider;
        let route_resolver = MockRouteResolver;
        let site_config = create_mock_site_config();
        let framework_config = FrameworkConfig::default();

        let state = ServerState::new(
            page_provider,
            content_provider,
            localization_provider,
            route_resolver,
            site_config,
            framework_config,
        );

        let app_context = state.app_context();
        assert_eq!(app_context.default_language(), "en");
    }

    #[test]
    fn test_server_builder() {
        let page_provider = MockPageProvider;
        let content_provider = MockContentProvider;
        let localization_provider = MockLocalizationProvider;
        let route_resolver = MockRouteResolver;
        let site_config = create_mock_site_config();
        let framework_config = FrameworkConfig::default();

        let state = ServerState::new(
            page_provider,
            content_provider,
            localization_provider,
            route_resolver,
            site_config,
            framework_config,
        );

        let builder = ServerBuilder::new().state(state);
        // Cannot test build_router without proper Leptos setup
        assert!(builder.state.is_some());
    }
}