use std::path::PathBuf;

use clap::Args;
use obikindex::{validate_label, KmerIndex};
use tracing::info;

#[derive(Args)]
pub struct UtilsArgs {
    /// Index directory to operate on
    pub index: PathBuf,

    /// Set a new genome label: NEW_LABEL=OLD_LABEL
    #[arg(long, value_name = "NEW=OLD")]
    pub new_label: Option<String>,

    /// Add missing layer_meta.json files to each layer (required after upgrading from old indexes)
    #[arg(long)]
    pub upgrade_index: bool,
}

pub fn run(args: UtilsArgs) {
    let mut any = false;

    if let Some(spec) = &args.new_label {
        any = true;
        run_rename(&args.index, spec);
    }

    if args.upgrade_index {
        any = true;
        run_upgrade_index(&args.index);
    }

    if !any {
        eprintln!("utils: no operation specified. Available options: --new-label NEW=OLD, --upgrade-index");
        std::process::exit(1);
    }
}

fn run_upgrade_index(index_path: &PathBuf) {
    let idx = KmerIndex::open(index_path).unwrap_or_else(|e| {
        eprintln!("error opening index: {e}");
        std::process::exit(1);
    });
    idx.upgrade_layer_meta().unwrap_or_else(|e| {
        eprintln!("upgrade error: {e}");
        std::process::exit(1);
    });
    info!("upgrade-index: layer_meta.json written to all layers that were missing it");
}

fn run_rename(index_path: &PathBuf, spec: &str) {
    let (old_label, new_label) = parse_rename_spec(spec);

    let mut idx = KmerIndex::open(index_path).unwrap_or_else(|e| {
        eprintln!("error opening index: {e}");
        std::process::exit(1);
    });

    let pos = idx
        .meta()
        .genomes
        .iter()
        .position(|g| g.label == old_label)
        .unwrap_or_else(|| {
            eprintln!("error: genome '{old_label}' not found in index");
            std::process::exit(1);
        });

    validate_label(&new_label).unwrap_or_else(|e| {
        eprintln!("error: --new-label: {e}");
        std::process::exit(1);
    });

    // Check the new label is not already taken.
    if idx.meta().genomes.iter().any(|g| g.label == new_label) {
        eprintln!("error: label '{new_label}' already exists in index");
        std::process::exit(1);
    }

    idx.meta_mut().genomes[pos].label = new_label.clone();
    idx.meta_mut().write(index_path).unwrap_or_else(|e| {
        eprintln!("error writing index metadata: {e}");
        std::process::exit(1);
    });

    // Rename the spectrum file if it exists.
    let spectrums_dir = index_path.join("spectrums");
    let old_spectrum = spectrums_dir.join(format!("{old_label}.json"));
    let new_spectrum = spectrums_dir.join(format!("{new_label}.json"));
    if old_spectrum.exists() {
        std::fs::rename(&old_spectrum, &new_spectrum).unwrap_or_else(|e| {
            eprintln!("warning: could not rename spectrum file: {e}");
        });
    }

    info!("renamed genome '{old_label}' → '{new_label}'");
}

fn parse_rename_spec(spec: &str) -> (String, String) {
    let eq = spec.find('=').unwrap_or_else(|| {
        eprintln!("error: --new-label expects NEW_LABEL=OLD_LABEL, got '{spec}'");
        std::process::exit(1);
    });
    let new = spec[..eq].trim().to_string();
    let old = spec[eq + 1..].trim().to_string();
    if old.is_empty() || new.is_empty() {
        eprintln!("error: --new-label: both old and new labels must be non-empty");
        std::process::exit(1);
    }
    (old, new)
}
