use std::path::Path;

use crate::{
    bare::GitBare,
    cmd::{blame::CommitBlameHunk, oid::ObjectId},
    errors::{GitError, GitResult},
};

impl GitBare {
    pub fn blame_hunk(
        &self,
        oid: &ObjectId,
        path: impl AsRef<Path>,
        line_on: usize,
    ) -> GitResult<CommitBlameHunk> {
        let repo = self.gix_repo()?;
        let gix_id: gix::hash::ObjectId = (oid).try_into()?;

        let ranges = gix::blame::BlameRanges::from_one_based_inclusive_range(
            line_on as u32..=line_on as u32,
        )?;

        let gix_options = gix::repository::blame_file::Options {
            diff_algorithm: None,
            ranges,
            since: None,
            rewrites: None,
        };

        let outcome = repo.blame_file(
            path.as_ref()
                .as_os_str()
                .to_string_lossy()
                .as_bytes()
                .as_ref(),
            gix_id,
            gix_options,
        )?;

        let entry = outcome.entries.first().ok_or_else(|| {
            GitError::ParseError(format!(
                "no blame hunk found for line {} in {} at {}",
                line_on,
                path.as_ref().display(),
                oid
            ))
        })?;

        let range_in_blamed = entry.range_in_blamed_file();
        let range_in_source = entry.range_in_source_file();

        let commit_oid = ObjectId::new(entry.commit_id.to_hex().to_string());
        let is_boundary = repo
            .find_commit(entry.commit_id)
            .ok()
            .is_some_and(|c| c.parent_ids().count() == 0);
        let orig_path = entry.source_file_name.as_ref().map(|p| p.to_string());

        Ok(CommitBlameHunk {
            commit_oid,
            final_start_line: range_in_blamed.start as u32 + 1,
            final_lines: range_in_blamed.len() as u32,
            orig_start_line: range_in_source.start as u32 + 1,
            orig_lines: range_in_source.len() as u32,
            boundary: is_boundary,
            orig_path,
        })
    }
}
