use std::sync::Arc;

use tonic::{Request, Response, Status};

use crate::rpc::{
    error::{spawn_blocking_error, to_status},
    proto as p,
    registry::RepoRegistry,
};

pub struct BranchServiceImpl {
    pub registry: Arc<RepoRegistry>,
}

#[tonic::async_trait]
impl p::branch_service_server::BranchService for BranchServiceImpl {
    async fn branch_list(
        &self,
        req: Request<p::BranchListRequest>,
    ) -> Result<Response<p::BranchListResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let result =
            tokio::task::spawn_blocking(move || bare.branch_list_all())
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::BranchListResponse {
            branches: result.into_iter().map(Into::into).collect(),
        }))
    }

    async fn branch_info(
        &self,
        req: Request<p::BranchInfoRequest>,
    ) -> Result<Response<p::BranchInfoResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let branch = inner.branch.clone();
        let result =
            tokio::task::spawn_blocking(move || bare.branch_info(branch))
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::BranchInfoResponse {
            branch: Some(result.into()),
        }))
    }

    async fn branch_summary(
        &self,
        req: Request<p::BranchSummaryRequest>,
    ) -> Result<Response<p::BranchSummaryResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let result = tokio::task::spawn_blocking(move || bare.branch_summary())
            .await
            .map_err(spawn_blocking_error)?
            .map_err(to_status)?;
        Ok(Response::new(p::BranchSummaryResponse {
            summary: Some(result.into()),
        }))
    }

    async fn branch_head(
        &self,
        req: Request<p::BranchHeadRequest>,
    ) -> Result<Response<p::BranchHeadResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let result =
            tokio::task::spawn_blocking(move || bare.branch_head_name())
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::BranchHeadResponse { head_name: result }))
    }

    async fn branch_ahead_behind(
        &self,
        req: Request<p::BranchAheadBehindRequest>,
    ) -> Result<Response<p::BranchAheadBehindResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let branch = inner.local_branch.clone();
        let result = tokio::task::spawn_blocking(move || {
            bare.branch_ahead_behind(branch)
        })
        .await
        .map_err(spawn_blocking_error)?
        .map_err(to_status)?;
        Ok(Response::new(p::BranchAheadBehindResponse {
            ahead: if result { 1 } else { 0 },
            behind: 0,
        }))
    }

    async fn branch_upstream(
        &self,
        req: Request<p::BranchUpstreamRequest>,
    ) -> Result<Response<p::BranchUpstreamResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let branch = inner.branch.clone();
        let result = tokio::task::spawn_blocking(move || {
            bare.branch_upstream_name(branch)
        })
        .await
        .map_err(spawn_blocking_error)?
        .map_err(to_status)?;
        Ok(Response::new(p::BranchUpstreamResponse {
            upstream_name: result.unwrap_or_default(),
        }))
    }

    async fn branch_fork(
        &self,
        req: Request<p::BranchForkRequest>,
    ) -> Result<Response<p::BranchForkResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let params = inner.params.unwrap_or_default().into();
        let _result =
            tokio::task::spawn_blocking(move || bare.branch_fork(&params))
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::BranchForkResponse {}))
    }

    async fn branch_delete(
        &self,
        req: Request<p::BranchDeleteRequest>,
    ) -> Result<Response<p::BranchDeleteResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let params = inner.params.unwrap_or_default().into();
        let _result =
            tokio::task::spawn_blocking(move || bare.branch_delete(params))
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::BranchDeleteResponse {}))
    }

    async fn branch_rename(
        &self,
        req: Request<p::BranchRenameRequest>,
    ) -> Result<Response<p::BranchRenameResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let params = inner.params.unwrap_or_default().into();
        let _result =
            tokio::task::spawn_blocking(move || bare.branch_rename(params))
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::BranchRenameResponse {}))
    }
}
