use crate::errors::GitError;
use crate::git::cli::GitCli;
use std::sync::Arc;

/// Service wrapper around GitCli that provides structured error handling
pub struct GitService {
    cli: Arc<GitCli>,
}

impl GitService {
    pub fn new() -> Self {
        Self {
            cli: Arc::new(GitCli::new()),
        }
    }
    
    /// Get status porcelain output
    pub fn status_porcelain(&self, path: &str) -> Result<String, GitError> {
        self.cli.status_porcelain(path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Stage a file
    pub fn stage_file(&self, repo_path: &str, path: &str) -> Result<(), GitError> {
        self.cli.stage_file(repo_path, path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Unstage a file
    pub fn unstage_file(&self, repo_path: &str, path: &str) -> Result<(), GitError> {
        self.cli.unstage_file(repo_path, path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get current branch
    pub fn current_branch(&self, repo_path: &str) -> Result<String, GitError> {
        self.cli.current_branch(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get branches list
    pub fn branches(&self, repo_path: &str) -> Result<Vec<crate::app::state::BranchEntry>, GitError> {
        self.cli.branches(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    pub fn branch_names(&self, repo_path: &str) -> Result<Vec<String>, GitError> {
        let branches = self.branches(repo_path)?;
        Ok(branches.into_iter().map(|b| b.name).collect())
    }
    
    /// Get ahead/behind counts for current branch
    pub fn ahead_behind(&self, repo_path: &str) -> Result<(i32, i32), GitError> {
        self.cli.ahead_behind(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get commit log
    pub fn log(&self, repo_path: &str) -> Result<Vec<crate::app::state::CommitEntry>, GitError> {
        self.cli.log(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Show commit details
    pub fn show_commit(&self, repo_path: &str, hash: &str) -> Result<String, GitError> {
        self.cli.show_commit(repo_path, hash)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get log with graph
    pub fn log_with_graph(&self, repo_path: &str, limit: usize) -> Result<String, GitError> {
        self.cli.log_with_graph(repo_path, limit)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get stash list
    pub fn stash_list(&self, repo_path: &str) -> Result<Vec<crate::app::state::StashEntry>, GitError> {
        self.cli.stash_list(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Apply or pop stash
    pub fn stash_apply(&self, repo_path: &str, name: &str, pop: bool) -> Result<(), GitError> {
        self.cli.stash_apply(repo_path, name, pop)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn stash_push(&self, repo_path: &str, message: &str) -> Result<(), GitError> {
        self.cli.stash_push(repo_path, message)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn stash_drop(&self, repo_path: &str, selector: &str) -> Result<(), GitError> {
        self.cli.stash_drop(repo_path, selector)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Checkout branch
    pub fn checkout_branch(&self, repo_path: &str, branch: &str) -> Result<(), GitError> {
        self.cli.checkout_branch(repo_path, branch)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Merge branch into current
    pub fn merge(&self, repo_path: &str, branch: &str) -> Result<(), GitError> {
        self.cli.merge(repo_path, branch)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Rebase current branch onto another
    pub fn rebase(&self, repo_path: &str, branch: &str) -> Result<(), GitError> {
        self.cli.rebase(repo_path, branch)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Create a new branch
    pub fn create_branch(&self, repo_path: &str, branch: &str) -> Result<(), GitError> {
        self.cli.create_branch(repo_path, branch)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get tags list
    pub fn tags(&self, repo_path: &str) -> Result<Vec<crate::app::state::TagEntry>, GitError> {
        self.cli.tags(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get reflog entries
    pub fn reflog(&self, repo_path: &str) -> Result<Vec<crate::app::state::ReflogEntry>, GitError> {
        self.cli.reflog(repo_path, 100) // Default limit of 100
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get diff text for a path with context and staged toggle.
    pub fn diff(
        &self,
        repo_path: &str,
        path: &str,
        staged: bool,
        context: usize,
    ) -> Result<String, GitError> {
        self.cli
            .diff(repo_path, path, staged, context)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Get last commit information (for amend operations)
    pub fn last_commit_info(&self, repo_path: &str) -> Result<crate::app::state::LastCommitInfo, GitError> {
        self.cli
            .last_commit_info(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }
    
    /// Commit staged changes with message.
    /// If `author_name` and `author_email` are provided, use `--author` flag.
    pub fn commit(&self, repo_path: &str, message: &str, amend: bool, author_name: Option<&str>, author_email: Option<&str>) -> Result<(), GitError> {
        self.cli
            .commit(repo_path, message, amend, author_name, author_email)
            .map_err(|e| GitError::OperationError(e))
    }

    /// Push current branch.
    pub fn push(&self, repo_path: &str, force_with_lease: bool) -> Result<(), GitError> {
        self.cli
            .push(repo_path, force_with_lease)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn cherry_pick(&self, repo_path: &str, hash: &str) -> Result<(), GitError> {
        self.cli
            .cherry_pick(repo_path, hash)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn revert(&self, repo_path: &str, hash: &str) -> Result<(), GitError> {
        self.cli
            .revert(repo_path, hash)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn rebase_continue(
        &self,
        repo_path: &str,
        message: Option<&str>,
        author_name: Option<&str>,
        author_email: Option<&str>,
    ) -> Result<(), GitError> {
        self.cli
            .rebase_continue(repo_path, message, author_name, author_email)
            .map_err(GitError::OperationError)
    }

    pub fn rebase_start_root(&self, repo_path: &str) -> Result<String, GitError> {
        self.cli
            .start_rebase_interactive_root(repo_path)
            .map_err(GitError::OperationError)
    }

    pub fn root_commit(&self, repo_path: &str) -> Result<String, GitError> {
        self.cli.root_commit(repo_path).map_err(GitError::OperationError)
    }

    pub fn rebase_abort(&self, repo_path: &str) -> Result<(), GitError> {
        self.cli
            .rebase_abort(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn rebase_skip(&self, repo_path: &str) -> Result<(), GitError> {
        self.cli
            .rebase_skip(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn reset(&self, repo_path: &str, mode: &str, target: &str) -> Result<(), GitError> {
        self.cli
            .reset(repo_path, mode, target)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn fetch_dry_run(&self, repo_path: &str, remote: &str) -> Result<String, GitError> {
        self.cli
            .fetch_dry_run(repo_path, remote)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn ahead_behind_remote(&self, repo_path: &str, branch: &str) -> Result<(i32, i32), GitError> {
        self.cli
            .ahead_behind_remote(repo_path, branch)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn merged_local_branches(&self, repo_path: &str, base: &str) -> Result<Vec<String>, GitError> {
        self.cli
            .merged_local_branches(repo_path, base)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn delete_local_branch(&self, repo_path: &str, branch: &str) -> Result<(), GitError> {
        self.cli
            .delete_local_branch(repo_path, branch)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn remote_prune(&self, repo_path: &str, remote: &str) -> Result<String, GitError> {
        self.cli
            .remote_prune(repo_path, remote)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn remote_url(&self, repo_path: &str, remote: &str) -> Result<String, GitError> {
        self.cli
            .remote_url(repo_path, remote)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn set_remote_url(&self, repo_path: &str, remote: &str, url: &str) -> Result<(), GitError> {
        self.cli
            .set_remote_url(repo_path, remote, url)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn list_pull_requests(&self, repo_path: &str, remote: &str) -> Result<Vec<(String, String)>, GitError> {
        self.cli
            .list_pull_requests(repo_path, remote)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn fetch_pr(&self, repo_path: &str, remote: &str, pr: &str) -> Result<(), GitError> {
        self.cli
            .fetch_pr(repo_path, remote, pr)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn checkout_pr_branch(&self, repo_path: &str, pr: &str) -> Result<(), GitError> {
        self.cli
            .checkout_pr_branch(repo_path, pr)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn log_brief(&self, repo_path: &str, limit: usize) -> Result<String, GitError> {
        self.cli
            .log_brief(repo_path, limit)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn log_stats(&self, repo_path: &str, limit: usize) -> Result<String, GitError> {
        self.cli
            .log_stats(repo_path, limit)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn pull_ff_only(&self, repo_path: &str, ff_only: bool, timeout_secs: u64) -> Result<(), GitError> {
        self.cli
            .pull_ff_only(repo_path, ff_only, timeout_secs)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn pull_rebase(&self, repo_path: &str, autostash: bool, timeout_secs: u64) -> Result<(), GitError> {
        self.cli
            .pull_rebase(repo_path, autostash, timeout_secs)
            .map_err(|e| GitError::OperationError(e))
    }

    /// Apply a patch to index or worktree; reverse when requested.
    pub fn apply_patch(&self, repo_path: &str, patch: &str, to_index: bool, reverse: bool) -> Result<(), GitError> {
        self.cli
            .apply_patch(repo_path, patch, to_index, reverse)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn read_rebase_todo(&self, repo_path: &str) -> Result<(Vec<String>, String), GitError> {
        self.cli
            .read_rebase_todo(repo_path)
            .map_err(|e| GitError::OperationError(e))
    }

    pub fn start_rebase_interactive(&self, repo_path: &str, base: &str) -> Result<String, GitError> {
        self.cli
            .start_rebase_interactive(repo_path, base)
            .map_err(|e| GitError::OperationError(e))
    }
}

impl Default for GitService {
    fn default() -> Self {
        Self::new()
    }
}