use crate::http::HttpAppState;
use crate::http::auth::authorize_repo_access;
use crate::http::handler::GitHttpHandler;
use crate::http::utils::get_repo_model;
use crate::ssh::RepoReceiveSyncTask;
use actix_web::{Error, HttpRequest, HttpResponse, web};
use std::path::PathBuf;
use std::time::Duration;
use tokio::time::timeout;

pub async fn info_refs(
    req: HttpRequest,
    path: web::Path<(String, String)>,
    state: web::Data<HttpAppState>,
) -> Result<HttpResponse, Error> {
    let ip = extract_ip(&req);
    if !state.rate_limiter.is_ip_read_allowed(&ip).await {
        return Err(actix_web::error::ErrorTooManyRequests(
            "Rate limit exceeded",
        ));
    }

    let service_param = req
        .query_string()
        .split('&')
        .find(|s| s.starts_with("service="))
        .and_then(|s| s.strip_prefix("service="))
        .ok_or_else(|| actix_web::error::ErrorBadRequest("Missing service parameter"))?;

    if service_param != "git-upload-pack" && service_param != "git-receive-pack" {
        return Err(actix_web::error::ErrorBadRequest("Invalid service"));
    }

    let path_inner = path.into_inner();
    let model = get_repo_model(&path_inner.0, &path_inner.1, &state.db).await?;
    let is_write = service_param == "git-receive-pack";
    authorize_repo_access(&req, &state.db, &model, is_write).await?;

    let storage_path = PathBuf::from(&model.storage_path);
    let handler = GitHttpHandler::new(storage_path, model, state.db.clone());
    handler.info_refs(service_param).await
}

pub async fn upload_pack(
    req: HttpRequest,
    path: web::Path<(String, String)>,
    payload: web::Payload,
    state: web::Data<HttpAppState>,
) -> Result<HttpResponse, Error> {
    let ip = extract_ip(&req);
    if !state.rate_limiter.is_ip_read_allowed(&ip).await {
        return Err(actix_web::error::ErrorTooManyRequests(
            "Rate limit exceeded",
        ));
    }

    let path_inner = path.into_inner();
    let model = get_repo_model(&path_inner.0, &path_inner.1, &state.db).await?;
    authorize_repo_access(&req, &state.db, &model, false).await?;

    let storage_path = PathBuf::from(&model.storage_path);
    let handler = GitHttpHandler::new(storage_path, model, state.db.clone());
    handler.upload_pack(payload).await
}

pub async fn receive_pack(
    req: HttpRequest,
    path: web::Path<(String, String)>,
    payload: web::Payload,
    state: web::Data<HttpAppState>,
) -> Result<HttpResponse, Error> {
    let ip = extract_ip(&req);
    if !state.rate_limiter.is_ip_write_allowed(&ip).await {
        return Err(actix_web::error::ErrorTooManyRequests(
            "Rate limit exceeded",
        ));
    }

    let path_inner = path.into_inner();
    let model = get_repo_model(&path_inner.0, &path_inner.1, &state.db).await?;
    authorize_repo_access(&req, &state.db, &model, true).await?;

    let storage_path = PathBuf::from(&model.storage_path);
    let handler = GitHttpHandler::new(storage_path, model.clone(), state.db.clone());
    let result = handler.receive_pack(payload).await;

    let _ = tokio::spawn({
        let sync = state.sync.clone();
        let repo_uid = model.id;
        async move {
            let _ = timeout(
                Duration::from_secs(5),
                sync.send(RepoReceiveSyncTask { repo_uid }),
            )
            .await;
        }
    });

    result
}

fn extract_ip(req: &HttpRequest) -> String {
    req.connection_info()
        .realip_remote_addr()
        .unwrap_or("unknown")
        .to_string()
}
