#![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]

//! REST API server for syntaxis management
//!
//! Configuration is passed via `--config <PATH>` command line argument.
//!
//! Usage:
//!   syntaxis-api --config /path/to/config.toml
//!
//! All configuration comes from the TOML file:
//! - host - Server host to bind to
//! - port - Server port
//! - database_path - Path to SQLite database file
//! - cors_enabled - Enable CORS (true/false)
//! - log_level - Logging level (trace/debug/info/warn/error)
//!
//! See `config.rs` for all available configuration options.

mod api;
mod auth;
mod config;
mod error;
// Note: extractors currently use middleware pattern instead (see middleware/auth.rs)
// mod extractors;
mod graphql;
mod graphql_handler;
mod handlers;
mod jwt;
mod metrics;
mod middleware;
mod models;
mod openapi;
mod persistence;
mod rate_limit;
mod recurring;
mod state;
mod websocket;

use axum::{
    routing::{delete, get, post, put},
    Router,
};
use clap::Parser;
use std::path::PathBuf;
use std::sync::Arc;
use syntaxis_core::persistence::SqliteDatabase;
use tower_http::cors::CorsLayer;
use tower_http::services::{ServeDir, ServeFile};
use tower_http::trace::TraceLayer;

use crate::state::AppState;

/// CLI arguments for syntaxis-api
#[derive(Parser, Debug)]
#[command(name = "syntaxis-api")]
#[command(about = "REST API server for syntaxis management", long_about = None)]
struct Args {
    /// Path to configuration file (TOML format)
    #[arg(short, long)]
    config: PathBuf,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse command line arguments
    let args = Args::parse();

    // Load configuration from file
    let cfg = config::ServerConfig::load(&args.config)?;

    // Validate TLS configuration if specified
    cfg.validate_tls()?;

    // Initialize tracing with configured log level
    let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
        .or_else(|_| tracing_subscriber::EnvFilter::try_new(&cfg.log_level))?;

    tracing_subscriber::fmt().with_env_filter(env_filter).init();

    cfg.log_config();

    // Initialize server uptime tracking
    api::init_uptime();

    // Ensure database directory exists
    cfg.ensure_database_dir()?;

    // Initialize database with configured path
    // Note: The server should be run from the project root for relative paths to work correctly
    let db = SqliteDatabase::new(&cfg.database_path)
        .await
        .map_err(|e| format!("Failed to initialize database: {}", e))?;

    // Initialize metrics if enabled
    let metrics_collector = if cfg.features.metrics.enabled {
        // Load metrics configuration
        let pwd = std::env::var("PWD").unwrap_or_else(|_| ".".to_string());
        let config_dir = std::path::Path::new(&pwd);
        let metrics_config_path = config_dir.join("configs/features/metrics.toml");

        if !metrics_config_path.exists() {
            return Err(format!(
                "Metrics feature enabled but config file not found: {}",
                metrics_config_path.display()
            )
            .into());
        }

        let metrics_config_content = std::fs::read_to_string(&metrics_config_path)
            .map_err(|e| format!("Failed to read metrics config: {}", e))?;

        let metrics_config: config::MetricsConfig = toml::from_str(&metrics_config_content)
            .map_err(|e| format!("Failed to parse metrics config: {}", e))?;

        let collector = metrics::MetricsCollector::new(&metrics_config.request_duration_buckets)
            .map_err(|e| format!("Failed to initialize metrics: {}", e))?;

        tracing::info!(
            "Metrics feature enabled at path: {}",
            metrics_config.metrics_path
        );
        Arc::new(collector)
    } else {
        // Create empty metrics collector if not enabled
        let collector =
            metrics::MetricsCollector::new(&[1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0])
                .map_err(|e| format!("Failed to initialize metrics: {}", e))?;
        Arc::new(collector)
    };

