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 TagServiceImpl {
    pub registry: Arc<RepoRegistry>,
}

#[tonic::async_trait]
impl p::tag_service_server::TagService for TagServiceImpl {
    async fn tag_list(
        &self,
        req: Request<p::TagListRequest>,
    ) -> Result<Response<p::TagListResponse>, Status> {
        let inner = req.into_inner();
        let bare = self.registry.get(&inner.repo_id).await?;
        let result = tokio::task::spawn_blocking(move || bare.tag_list())
            .await
            .map_err(spawn_blocking_error)?
            .map_err(to_status)?;
        Ok(Response::new(p::TagListResponse {
            tags: result.into_iter().map(Into::into).collect(),
        }))
    }

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

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

    async fn tag_init(
        &self,
        req: Request<p::TagInitRequest>,
    ) -> Result<Response<p::TagInitResponse>, 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.tag_init(params))
            .await
            .map_err(spawn_blocking_error)?
            .map_err(to_status)?;
        Ok(Response::new(p::TagInitResponse {
            oid: Some(result.into()),
        }))
    }

    async fn tag_delete(
        &self,
        req: Request<p::TagDeleteRequest>,
    ) -> Result<Response<p::TagDeleteResponse>, 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.tag_delete(params))
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::TagDeleteResponse {}))
    }

    async fn tag_rename(
        &self,
        req: Request<p::TagRenameRequest>,
    ) -> Result<Response<p::TagRenameResponse>, 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.tag_rename(params))
                .await
                .map_err(spawn_blocking_error)?
                .map_err(to_status)?;
        Ok(Response::new(p::TagRenameResponse {}))
    }

    async fn tag_update_message(
        &self,
        req: Request<p::TagUpdateMessageRequest>,
    ) -> Result<Response<p::TagUpdateMessageResponse>, 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.tag_update_message(params)
        })
        .await
        .map_err(spawn_blocking_error)?
        .map_err(to_status)?;
        Ok(Response::new(p::TagUpdateMessageResponse {
            oid: Some(result.into()),
        }))
    }
}
