use crate::http::utils::{extract_basic_credentials, hash_access_key};
use crate::ssh::authz::SshAuthService;
use actix_web::{Error, HttpRequest};
use db::database::AppDatabase;
use models::repos::repo;
use models::users::{user, user_token};
use sea_orm::sqlx::types::chrono;
use sea_orm::*;

pub async fn verify_access_token(
    db: &AppDatabase,
    username: &str,
    access_key: &str,
) -> Result<user::Model, Error> {
    let user = user::Entity::find()
        .filter(user::Column::Username.eq(username))
        .one(db.reader())
        .await
        .map_err(|_| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?
        .ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?;

    let token_hash = hash_access_key(access_key);

    let token = user_token::Entity::find()
        .filter(user_token::Column::User.eq(user.uid))
        .filter(user_token::Column::TokenHash.eq(token_hash))
        .filter(user_token::Column::IsRevoked.eq(false))
        .one(db.reader())
        .await
        .map_err(|_| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?
        .ok_or_else(|| actix_web::error::ErrorUnauthorized("Invalid username or access key"))?;

    if let Some(expires_at) = token.expires_at {
        if expires_at < chrono::Utc::now() {
            return Err(actix_web::error::ErrorUnauthorized(
                "Access key has expired",
            ));
        }
    }

    Ok(user)
}

pub async fn authorize_repo_access(
    req: &HttpRequest,
    db: &AppDatabase,
    repo: &repo::Model,
    is_write: bool,
) -> Result<(), Error> {
    if !is_write && !repo.is_private {
        return Ok(());
    }

    let (username, access_key) = extract_basic_credentials(req)?;
    let user = verify_access_token(db, &username, &access_key).await?;
    let authz = SshAuthService::new(db.clone());

    let can_access = authz.check_repo_permission(&user, repo, is_write).await;
    if !can_access {
        return Err(actix_web::error::ErrorForbidden(
            "No permission for repository",
        ));
    }

    Ok(())
}