    // Initialize rate limiter if enabled
    let rate_limiter = if cfg.features.rate_limit.enabled {
        // Load rate limit configuration
        let pwd = std::env::var("PWD").unwrap_or_else(|_| ".".to_string());
        let config_dir = std::path::Path::new(&pwd);
        let rate_limit_config_path = config_dir.join("configs/features/rate_limit.toml");

        if !rate_limit_config_path.exists() {
            return Err(format!(
                "Rate limit feature enabled but config file not found: {}",
                rate_limit_config_path.display()
            )
            .into());
        }

        let rate_limit_config_content = std::fs::read_to_string(&rate_limit_config_path)
            .map_err(|e| format!("Failed to read rate limit config: {}", e))?;

        let rate_limit_config: config::RateLimitConfig = toml::from_str(&rate_limit_config_content)
            .map_err(|e| format!("Failed to parse rate limit config: {}", e))?;

        let limiter = rate_limit::RateLimiter::new(
            rate_limit_config.requests_per_second,
            rate_limit_config.burst_size,
        );

        tracing::info!(
            "Rate limit feature enabled: {} req/sec, {} burst size",
            rate_limit_config.requests_per_second,
            rate_limit_config.burst_size
        );
        Arc::new(limiter)
    } else {
        // Create default rate limiter (100 req/sec, 200 burst)
        Arc::new(rate_limit::RateLimiter::new(100.0, 200.0))
    };

    // Initialize authentication if enabled
    let (auth_provider, jwt_provider) = if cfg.features.auth.enabled {
        // Load auth configuration
        let pwd = std::env::var("PWD").unwrap_or_else(|_| ".".to_string());
        let config_dir = std::path::Path::new(&pwd);
        let auth_config_path = config_dir.join("configs/features/auth.toml");

        if !auth_config_path.exists() {
            return Err(format!(
                "Auth feature enabled but config file not found: {}",
                auth_config_path.display()
            )
            .into());
        }

        let auth_config_content = std::fs::read_to_string(&auth_config_path)
            .map_err(|e| format!("Failed to read auth config: {}", e))?;

        let auth_config: config::AuthConfig = toml::from_str(&auth_config_content)
            .map_err(|e| format!("Failed to parse auth config: {}", e))?;

        let provider = auth::AuthProvider::with_config(
            auth_config.auth_header,
            auth_config.auth_prefix,
            auth_config.strict_validation,
        );

        // Add all configured API keys
        for key_str in auth_config.api_keys {
            let key = auth::ApiKey::new(key_str);
            provider
                .add_key(key)
                .map_err(|e| format!("Failed to add API key: {}", e))?;
        }

        tracing::info!(
            "Auth feature enabled with {} API keys",
            provider.key_count()
        );

        // Initialize JWT provider if enabled
        let jwt_prov = if let Some(secret) = auth_config.jwt_secret.as_ref().filter(|_| auth_config.jwt_enabled) {
            let expiration = auth_config.jwt_expiration as i64;

            match shared_api_lib::auth::jwt::JwtProvider::new(secret, expiration) {
                Ok(provider) => {
                    tracing::info!(
                        "JWT provider initialized with {} second expiration",
                        expiration
                    );
                    Some(Arc::new(provider))
                }
                Err(e) => {
                    return Err(format!("Failed to initialize JWT provider: {}", e).into());
                }
            }
        } else {
            if auth_config.jwt_enabled {
                tracing::warn!(
                    "JWT enabled but jwt_secret not configured. JWT authentication disabled."
                );
            }
            None
        };

        (Arc::new(provider), jwt_prov)
    } else {
        // Create empty auth provider if not enabled
        (Arc::new(auth::AuthProvider::new()), None)
    };

    // Initialize event broadcaster for WebSocket events
    let event_broadcaster = websocket::EventBroadcaster::new(100);
    tracing::info!("WebSocket event broadcaster initialized with capacity 100");

    // Create application state
    let db_arc = Arc::new(db);
    let jwt_provider_enabled = jwt_provider.is_some();
    let state = AppState {
        db: db_arc.clone(),
        metrics: metrics_collector,
        rate_limiter,
        auth: auth_provider,
        jwt_provider,
        events: Arc::new(event_broadcaster),
    };

    // Spawn recurring tasks worker
    let db_for_worker = db_arc.clone();
    tokio::spawn(async move {
        recurring::recurring_tasks_worker(db_for_worker).await;
    });
    tracing::info!("Recurring tasks worker started");

    // Get public files path for static file serving
    let public_files_path = cfg.public_files_path_buf();
    let public_files_exist = public_files_path.exists();

    if public_files_exist {
        tracing::info!("Static files directory: {}", public_files_path.display());
    } else {
        tracing::warn!(
            "Static files directory does not exist: {}. Skipping /public and /docs endpoints.",
            public_files_path.display()
        );
    }

