use std::path::Path;
use std::sync::Arc;

use crate::GitError;
use git2::Repository;
use models::repos::repo;

#[derive(Clone)]
pub struct GitDomain {
    pub(crate) repo: Arc<Repository>,
}

// SAFETY: git2's Repository uses internal locking for thread-safe operations.
// We additionally enforce exclusive access via Arc::get_mut in repo_mut().
// All mutable access is gated through the synchronous methods of HookMetaDataSync,
// which are called from a single blocking thread per sync cycle.
#[allow(unsafe_code)]
unsafe impl Send for GitDomain {}
#[allow(unsafe_code)]
unsafe impl Sync for GitDomain {}

impl GitDomain {
    pub fn from_model(model: repo::Model) -> crate::GitResult<Self> {
        let repo =
            Repository::open(model.storage_path).map_err(|e| GitError::Internal(e.to_string()))?;
        Ok(Self {
            repo: Arc::new(repo),
        })
    }
    pub fn open<P: AsRef<Path>>(path: P) -> crate::GitResult<Self> {
        let repo = Repository::open(path).map_err(|e| GitError::Internal(e.to_string()))?;
        Ok(Self {
            repo: Arc::new(repo),
        })
    }

    pub fn open_workdir<P: AsRef<Path>>(path: P) -> crate::GitResult<Self> {
        let repo = Repository::open_bare(path).map_err(|e| GitError::Internal(e.to_string()))?;
        Ok(Self {
            repo: Arc::new(repo),
        })
    }

    pub fn init_bare<P: AsRef<Path>>(path: P) -> crate::GitResult<Self> {
        let repo = Repository::init_bare(path).map_err(|e| GitError::Internal(e.to_string()))?;
        Ok(Self {
            repo: Arc::new(repo),
        })
    }
    pub fn repo(&self) -> &Repository {
        &self.repo
    }

    pub fn repo_mut(&mut self) -> crate::GitResult<&mut Repository> {
        Arc::get_mut(&mut self.repo)
            .ok_or_else(|| GitError::Internal("GitDomain requires exclusive access".to_string()))
    }
}
