pub mod archive;
pub mod blame;
pub mod blob;
pub mod branch;
pub mod commit;
pub mod commit_status;
pub mod compare;
pub mod contents;
pub mod contributor;
pub mod diff;
pub mod fork;
pub mod init;
pub mod language;
pub mod protect;
pub mod readme;
pub mod refs;
pub mod release;
pub mod repo;
pub mod star;
pub mod tag;
pub mod tree;
pub mod watch;
pub mod webhook;

use db::sqlx;
use git::sync::{ReceiveSyncService, RepoReceiveSyncTask};
use model::repos::RepoModel;
use session::Session;

use crate::{AppService, error::AppError, session_user};

impl AppService {
    pub async fn queue_sync(&self, repo_uid: uuid::Uuid) {
        let sync_service = ReceiveSyncService::new(self.redis_pool.clone());
        sync_service.send(RepoReceiveSyncTask { repo_uid }).await;
    }

    pub async fn repo_resolve(
        &self,
        wk_id: uuid::Uuid,
        repo_name: &str,
    ) -> Result<RepoModel, AppError> {
        sqlx::query_as::<_, RepoModel>(
            "SELECT id, wk, name, description, default_branch, visibility, size_bytes, \
             is_archived, is_template, is_mirror, created_by, storage_path, created_at, updated_at, deleted_at \
             FROM repo WHERE wk = $1 AND name = $2 AND deleted_at IS NULL",
        )
        .bind(wk_id)
        .bind(repo_name)
        .fetch_optional(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?
        .ok_or(AppError::RepoNotFound)
    }

    pub async fn git_require_member(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<RepoModel, AppError> {
        let user_uid = session_user(ctx)?;
        let wk = self.workspace_resolve(wk_name).await?;
        self.workspace_require_member(wk.id, user_uid).await?;
        self.repo_resolve(wk.id, repo_name).await
    }

    pub async fn git_require_admin(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<RepoModel, AppError> {
        let user_uid = session_user(ctx)?;
        let wk = self.workspace_resolve(wk_name).await?;
        self.workspace_require_admin(wk.id, user_uid).await?;
        self.repo_resolve(wk.id, repo_name).await
    }
}

pub fn rpc_err(status: tonic::Status) -> AppError {
    match status.code() {
        tonic::Code::NotFound => {
            AppError::NotFound(status.message().to_string())
        }
        tonic::Code::PermissionDenied => AppError::PermissionDenied,
        tonic::Code::InvalidArgument => {
            AppError::BadRequest(status.message().to_string())
        }
        tonic::Code::AlreadyExists => {
            AppError::Conflict(status.message().to_string())
        }
        _ => AppError::GitRpcError(status.message().to_string()),
    }
}