    // Build router with public and protected routes
    // Public routes (no authentication required)
    let public_routes = Router::new()
        // Health check
        .route("/api/health", get(api::health))
        // OpenAPI documentation
        .route("/api/docs/openapi.json", get(api::openapi_spec))
        // Authentication endpoints
        .route("/api/auth/register", post(handlers::auth::register))
        .route("/api/auth/login", post(handlers::auth::login))
        // GraphQL endpoint (public for now, can be protected later)
        .route("/graphql", post(graphql_handler::graphql_handler))
        // Metrics (public for now, can be protected later)
        .route("/metrics", get(metrics::metrics_handler));

    // Protected routes (require JWT authentication)
    let protected_routes = Router::new()
        // Authentication endpoints (protected)
        .route("/api/auth/logout", post(handlers::auth::logout))
        .route("/api/auth/me", get(handlers::auth::current_user))
        // Projects
        .route("/api/projects", post(handlers::create_project))
        .route("/api/projects", get(handlers::list_projects))
        .route("/api/projects/{id}", get(handlers::get_project))
        .route("/api/projects/{id}", put(handlers::update_project))
        .route("/api/projects/{id}", delete(handlers::delete_project))
        // Phases
        .route(
            "/api/projects/{id}/phases",
            get(handlers::get_current_phase),
        )
        .route(
            "/api/projects/{id}/phases/transition",
            post(handlers::transition_phase),
        )
        .route(
            "/api/projects/{id}/phases/history",
            get(handlers::get_phase_history),
        )
        // Checklists
        .route(
            "/api/projects/{id}/checklists",
            get(handlers::get_checklists),
        )
        .route(
            "/api/projects/{id}/checklists/phases/{phase}",
            get(handlers::get_checklist_by_phase),
        )
        .route(
            "/api/projects/{id}/checklists",
            post(handlers::add_checklist_item),
        )
        .route(
            "/api/projects/{id}/checklists/items/{item_id}",
            put(handlers::update_checklist_item),
        )
        // Security
        .route(
            "/api/projects/{id}/security",
            get(handlers::get_security_assessment),
        )
        .route(
            "/api/projects/{id}/security/assess",
            post(handlers::run_security_assessment),
        )
        // Status
        .route(
            "/api/projects/{id}/status",
            get(handlers::get_project_status),
        )
        // Tools
        .route("/api/projects/{id}/tools", get(handlers::list_tools))
        .route(
            "/api/projects/{id}/tools/{name}/enable",
            post(handlers::enable_tool),
        )
        .route(
            "/api/projects/{id}/tools/{name}/disable",
            post(handlers::disable_tool),
        )
        // WebSocket
        .route("/ws", get(websocket::websocket_handler))
        // Phase 2: Enhanced Task Operations
        .route(
            "/api/projects/{id}/tasks/{task_id}",
            put(handlers::tasks_enhanced::update_task),
        )
        .route(
            "/api/projects/{id}/tasks/{task_id}",
            delete(handlers::tasks_enhanced::delete_task),
        )
        .route(
            "/api/projects/{id}/tasks/{task_id}/assign",
            put(handlers::tasks_enhanced::assign_task),
        )
        .route(
            "/api/projects/{id}/tasks/{task_id}/deadline",
            put(handlers::tasks_enhanced::set_deadline),
        )
        // Phase 2: Task Comments
        .route(
            "/api/projects/{id}/tasks/{task_id}/comments",
            get(handlers::comments::get_comments),
        )
        .route(
            "/api/projects/{id}/tasks/{task_id}/comments",
            post(handlers::comments::add_comment),
        )
        // Phase 2: Task Attachments
        .route(
            "/api/projects/{id}/tasks/{task_id}/attachments",
            get(handlers::attachments::get_attachments),
        )
        .route(
            "/api/projects/{id}/tasks/{task_id}/attachments",
            post(handlers::attachments::upload_attachment),
        )
        .route(
            "/api/projects/{id}/tasks/{task_id}/attachments/{attachment_id}",
            delete(handlers::attachments::delete_attachment),
        )
        // Phase 2: Filter Presets
        .route(
            "/api/users/{user_id}/presets",
            get(handlers::presets::list_presets),
        )
        .route("/api/presets", post(handlers::presets::create_preset))
        .route(
            "/api/users/{user_id}/presets/{preset_id}",
            get(handlers::presets::get_preset),
        )
        .route(
            "/api/users/{user_id}/presets/{preset_id}",
            delete(handlers::presets::delete_preset),
        )
        // Phase 5: Task Dependencies
        .route(
            "/api/tasks/{id}/dependencies",
            post(handlers::dependencies::add_dependency),
        )
        .route(
            "/api/tasks/{id}/dependencies",
            get(handlers::dependencies::get_dependencies),
        )
        .route(
            "/api/tasks/{task_id}/dependencies/{dep_id}",
            delete(handlers::dependencies::delete_dependency),
        )
        // Phase 5: Milestones
        .route(
            "/api/projects/{id}/milestones",
            get(handlers::milestones::list_milestones),
        )
        .route(
            "/api/projects/{id}/milestones",
            post(handlers::milestones::create_milestone),
        )
        .route(
            "/api/milestones/{id}",
            put(handlers::milestones::update_milestone),
        )
        .route(
            "/api/milestones/{id}/complete",
            post(handlers::milestones::complete_milestone),
        )
        // Phase 5: Analytics
        .route(
            "/api/projects/{id}/burndown",
            get(handlers::analytics::burndown_data),
        )
        .route(
            "/api/projects/{id}/progress",
            get(handlers::analytics::progress_stats),
        )
        .route(
            "/api/projects/{id}/velocity",
            get(handlers::analytics::velocity_data),
        )
        // Phase 5: Team Assignment
        .route(
            "/api/tasks/{id}/assign",
            put(handlers::assignments::assign_task),
        )
        .route(
            "/api/tasks/{id}/unassign",
            delete(handlers::assignments::unassign_task),
        )
        .route(
            "/api/projects/{id}/assignments",
            get(handlers::assignments::get_task_assignments),
        )
        .route("/api/users", post(handlers::assignments::create_user))
        .route("/api/users", get(handlers::assignments::list_users))
        // Phase 5: Subtasks
        .route(
            "/api/tasks/{id}/subtasks",
            get(handlers::subtasks::get_subtasks),
        )
        .route(
            "/api/tasks/{id}/subtasks",
            post(handlers::subtasks::create_subtask),
        )
        .route(
            "/api/tasks/{parent_id}/subtasks/{subtask_id}",
            delete(handlers::subtasks::delete_subtask),
        )
        .route(
            "/api/tasks/{id}/hierarchy",
            get(handlers::subtasks::get_task_hierarchy),
        );

