//! Córtex API Server

use crate::error::Result;
use crate::rest;
use crate::state::AppState;

use axum::Router;
use std::net::SocketAddr;
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::TraceLayer;
use tracing::info;

/// Server configuration
#[derive(Debug, Clone)]
pub struct CortexConfig {
    /// Host to bind to
    pub host: String,
    /// Port to listen on
    pub port: u16,
    /// Enable CORS
    pub cors_enabled: bool,
    /// Enable GraphQL playground
    pub graphql_playground: bool,
    /// Enable request tracing
    pub tracing: bool,
    /// Enable rate limiting
    pub rate_limit_enabled: bool,
    /// Requests per minute (if rate limiting enabled)
    pub rate_limit_rpm: u32,
}

impl Default for CortexConfig {
    fn default() -> Self {
        Self {
            host: "127.0.0.1".to_string(),
            port: 8080,
            cors_enabled: true,
            graphql_playground: true,
            tracing: true,
            rate_limit_enabled: true,
            rate_limit_rpm: 100, // 100 requests per minute
        }
    }
}

impl CortexConfig {
    /// Create config for all interfaces
    pub fn public() -> Self {
        Self {
            host: "0.0.0.0".to_string(),
            ..Default::default()
        }
    }

    /// Set port
    pub fn with_port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }

    /// Set host
    pub fn with_host(mut self, host: impl Into<String>) -> Self {
        self.host = host.into();
        self
    }
}

/// Córtex API Server
pub struct CortexServer {
    config: CortexConfig,
    state: AppState,
}

impl CortexServer {
    /// Create new server with default state
    pub fn new(config: CortexConfig) -> Result<Self> {
        Ok(Self {
            config,
            state: AppState::new(),
        })
    }

    /// Create with existing state
    pub fn with_state(config: CortexConfig, state: AppState) -> Self {
        Self { config, state }
    }

    /// Get application state
    pub fn state(&self) -> &AppState {
        &self.state
    }

    /// Build the router
    pub fn build_router(&self) -> Router {
        let mut app: Router<AppState> = Router::new();

        // REST API routes
        app = app.merge(rest::router());

        // SPARQL routes
        #[cfg(feature = "sparql")]
        {
            app = app.merge(crate::sparql::router());
        }

        // Auth routes
        #[cfg(feature = "auth")]
        {
            app = app.merge(crate::auth::router());
        }

        // Add state and convert to Router<()>
        let app = app.with_state(self.state.clone());

        // Add middleware layers (in reverse order of execution)

        // Rate limiting (applied first, before other middleware)
        let app = if self.config.rate_limit_enabled {
            use crate::middleware::RateLimiter;
            use axum_client_ip::SecureClientIpSource;

            let rate_limiter = RateLimiter::new(self.config.rate_limit_rpm)
                .with_burst_capacity(self.config.rate_limit_rpm);

            app.layer(rate_limiter.into_layer())
                .layer(SecureClientIpSource::ConnectInfo.into_extension())
        } else {
            app
        };

        // CORS
        let app = if self.config.cors_enabled {
            app.layer(
                CorsLayer::new()
                    .allow_origin(Any)
                    .allow_methods(Any)
                    .allow_headers(Any),
            )
        } else {
            app
        };

        // Tracing
        let app = if self.config.tracing {
            app.layer(TraceLayer::new_for_http())
        } else {
            app
        };

        app
    }

    /// Run the server
    pub async fn run(self) -> Result<()> {
        let addr: SocketAddr = format!("{}:{}", self.config.host, self.config.port)
            .parse()
            .map_err(|e| crate::error::Error::Internal(format!("Invalid address: {}", e)))?;

        let router = self.build_router();

        info!("Starting Córtex API server on http://{}", addr);
        info!("REST API: http://{}/api/v1", addr);
        #[cfg(feature = "graphql")]
        info!("GraphQL: http://{}/graphql", addr);
        #[cfg(feature = "sparql")]
        info!("SPARQL: http://{}/sparql", addr);

        let listener = tokio::net::TcpListener::bind(addr).await?;
        axum::serve(listener, router).await?;

        Ok(())
    }

    /// Run with graceful shutdown
    pub async fn run_with_shutdown<F>(self, shutdown_signal: F) -> Result<()>
    where
        F: std::future::Future<Output = ()> + Send + 'static,
    {
        let addr: SocketAddr = format!("{}:{}", self.config.host, self.config.port)
            .parse()
            .map_err(|e| crate::error::Error::Internal(format!("Invalid address: {}", e)))?;

        let router = self.build_router();

        info!("Starting Córtex API server on http://{}", addr);

        let listener = tokio::net::TcpListener::bind(addr).await?;
        axum::serve(listener, router)
            .with_graceful_shutdown(shutdown_signal)
            .await?;

        info!("Córtex API server stopped");
        Ok(())
    }
}

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

    #[test]
    fn test_config_default() {
        let config = CortexConfig::default();
        assert_eq!(config.host, "127.0.0.1");
        assert_eq!(config.port, 8080);
        assert!(config.cors_enabled);
    }

    #[test]
    fn test_config_public() {
        let config = CortexConfig::public();
        assert_eq!(config.host, "0.0.0.0");
    }

    #[test]
    fn test_config_builder() {
        let config = CortexConfig::default()
            .with_host("localhost")
            .with_port(9090);
        assert_eq!(config.host, "localhost");
        assert_eq!(config.port, 9090);
    }
}
