//! Request handlers using trait-based dependency injection

use crate::*;
use axum::{
    extract::{State, Path, Query},
    response::Json,
    http::StatusCode,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Health check endpoint
pub async fn health() -> Result<Json<HealthResponse>, ServerError> {
    Ok(Json(HealthResponse {
        status: "ok".to_string(),
        timestamp: chrono::Utc::now().to_rfc3339(),
    }))
}

/// Get content by ID using trait-based content provider
pub async fn get_content<P, C, L, R>(
    State(state): State<ServerState<P, C, L, R>>,
    Path(content_id): Path<String>,
    Query(params): Query<ContentParams>,
) -> Result<Json<ContentResponse>, ServerError>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    let language = params.lang.unwrap_or_else(|| state.site_config.default_language.clone());

    match state.content_provider.get_content(&content_id, &language) {
        Ok(content) => Ok(Json(ContentResponse {
            id: content_id,
            content: format!("{:?}", content), // Implementation should provide proper serialization
            language,
        })),
        Err(ComponentError::NotFound(_)) => Err(ServerError::Routing(RoutingError::NotFound(content_id))),
        Err(e) => Err(ServerError::Component(e)),
    }
}

/// Get available routes for a language using trait-based route resolver
pub async fn get_routes<P, C, L, R>(
    State(state): State<ServerState<P, C, L, R>>,
    Path(language): Path<String>,
) -> Result<Json<RoutesResponse>, ServerError>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    match state.route_resolver.get_routes_for_language(&language) {
        Ok(routes) => Ok(Json(RoutesResponse {
            language,
            routes,
        })),
        Err(e) => Err(ServerError::Routing(e)),
    }
}

/// Get localized string using trait-based localization provider
pub async fn get_localization<P, C, L, R>(
    State(state): State<ServerState<P, C, L, R>>,
    Path((language, key)): Path<(String, String)>,
    Query(params): Query<LocalizationParams>,
) -> Result<Json<LocalizationResponse>, ServerError>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    let args = params.args.unwrap_or_default();

    match state.localization_provider.get_localized_string(&key, &language, &args) {
        Ok(text) => Ok(Json(LocalizationResponse {
            key,
            language,
            text,
            args,
        })),
        Err(e) => Err(ServerError::Component(e)),
    }
}

/// Render page using trait-based page provider
pub async fn render_page<P, C, L, R>(
    State(state): State<ServerState<P, C, L, R>>,
    Path(path): Path<String>,
    Query(params): Query<RenderParams>,
) -> Result<Html<String>, ServerError>
where
    P: PageProvider + Send + Sync + 'static,
    C: ContentProvider + Send + Sync + 'static,
    L: LocalizationProvider + Send + Sync + 'static,
    R: RouteResolver + Send + Sync + 'static,
{
    let request_path = format!("/{}", path.trim_start_matches('/'));

    // Resolve route using trait-based resolver
    let resolution = state.route_resolver.resolve_route(&request_path)?;

    if let Some(component) = resolution.component {
        // Render component using trait-based provider
        let view = state.page_provider.render_component(&component, (), &resolution.language)?;
        Ok(Html(format!("{:?}", view))) // Implementation should provide proper HTML serialization
    } else {
        Err(ServerError::Routing(RoutingError::NotFound(request_path)))
    }
}

// Request/Response types
#[derive(Serialize)]
pub struct HealthResponse {
    pub status: String,
    pub timestamp: String,
}

#[derive(Deserialize)]
pub struct ContentParams {
    pub lang: Option<String>,
}

#[derive(Serialize)]
pub struct ContentResponse {
    pub id: String,
    pub content: String, // Implementation should use proper content type
    pub language: String,
}

#[derive(Serialize)]
pub struct RoutesResponse {
    pub language: String,
    pub routes: Vec<String>,
}

#[derive(Deserialize)]
pub struct LocalizationParams {
    #[serde(default)]
    pub args: HashMap<String, String>,
}

#[derive(Serialize)]
pub struct LocalizationResponse {
    pub key: String,
    pub language: String,
    pub text: String,
    pub args: HashMap<String, String>,
}

#[derive(Deserialize)]
pub struct RenderParams {
    pub lang: Option<String>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum_test::TestServer;
    use std::sync::Arc;

    // 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("<h1>Mock View</h1>".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> {
            if content_id == "test" {
                Ok("Mock content".to_string())
            } else {
                Err(ComponentError::NotFound(content_id.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!["test".to_string()])
        }
    }

    impl LocalizationProvider for MockLocalizationProvider {
        fn get_localized_string(
            &self,
            key: &str,
            _language: &str,
            _args: &HashMap<String, String>,
        ) -> ComponentResult<String> {
            match key {
                "welcome" => Ok("Welcome".to_string()),
                _ => Err(ComponentError::ContentError(format!("Key not found: {}", key))),
            }
        }

        fn available_languages(&self) -> Vec<String> {
            vec!["en".to_string(), "es".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>> {
            match path {
                "/test" => Ok(RouteResolution {
                    component: Some("test_component".to_string()),
                    path: "/test".to_string(),
                    language: "en".to_string(),
                    canonical_path: "/test".to_string(),
                    parameters: HashMap::new(),
                    is_language_specific: false,
                }),
                _ => Ok(RouteResolution {
                    component: None,
                    path: path.to_string(),
                    language: "en".to_string(),
                    canonical_path: path.to_string(),
                    parameters: HashMap::new(),
                    is_language_specific: false,
                }),
            }
        }

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

    fn create_test_state() -> ServerState<MockPageProvider, MockContentProvider, MockLocalizationProvider, MockRouteResolver> {
        let site_config = 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(),
        };

        ServerState::new(
            MockPageProvider,
            MockContentProvider,
            MockLocalizationProvider,
            MockRouteResolver,
            site_config,
            FrameworkConfig::default(),
        )
    }

    #[tokio::test]
    async fn test_health_endpoint() {
        let response = health().await.unwrap();
        assert_eq!(response.0.status, "ok");
    }

    #[tokio::test]
    async fn test_get_content() {
        let state = create_test_state();
        let path = Path("test".to_string());
        let query = Query(ContentParams { lang: None });

        let response = get_content(State(state), path, query).await.unwrap();
        assert_eq!(response.0.id, "test");
        assert_eq!(response.0.language, "en");
    }

    #[tokio::test]
    async fn test_get_routes() {
        let state = create_test_state();
        let path = Path("en".to_string());

        let response = get_routes(State(state), path).await.unwrap();
        assert_eq!(response.0.language, "en");
        assert!(response.0.routes.contains(&"/test".to_string()));
    }

    #[tokio::test]
    async fn test_get_localization() {
        let state = create_test_state();
        let path = Path(("en".to_string(), "welcome".to_string()));
        let query = Query(LocalizationParams { args: HashMap::new() });

        let response = get_localization(State(state), path, query).await.unwrap();
        assert_eq!(response.0.key, "welcome");
        assert_eq!(response.0.text, "Welcome");
    }
}