use std::path::PathBuf;

use clap::Args;
use obikindex::{KmerIndex, MergeMode};
use obisys::Reporter;
use tracing::info;

#[derive(Args)]
pub struct MergeArgs {
    /// Source index directories to merge
    #[arg(required = true)]
    pub sources: Vec<PathBuf>,

    /// Output index directory
    #[arg(short, long)]
    pub output: PathBuf,

    /// Overwrite output directory if it already exists
    #[arg(long, default_value_t = false)]
    pub force: bool,

    /// Force presence/absence mode even if all sources have count data
    #[arg(long, default_value_t = false)]
    pub force_presence: bool,

    /// Disambiguate duplicate genome labels by appending .1, .2, … instead of erroring
    #[arg(long, default_value_t = false)]
    pub rename_duplicates: bool,

    /// Fraction of available RAM reserved as memory budget for parallel partition merging.
    /// Reduce if OOM occurs despite the adaptive scheduler (e.g. --budget-fraction 0.3).
    #[arg(long, default_value_t = 0.5)]
    pub budget_fraction: f64,
}

pub fn run(args: MergeArgs) {
    let sources: Vec<KmerIndex> = args.sources.iter().map(|p| {
        info!("opening source index: {}", p.display());
        KmerIndex::open(p).unwrap_or_else(|e| {
            eprintln!("error opening source index {}: {e}", p.display());
            std::process::exit(1);
        })
    }).collect();

    // Auto-detect mode: count if all sources have count data, presence otherwise.
    // --force-presence overrides to presence regardless.
    let all_have_counts = sources.iter().all(|s| s.meta().config.with_counts);
    let mode = if !args.force_presence && all_have_counts {
        MergeMode::Count
    } else {
        MergeMode::Presence
    };
    info!(
        "merge mode: {}",
        if mode == MergeMode::Count { "count" } else { "presence/absence" }
    );

    let source_refs: Vec<&KmerIndex> = sources.iter().collect();


    let n_genomes: usize = sources.iter().map(|s| s.meta().genomes.len()).sum();
    info!(
        "merging {} index(es), {} genome(s) total → {}",
        sources.len(), n_genomes, args.output.display()
    );

    // Only the destination is written; sources are opened read-only above
    // and need no lock.
    let _lock = obisys::DirLock::acquire(&args.output).unwrap_or_else(|e| {
        eprintln!("error locking output directory {}: {e}", args.output.display());
        std::process::exit(1);
    });

    let mut rep = Reporter::new();
    KmerIndex::merge(&args.output, &source_refs, mode, args.force, args.rename_duplicates, args.budget_fraction, &mut rep).unwrap_or_else(|e| {
        eprintln!("error merging: {e}");
        std::process::exit(1);
    });
    rep.print();
}
