use std::sync::Arc;

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

use crate::{
    cmd::init::InitRepositoriesParams,
    rpc::{
        error::{spawn_blocking_error, to_status},
        proto as p,
        registry::RepoRegistry,
    },
    sync::ReceiveSyncService,
};

pub struct InitServiceImpl {
    pub registry: Arc<RepoRegistry>,
    pub sync: ReceiveSyncService,
}

#[tonic::async_trait]
impl p::init_service_server::InitService for InitServiceImpl {
    async fn init_bare(
        &self,
        req: Request<p::InitBareRequest>,
    ) -> Result<Response<p::InitBareResponse>, Status> {
        let inner = req.into_inner();
        let proto_params = inner.params.unwrap_or_default();
        let namespace = proto_params.namespace.clone();
        let repo_name = proto_params.repo_name.clone();

        let params = InitRepositoriesParams {
            namespace,
            repo_name: repo_name.clone(),
            default_branch: proto_params.default_branch,
            description: proto_params.description,
            initialize_with_readme: proto_params.initialize_with_readme,
            enable_lfs: proto_params.enable_lfs,
        };

        let storage_root = inner.storage_root;
        let storage_path =
            InitRepositoriesParams::init_bare(storage_root, params)
                .await
                .map_err(to_status)?;

        // Note: init_bare creates a new repo; sync is triggered by the caller (service layer)
        // because the repo UUID is assigned in the DB, not here.

        Ok(Response::new(p::InitBareResponse { storage_path }))
    }

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

    async fn clone_bare(
        &self,
        req: Request<p::CloneBareRequest>,
    ) -> Result<Response<p::CloneBareResponse>, Status> {
        let inner = req.into_inner();
        let params = crate::cmd::init::CloneRepoParams {
            namespace: inner.namespace,
            repo_name: inner.repo_name,
            source_url: inner.source_url,
        };
        let storage_root = inner.storage_root;
        let storage_path =
            crate::cmd::init::CloneRepoParams::clone_bare(storage_root, params)
                .await
                .map_err(to_status)?;
        Ok(Response::new(p::CloneBareResponse { storage_path }))
    }
}
