mod db_util;
mod handlers;
mod handlers_announcements;
mod handlers_cohorts;
mod handlers_discussions;
mod handlers_notes;
mod handlers_payments;
mod handlers_peer_review;
mod handlers_embeddings;
mod lti;
mod jwks;
mod predictive;
mod live;
mod portfolio;
mod external_db;
mod openapi;

use axum::{
    Router, middleware,
    routing::{delete, get, post, put},
    response::Html,
    http::{Method, header},
};
use common::health::{self, HealthState};
use dotenvy::dotenv;
use sqlx::postgres::PgPoolOptions;
use std::env;
use std::net::SocketAddr;
use std::time::Duration;
use tower_http::cors::CorsLayer;
use tower_http::set_header::SetResponseHeaderLayer;
use utoipa::OpenApi;

#[tokio::main]
async fn main() {
    dotenv().ok();
    tracing_subscriber::fmt::init();

    let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let pool = PgPoolOptions::new()
        .max_connections(10)
        .min_connections(2)
        .acquire_timeout(Duration::from_secs(30))
        .connect(&db_url)
        .await
        .expect("Failed to connect to database");

    // Initialize health state
    let health_state = HealthState::default();

    let mysql_pool = external_db::init_mysql_pool().await;

    // Run migrations automatically
    sqlx::migrate!("./migrations")
        .run(&pool)
        .await
        .expect("Failed to run migrations");

    // Start background task for deadline notifications
    let pool_clone = pool.clone();
    tokio::spawn(async move {
        loop {
            handlers::check_deadlines_and_notify(pool_clone.clone()).await;
            tokio::time::sleep(tokio::time::Duration::from_secs(3600)).await; // Every hour
        }
    });

    // CORS configuration - Allow multiple origins for development and production
    // Using a predicate closure to support wildcard subdomains for norteamericano.cl
    use tower_http::cors::AllowOrigin;
    
    let cors = CorsLayer::new()
        .allow_origin(AllowOrigin::predicate(|origin: &http::HeaderValue, _request: &http::request::Parts| -> bool {
            let origin_str = origin.to_str().unwrap_or("");
            
            // Development origins
            let allowed_origins = [
                "http://localhost:3000",
                "http://localhost:3003",
                "http://127.0.0.1:3000",
                "http://127.0.0.1:3003",
                "http://192.168.0.254:3000",
                "http://192.168.0.254:3003",
                "http://192.168.0.254",
                // Production - Norteamericano domains (HTTPS)
                "https://studio.norteamericano.cl",
                "https://learning.norteamericano.cl",
            ];
            
            // Check exact matches
            if allowed_origins.contains(&origin_str) {
                return true;
            }
            
            // Check wildcard for subdomains: https://*.norteamericano.cl
            if origin_str.starts_with("https://") && origin_str.ends_with(".norteamericano.cl") {
                let subdomain = origin_str
                    .strip_prefix("https://")
                    .unwrap_or("")
                    .strip_suffix(".norteamericano.cl")
                    .unwrap_or("");
                
                // Allow any subdomain (e.g., api., cdn., admin., etc.)
                if !subdomain.is_empty() && !subdomain.contains('/') {
                    return true;
                }
            }
            
            false
        }))
        .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE, Method::OPTIONS, Method::PATCH])
        .allow_headers([
            header::CONTENT_TYPE,
            header::AUTHORIZATION,
            header::HeaderName::from_static("x-requested-with"),
            header::HeaderName::from_static("x-organization-id"),
        ])
        .expose_headers([header::CONTENT_LENGTH, header::CONTENT_TYPE]);

    // Rate limiter DESHABILITADO debido a problemas de compatibilidad con el middleware de autenticación
    // Ver QWEN.md para más detalles
    // let governor_conf = Arc::new(
    //     GovernorConfigBuilder::default()
    //         .per_second(10)
    //         .burst_size(50)
    //         .finish()
    //         .unwrap(),
    // );

    // Rate limiter solo para rutas protegidas (después del middleware de autenticación)
    let protected_routes = Router::new()
        .route("/auth/me", get(handlers::get_me))
        .route("/courses/{id}/language-config", get(handlers::get_course_language_config))
        .route("/enroll", post(handlers::enroll_user))
        .route("/bulk-enroll", post(handlers::bulk_enroll_users))
        .route("/enrollments/{id}", get(handlers::get_user_enrollments))
        .route(
            "/payments/preference",
            post(handlers_payments::create_payment_preference),
        )
        .route("/courses/{id}/outline", get(handlers::get_course_outline))
        .route("/courses/{id}/progress-stats", get(handlers::get_student_progress_stats))
        .route("/lessons/{id}", get(handlers::get_lesson_content))
        .route("/lessons/{id}/bookmark", post(handlers::toggle_bookmark))
        .route("/bookmarks", get(handlers::get_user_bookmarks))
        .route("/grades", post(handlers::submit_lesson_score))
        .route(
            "/users/{user_id}/courses/{course_id}/grades",
            get(handlers::get_user_course_grades),
        )
        .route(
            "/courses/{id}/analytics",
            get(handlers::get_course_analytics),
        )
        .route("/courses/{id}/grades", get(handlers::get_course_grades))
        .route(
            "/courses/{id}/export-grades",
            get(handlers::export_course_grades),
        )
        .route(
            "/courses/{id}/analytics/advanced",
            get(handlers::get_advanced_analytics),
        )
        .route(
            "/courses/{id}/recommendations",
            get(handlers::get_recommendations),
        )
        .route(
            "/courses/{id}/dropout-risks",
            get(predictive::get_course_dropout_risks),
        )
        // Live Learning
        .route("/courses/{id}/meetings", get(live::get_course_meetings).post(live::create_meeting))
        .route("/courses/{id}/meetings/{meeting_id}", delete(live::delete_meeting))
        // Portfolio & Badges
        .route("/profile/{user_id}", get(portfolio::get_public_profile))
        .route("/my/badges", get(portfolio::get_my_badges))
        .route("/badges/award", post(portfolio::award_badge))
        .route(
            "/users/{id}/gamification",
            get(handlers::get_user_gamification),
        )
        .route("/users/{id}", post(handlers::update_user))
        .route("/analytics/leaderboard", get(handlers::get_leaderboard))
        .route(
            "/lessons/{id}/interactions",
            post(handlers::record_interaction),
        )
        .route("/lessons/{id}/heatmap", get(handlers::get_lesson_heatmap))
        .route("/audio/evaluate", post(handlers::evaluate_audio_response))
        .route("/audio/evaluate-file", post(handlers::evaluate_audio_file))
        // Audio Response Teacher Routes
        .route("/audio-responses", get(handlers::get_audio_responses))
        .route("/audio-responses/{id}", get(handlers::get_audio_response_detail))
        .route("/audio-responses/{id}/audio", get(handlers::get_audio_response_audio))
        .route("/audio-responses/{id}/evaluate", post(handlers::teacher_evaluate_audio))
        .route("/courses/{id}/audio-responses/stats", get(handlers::get_audio_response_stats))
        .route("/lessons/{id}/chat", post(handlers::chat_with_tutor))
        .route("/lessons/{id}/chat-role-play", post(handlers::chat_role_play))
        .route("/lessons/{id}/code-hint", post(handlers::get_code_hint))
        .route("/lessons/{id}/feedback", get(handlers::get_lesson_feedback))
        .route("/notifications", get(handlers::get_notifications))
        .route(
            "/notifications/{id}/read",
            post(handlers::mark_notification_as_read),
        )
        // Knowledge Base Embedding Routes for Semantic RAG
        .route(
            "/knowledge-base/embeddings/generate",
            post(handlers_embeddings::generate_knowledge_embeddings),
        )
        .route(
            "/knowledge-base/semantic-search",
            get(handlers_embeddings::semantic_search_knowledge),
        )
        .route(
            "/knowledge-base/{id}/embedding/regenerate",
            post(handlers_embeddings::regenerate_knowledge_embedding),
        )
        // Discussion Forums Routes
        .route(
            "/courses/{id}/discussions",
            get(handlers_discussions::list_threads),
        )
        .route(
            "/courses/{id}/discussions",
            post(handlers_discussions::create_thread),
        )
        .route(
            "/discussions/{id}",
            get(handlers_discussions::get_thread_detail),
        )
        .route(
            "/discussions/{id}/pin",
            post(handlers_discussions::pin_thread),
        )
        .route(
            "/discussions/{id}/lock",
            post(handlers_discussions::lock_thread),
        )
        .route(
            "/discussions/{id}/posts",
            post(handlers_discussions::create_post),
        )
        .route(
            "/posts/{id}/endorse",
            post(handlers_discussions::endorse_post),
        )
        .route("/posts/{id}/vote", post(handlers_discussions::vote_post))
        .route(
            "/discussions/{id}/subscribe",
            post(handlers_discussions::subscribe_thread),
        )
        .route(
            "/discussions/{id}/unsubscribe",
            post(handlers_discussions::unsubscribe_thread),
        )
        // Announcements
        .route(
            "/courses/{id}/announcements",
            get(handlers_announcements::list_announcements),
        )
        .route(
            "/courses/{id}/announcements",
            post(handlers_announcements::create_announcement),
        )
        .route(
            "/announcements/{id}",
            put(handlers_announcements::update_announcement),
        )
        .route(
            "/announcements/{id}",
            delete(handlers_announcements::delete_announcement),
        )
        .route("/lessons/{id}/notes", get(handlers_notes::get_note))
        .route("/lessons/{id}/notes", put(handlers_notes::save_note))
        // Cohorts
        .route("/cohorts", get(handlers_cohorts::list_cohorts))
        .route("/cohorts", post(handlers_cohorts::create_cohort))
        .route(
            "/cohorts/{id}/members",
            post(handlers_cohorts::add_cohort_member),
        )
        .route(
            "/cohorts/{cohort_id}/members/{user_id}",
            delete(handlers_cohorts::remove_cohort_member),
        )
        .route(
            "/cohorts/{id}/members",
            get(handlers_cohorts::get_cohort_members),
        )
        // Peer Assessment
        .route(
            "/courses/{id}/lessons/{lesson_id}/submit",
            post(handlers_peer_review::submit_assignment),
        )
        .route(
            "/courses/{id}/lessons/{lesson_id}/peer-review",
            get(handlers_peer_review::get_peer_review_assignment),
        )
        .route(
            "/courses/{id}/lessons/{lesson_id}/peer-review",
            post(handlers_peer_review::submit_peer_review),
        )
        .route(
            "/courses/{id}/lessons/{lesson_id}/feedback",
            get(handlers_peer_review::get_my_submission_feedback),
        )
        .route(
            "/courses/{id}/lessons/{lesson_id}/submissions",
            get(handlers_peer_review::list_lesson_submissions),
        )
        .route(
            "/peer-reviews/submissions/{id}/reviews",
            get(handlers_peer_review::get_submission_reviews),
        )
        .route_layer(middleware::from_fn(
            common::middleware::org_extractor_middleware,
        ));

    let public_routes = Router::new()
        .route("/api-docs/openapi.json", get(|| async {
            axum::Json(openapi::ApiDoc::openapi())
        }))
        .route("/scalar", get(|| async {
            Html(r#"
<!doctype html>
<html>
  <head>
    <title>OpenCCB LMS API</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <script id="api-reference" data-url="/api-docs/openapi.json"></script>
    <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
  </body>
</html>
            "#)
        }))
        // Health check routes
        .merge(health::health_routes(pool.clone()).with_state(health_state))
        .route("/catalog", get(handlers::get_course_catalog))
        .route("/ingest", post(handlers::ingest_course))
        .route("/auth/register", post(handlers::register))
        .route("/auth/login", post(handlers::login))
        .route(
            "/payments/mercadopago/webhook",
            post(handlers_payments::mercadopago_webhook),
        )
        .route("/lti/login", get(lti::lti_login_initiation))
        .route("/lti/launch", post(lti::lti_launch))
        .route("/lti/jwks", get(jwks::lti_jwks_handler))
        .route("/lti/deep-linking/response", post(lti::lti_deep_linking_response))
        .merge(protected_routes)
        // Security headers
        .layer(SetResponseHeaderLayer::overriding(
            http::header::STRICT_TRANSPORT_SECURITY,
            http::HeaderValue::from_static("max-age=31536000; includeSubDomains"),
        ))
        .layer(SetResponseHeaderLayer::overriding(
            http::header::X_CONTENT_TYPE_OPTIONS,
            http::HeaderValue::from_static("nosniff"),
        ))
        .layer(SetResponseHeaderLayer::overriding(
            http::header::X_FRAME_OPTIONS,
            http::HeaderValue::from_static("SAMEORIGIN"),
        ))
        .layer(SetResponseHeaderLayer::overriding(
            http::header::X_XSS_PROTECTION,
            http::HeaderValue::from_static("1; mode=block"),
        ))
        .layer(SetResponseHeaderLayer::overriding(
            http::header::REFERRER_POLICY,
            http::HeaderValue::from_static("strict-origin-when-cross-origin"),
        ))
        .layer(cors)
        .with_state(pool)
        .layer(axum::Extension(mysql_pool));

    let addr = SocketAddr::from(([0, 0, 0, 0], 3002));
    tracing::info!("LMS Service listening on {} with rate limiting and security headers", addr);
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, public_routes).await.unwrap();
}
