use serde::{Deserialize, Serialize};

use crate::{
    bare::GitBare,
    cmd::{merge::MergeOptions, oid::ObjectId},
    errors::{GitError, GitResult},
};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MergeTreeResult {
    pub tree_id: ObjectId,
    pub has_conflicts: bool,
}

impl GitBare {
    pub fn merge_tree(
        &self,
        ours: ObjectId,
        theirs: ObjectId,
        options: Option<MergeOptions>,
    ) -> GitResult<MergeTreeResult> {
        let mut args =
            vec!["merge-tree".to_string(), "--write-tree".to_string()];

        if let Some(opts) = &options {
            if opts.find_renames {
                args.push("--find-renames".to_string());
                if opts.rename_threshold > 0 {
                    args.push(format!(
                        "--rename-threshold={}",
                        opts.rename_threshold
                    ));
                }
            }
            if opts.fail_on_conflict {
                args.push("--fail-on-conflict".to_string());
            }
            if opts.no_recursive {
                args.push("--no-recursive".to_string());
            }
            if opts.target_limit > 0 {
                args.push(format!("--target-limit={}", opts.target_limit));
            }
        }

        args.push(ours.as_str().to_string());
        args.push(theirs.as_str().to_string());

        let output = self.git_command_with(
            crate::cmd::command::GitCommandParams::new(args).unchecked(),
        )?;

        let stdout = output.stdout_lossy();
        let mut lines = stdout.lines();
        let tree_oid_str = lines.next().unwrap_or("").trim();

        if tree_oid_str.is_empty() {
            if !output.success {
                return Err(GitError::CommandFailed {
                    status_code: output.status_code,
                    stderr: output.stderr_lossy(),
                });
            }
            return Err(GitError::ParseError(
                "merge-tree produced no tree OID output".to_string(),
            ));
        }
        let has_conflicts =
            !output.success || lines.any(|l| !l.trim().is_empty());

        Ok(MergeTreeResult {
            tree_id: ObjectId::new(tree_oid_str),
            has_conflicts,
        })
    }
}
