use std::path::Path;

use crate::{
    bare::GitBare,
    cmd::{
        blame::{BlameOptions, CommitBlameLine},
        oid::ObjectId,
    },
    errors::GitResult,
};

impl GitBare {
    pub fn blame_lines(
        &self,
        oid: ObjectId,
        path: impl AsRef<Path>,
        opts: Option<BlameOptions>,
    ) -> GitResult<Vec<CommitBlameLine>> {
        let repo = self.gix_repo()?;
        let gix_id: gix::hash::ObjectId = (&oid).try_into()?;

        let blame_opts = opts.unwrap_or_default();

        let ranges = if let (Some(min), Some(max)) =
            (blame_opts.min_line, blame_opts.max_line)
        {
            gix::blame::BlameRanges::from_one_based_inclusive_range(
                min as u32..=max as u32,
            )?
        } else if let Some(min) = blame_opts.min_line {
            gix::blame::BlameRanges::from_one_based_inclusive_range(
                min as u32..=min as u32,
            )?
        } else {
            gix::blame::BlameRanges::default()
        };

        let rewrites = if blame_opts.track_copies_same_file
            || blame_opts.track_copies_same_commit_moves
        {
            Some(gix::diff::Rewrites::default())
        } else {
            None
        };

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

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

        let mut result = Vec::new();

        for (entry, lines) in outcome.entries_with_lines() {
            let commit_oid =
                ObjectId::new(entry.commit_id.to_hex().to_string());
            let orig_path =
                entry.source_file_name.as_ref().map(|p| p.to_string());
            let base_line = entry.range_in_blamed_file().start as u32 + 1;

            for (i, line) in lines.iter().enumerate() {
                result.push(CommitBlameLine {
                    commit_oid: commit_oid.clone(),
                    line_no: base_line + i as u32,
                    content: line.to_string(),
                    orig_path: orig_path.clone(),
                });
            }
        }

        Ok(result)
    }
}
