use git::rpc::{proto as p, proto::branch_service_client::BranchServiceClient};
use session::Session;

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

impl AppService {
    pub async fn git_branch_list(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        pagination: Pagination,
    ) -> Result<p::BranchListResponse, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let mut resp = client
            .branch_list(tonic::Request::new(p::BranchListRequest {
                repo_id: repo.id.to_string(),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();

        let offset = pagination.offset() as usize;
        let limit = pagination.limit() as usize;
        if offset > 0 || resp.branches.len() > limit {
            let start = offset.min(resp.branches.len());
            let end = (start + limit).min(resp.branches.len());
            resp.branches = resp.branches.drain(start..end).collect();
        }
        Ok(resp)
    }

    pub async fn git_branch_info(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        branch: String,
    ) -> Result<p::BranchInfoResponse, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_info(tonic::Request::new(p::BranchInfoRequest {
                repo_id: repo.id.to_string(),
                branch,
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        Ok(resp)
    }

    pub async fn git_branch_summary(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<p::BranchSummaryResponse, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_summary(tonic::Request::new(p::BranchSummaryRequest {
                repo_id: repo.id.to_string(),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        Ok(resp)
    }

    pub async fn git_branch_head(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
    ) -> Result<p::BranchHeadResponse, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_head(tonic::Request::new(p::BranchHeadRequest {
                repo_id: repo.id.to_string(),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        Ok(resp)
    }

    pub async fn git_branch_ahead_behind(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        local_branch: String,
        remote_branch: String,
    ) -> Result<p::BranchAheadBehindResponse, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_ahead_behind(tonic::Request::new(
                p::BranchAheadBehindRequest {
                    repo_id: repo.id.to_string(),
                    local_branch,
                    remote_branch,
                },
            ))
            .await
            .map_err(rpc_err)?
            .into_inner();
        Ok(resp)
    }

    pub async fn git_branch_upstream(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        branch: String,
    ) -> Result<p::BranchUpstreamResponse, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_upstream(tonic::Request::new(p::BranchUpstreamRequest {
                repo_id: repo.id.to_string(),
                branch,
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        Ok(resp)
    }

    pub async fn git_branch_fork(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        params: p::BranchForkParams,
    ) -> Result<p::BranchForkResponse, AppError> {
        let repo = self.git_require_admin(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_fork(tonic::Request::new(p::BranchForkRequest {
                repo_id: repo.id.to_string(),
                params: Some(params),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        self.queue_sync(repo.id).await;
        Ok(resp)
    }

    pub async fn git_branch_delete(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        params: p::BranchDeleteParams,
    ) -> Result<p::BranchDeleteResponse, AppError> {
        let repo = self.git_require_admin(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_delete(tonic::Request::new(p::BranchDeleteRequest {
                repo_id: repo.id.to_string(),
                params: Some(params),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        self.queue_sync(repo.id).await;
        Ok(resp)
    }

    pub async fn git_branch_rename(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        params: p::BranchReNameParams,
    ) -> Result<p::BranchRenameResponse, AppError> {
        let repo = self.git_require_admin(ctx, wk_name, repo_name).await?;
        let mut client = BranchServiceClient::new(self.git.clone());
        let resp = client
            .branch_rename(tonic::Request::new(p::BranchRenameRequest {
                repo_id: repo.id.to_string(),
                params: Some(params),
            }))
            .await
            .map_err(rpc_err)?
            .into_inner();
        self.queue_sync(repo.id).await;
        Ok(resp)
    }
}
