pub mod assignee;
pub mod comment;
pub mod label;
pub mod merge;
pub mod pull_request;
pub mod reaction;
pub mod review;
pub mod types;

use db::sqlx;
use git::rpc::proto::branch_service_client::BranchServiceClient;
use model::{pull_request::PullRequestModel, repos::RepoModel};
use session::Session;

use crate::{AppService, error::AppError, git::rpc_err};

impl AppService {
    pub async fn pr_resolve(
        &self,
        repo_id: uuid::Uuid,
        number: i64,
    ) -> Result<PullRequestModel, AppError> {
        sqlx::query_as::<_, PullRequestModel>(
            "SELECT id, repo, number, title, body, state, draft, author, \
             source_repo, source_branch, source_sha, target_branch, target_sha, \
             merged_by, merged_at, closed_by, closed_at, created_at, updated_at, deleted_at \
             FROM pull_request WHERE repo = $1 AND number = $2 AND deleted_at IS NULL",
        )
        .bind(repo_id)
        .bind(number)
        .fetch_optional(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?
        .ok_or(AppError::PullRequestNotFound)
    }

    pub async fn pr_resolve_repo(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<(uuid::Uuid, RepoModel), AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        Ok((repo.id, repo))
    }

    pub async fn pr_resolve_repo_admin(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<(uuid::Uuid, RepoModel), AppError> {
        let repo = self.git_require_admin(ctx, wk_name, repo_name).await?;
        Ok((repo.id, repo))
    }

    pub async fn branch_head_sha(
        &self,
        repo_id: uuid::Uuid,
        branch: &str,
    ) -> Result<String, AppError> {
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_info(tonic::Request::new(
                git::rpc::proto::BranchInfoRequest {
                    repo_id: repo_id.to_string(),
                    branch: branch.to_string(),
                },
            ))
            .await
            .map_err(rpc_err)?
            .into_inner();
        let sha = resp.branch.and_then(|b| b.oid).map(|oid| oid.value).ok_or(
            AppError::NotFound(format!("branch '{}' not found", branch)),
        )?;
        Ok(sha)
    }
}