    // Apply authentication middleware to protected routes if JWT is enabled
    let protected_routes_with_middleware = if jwt_provider_enabled {
        protected_routes.layer(axum::middleware::from_fn_with_state(
            state.clone(),
            middleware::auth::auth_middleware,
        ))
    } else {
        // If JWT is not enabled, protected routes still require auth (but via API keys or other means)
        protected_routes
    };

    // Merge public and protected routes
    let mut router = public_routes
        .merge(protected_routes_with_middleware)
        .layer(TraceLayer::new_for_http())
        .with_state(state);

    // Only add static file routes if the directory exists
    if public_files_exist {
        router = router
            .nest_service("/public", ServeDir::new(public_files_path.clone()))
            .nest_service("/docs", ServeDir::new(public_files_path));
    }

    // Serve dashboard (Leptos WASM frontend) from target/site
    // Built by build-dashboard.nu script (or trunk build command)
    let dashboard_dir = std::path::PathBuf::from("target/site");
    let dashboard_index = dashboard_dir.join("index.html");

    if dashboard_dir.exists() && dashboard_index.exists() {
        tracing::info!("Dashboard frontend enabled at root path");

        // Serve WASM artifacts at /pkg/*
        router = router.nest_service("/pkg", ServeDir::new(dashboard_dir.join("pkg")));

        // Serve CSS at /styles/*
        if dashboard_dir.join("styles").exists() {
            router = router.nest_service("/styles", ServeDir::new(dashboard_dir.join("styles")));
        }

        // SPA fallback: all unmatched routes serve index.html for client-side routing
        router = router.fallback_service(
            ServeDir::new(dashboard_dir.clone()).not_found_service(ServeFile::new(dashboard_index)),
        );
    } else {
        tracing::warn!(
            "Dashboard frontend not found. Build with: 'cd core && trunk build --release' or 'nu scripts/core/build-dashboard.nu build'"
        );
    }

    // Apply CORS layer if enabled in configuration
    if cfg.cors_enabled {
        router = router.layer(CorsLayer::permissive());
        tracing::debug!("CORS enabled");
    } else {
        tracing::debug!("CORS disabled");
    }

    let app = router;

    let socket_addr = cfg.socket_addr()?;
    let listener = tokio::net::TcpListener::bind(socket_addr)
        .await
        .map_err(|e| format!("Failed to bind to {}: {}", socket_addr, e))?;

    tracing::info!("Server listening on {}", listener.local_addr()?);

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

    Ok(())
}
