//! Server setup and lifecycle management

use anyhow::Result;
use axum::{middleware, Router};
use std::sync::Arc;
use tokio::net::TcpListener;
use tower::ServiceBuilder;
use tower_http::trace::TraceLayer;
use tracing::info;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;

use crate::{
    middleware::{create_cors_layer, log_request},
    openapi::ApiDoc,
    routes,
    state::AppState,
};

/// Server configuration and lifecycle manager
pub struct Server {
    /// Application state
    state: Arc<AppState>,
    /// Server host address
    host: String,
    /// Server port
    port: u16,
}

impl Server {
    /// Create a new server instance
    pub fn new(state: Arc<AppState>) -> Self {
        let host = state.config.server.host.clone();
        let port = state.config.server.port;

        Self { state, host, port }
    }

    /// Get the router for testing purposes
    pub fn router(&self) -> Router {
        self.build_router()
    }

    /// Build the application router with all routes and middleware
    fn build_router(&self) -> Router {
        // API v1 routes (versioned endpoints)
        let api_v1 = Router::new()
            .merge(routes::pack_routes())
            .merge(routes::action_routes())
            .merge(routes::runtime_routes())
            .merge(routes::rule_routes())
            .merge(routes::execution_routes())
            .merge(routes::trigger_routes())
            .merge(routes::inquiry_routes())
            .merge(routes::event_routes())
            .merge(routes::key_routes())
            .merge(routes::permission_routes())
            .merge(routes::workflow_routes())
            .merge(routes::webhook_routes())
            .merge(routes::history_routes())
            .merge(routes::analytics_routes())
            .merge(routes::artifact_routes())
            .merge(routes::agent_routes())
            .with_state(self.state.clone());

        // Auth routes at root level (not versioned for frontend compatibility)
        let auth_routes = routes::auth_routes().with_state(self.state.clone());

        // Health endpoint at root level (operational endpoint, not versioned)
        let health_routes = routes::health_routes().with_state(self.state.clone());

        // Root router with versioning and documentation
        Router::new()
            .merge(SwaggerUi::new("/docs").url("/api-spec/openapi.json", ApiDoc::openapi()))
            .merge(health_routes)
            .nest("/auth", auth_routes)
            .nest("/api/v1", api_v1)
            .layer(
                ServiceBuilder::new()
                    // Add tracing for all requests
                    .layer(TraceLayer::new_for_http())
                    // Add CORS support with configured origins
                    .layer(create_cors_layer(self.state.cors_origins.clone()))
                    // Add custom request logging
                    .layer(middleware::from_fn(log_request)),
            )
    }

    /// Start the server and listen for requests
    pub async fn run(self) -> Result<()> {
        let router = self.build_router();
        let addr = format!("{}:{}", self.host, self.port);

        info!("Starting server on {}", addr);
        info!("API documentation available at http://{}/docs", addr);

        let listener = TcpListener::bind(&addr).await?;
        info!("Server listening on {}", addr);

        axum::serve(listener, router).await?;

        Ok(())
    }

    /// Graceful shutdown handler
    pub async fn shutdown(&self) {
        info!("Shutting down server...");
        // Perform any cleanup here
        // - Close database connections
        // - Flush logs
        // - Wait for in-flight requests
        info!("Server shutdown complete");
    }
}

#[cfg(test)]
mod tests {
    #[tokio::test]
    #[ignore] // Ignore until we have test database setup
    async fn test_server_creation() {
        // This test is ignored because it requires a test database pool
        // When implemented, create a test pool and verify server creation
        // let pool = PgPool::connect(&test_db_url).await.unwrap();
        // let state = AppState::new(pool);
        // let server = Server::new(state, "127.0.0.1".to_string(), 8080);
        // assert_eq!(server.host, "127.0.0.1");
        // assert_eq!(server.port, 8080);
    }
}
