use crate::hook::HookService;
use actix_web::{App, HttpServer, web};
use config::AppConfig;
use db::cache::AppCache;
use db::database::AppDatabase;
use std::sync::Arc;

pub mod auth;
pub mod handler;
pub mod lfs;
pub mod lfs_routes;
pub mod rate_limit;
pub mod routes;
pub mod utils;

#[derive(Clone)]
pub struct HttpAppState {
    pub db: AppDatabase,
    pub cache: AppCache,
    pub sync: crate::ssh::ReceiveSyncService,
    pub rate_limiter: Arc<rate_limit::RateLimiter>,
}

pub fn git_http_cfg(cfg: &mut web::ServiceConfig) {
    cfg.route(
        "/{namespace}/{repo_name}.git/info/refs",
        web::get().to(routes::info_refs),
    )
    .route(
        "/{namespace}/{repo_name}.git/git-upload-pack",
        web::post().to(routes::upload_pack),
    )
    .route(
        "/{namespace}/{repo_name}.git/git-receive-pack",
        web::post().to(routes::receive_pack),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/objects/batch",
        web::post().to(lfs_routes::lfs_batch),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/objects/{oid}",
        web::put().to(lfs_routes::lfs_upload),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/objects/{oid}",
        web::get().to(lfs_routes::lfs_download),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/locks",
        web::post().to(lfs_routes::lfs_lock_create),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/locks",
        web::get().to(lfs_routes::lfs_lock_list),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/locks/{id}",
        web::get().to(lfs_routes::lfs_lock_get),
    )
    .route(
        "/{namespace}/{repo_name}.git/info/lfs/locks/{id}",
        web::delete().to(lfs_routes::lfs_lock_delete),
    );
}

pub async fn run_http(config: AppConfig) -> anyhow::Result<()> {
    let (db, app_cache) = tokio::join!(AppDatabase::init(&config), AppCache::init(&config),);
    let db = db?;
    let app_cache = app_cache?;

    let redis_pool = app_cache.redis_pool().clone();
    let hook = HookService::new(
        db.clone(),
        app_cache.clone(),
        redis_pool.clone(),
        config.clone(),
    );
    let _worker_cancel = hook.start_worker();
    tracing::info!("hook worker started");

    let sync = crate::ssh::ReceiveSyncService::new(redis_pool.clone());

    let rate_limiter = Arc::new(rate_limit::RateLimiter::new(
        rate_limit::RateLimitConfig::default(),
    ));
    let _cleanup = rate_limiter.clone().start_cleanup();

    let state = HttpAppState {
        db: db.clone(),
        cache: app_cache.clone(),
        sync,
        rate_limiter,
    };

    tracing::info!("Starting git HTTP server on 0.0.0.0:8021");

    let server = HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(state.clone()))
            .configure(git_http_cfg)
    })
    .bind("0.0.0.0:8021")?
    .run();

    // Await the server. Actix-web handles Ctrl+C gracefully by default:
    // workers finish in-flight requests then exit (graceful shutdown).
    let result = server.await;
    if let Err(e) = result {
        tracing::error!("HTTP server error: {}", e);
    }

    tracing::info!("Git HTTP server stopped");
    Ok(())
}
