// Copyright (c) 2026 Ranjith N
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::process::Command;
use std::fs::{self, File};
use std::path::Path;
use std::io::{BufRead, BufReader};
use std::collections::HashSet;
use zip::write::FileOptions;

pub fn run(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {

    let mut input_file = String::new();
    let mut metadata_file = String::new();
    let mut meta_column = String::new();

    let mut class_level: Option<String> = None;
    let mut norm_flag = "clr".to_string();
    let mut output_prefix = "oss_output".to_string();

    // Only WEAVE flags remain
    let mut weave_flags: Vec<String> = vec![];

    // ---------------- FLAG PARSING ----------------
    let mut i = 1;
    while i < args.len() {
        match args[i].as_str() {

            "-i" => { i += 1; input_file = args[i].clone(); }
            "-m" => { i += 1; metadata_file = args[i].clone(); }
            "-mc" => { i += 1; meta_column = args[i].clone(); }

            "-c" => { i += 1; class_level = Some(args[i].clone()); }
            "-f" => { i += 1; norm_flag = args[i].clone(); }
            "-o" => { i += 1; output_prefix = args[i].clone(); }

            // WEAVE-only flags
            "-mp" | "-t" | "-Sminval" | "-Pminval" | "-Smaxp" | "-Pmaxp" => {
                weave_flags.push(args[i].clone());
                i += 1;
                weave_flags.push(args[i].clone());
            }

            _ => {}
        }
        i += 1;
    }

    if input_file.is_empty() || metadata_file.is_empty() || meta_column.is_empty() {
//        eprintln!("❌ Missing required arguments.");
        eprintln!(
            "Usage: {} -i <input.csv> -m <metadata.csv> -mc <meta_column> \
            [-c <class_level>] [-f <clr|abun|log1p|logret>] [-o <output_prefix>] \
            [-mp <min_percent>] [-t <threshold>] \
            [-Sminval <val>] [-Pminval <val>] [-Smaxp <val>] [-Pmaxp <val>]",
            args[0]
        );
        return Ok(());
    }


    // -------------- OUTPUT DIR -------------------
    let out_dir = format!("{}/", output_prefix);
    fs::create_dir_all(&out_dir)?;

    let exe_path = std::env::current_exe()?;

    // ============================================================
    // STEP 1: SPUN ALWAYS RUNS
    // ============================================================

    println!("▶ Running SPUN");

    let spun_out = format!("{out_dir}{output_prefix}_spun.csv");

    // Build base SPUN args
    let mut spun_args = vec![
        "spun",
        "-i", &input_file,
        "-o", &spun_out,
        "-f", &norm_flag,
    ];

    // Add optional -c only if provided
    if let Some(c_level) = &class_level {
        spun_args.extend(["-c", c_level]);
    }

    let spun_status = Command::new(&exe_path)
        .args(&spun_args)
        .status()?;

    if !spun_status.success() {
        eprintln!("❌ spun failed");
        return Ok(());
    }

    println!("✅ SPUN complete → {}", spun_out);

    let analysis_input = spun_out;

    // ============================================================
    // STEP 2: READ METADATA VALUES
    // ============================================================

    let metadata = fs::read_to_string(&metadata_file)?;
    let headers: Vec<&str> = metadata.lines().next().unwrap().split(',').collect();

    let col_idx = headers
        .iter()
        .position(|h| *h == meta_column)
        .expect("Metadata column not found");

    let mut unique_values: HashSet<String> = HashSet::new();

    let file = File::open(&metadata_file)?;
    let reader = BufReader::new(file);

    for line in reader.lines().skip(1) {
        let row_str = line?;
        let row: Vec<&str> = row_str.split(',').collect();
        if let Some(val) = row.get(col_idx) {
            unique_values.insert(val.to_string());
        }
    }

    // ============================================================
    // STEP 3: WEAVE
    // ============================================================

    let weave_prefix = format!("{out_dir}{output_prefix}_weave_{}", meta_column);

    let mut weave_args: Vec<String> = vec![
        "weave".into(),
        "-i".into(), analysis_input.clone(),
        "-m".into(), metadata_file.clone(),
        "-mc".into(), meta_column.clone(),
    ];

    weave_args.extend(weave_flags.clone());

    weave_args.push("-o".into());
    weave_args.push(weave_prefix.clone());

    let weave_status = Command::new(&exe_path)
        .args(&weave_args)
        .status()?;

    if !weave_status.success() {
        eprintln!("❌ weave failed");
        return Ok(());
    }

    println!("✅ WEAVE complete");

    let weave_oall = format!("{weave_prefix}_oall.csv");

    // ============================================================
    // STEP 4: ECOCART PER GROUP
    // ============================================================

    for val in unique_values {
        let svg_out = format!("{out_dir}{output_prefix}_ecocart_{}_{}.svg", meta_column, val);

        let ec_status = Command::new(&exe_path)
            .args([
                "ecocart",
                "-i", &weave_oall,
                "-m", &val,
                "-o", &svg_out,
            ])
            .status()?;

        if ec_status.success() {
            println!("   → SVG created: {}", svg_out);
        } else {
            eprintln!("❌ ecocart failed for {}", val);
        }
    }

    println!("✅ ECOCART visualizations completed");

    // ============================================================
    // STEP 5: ZIP OUTPUT
    // ============================================================

    zip_dir(&out_dir, &format!("{output_prefix}.zip"))?;
    println!("🎉 ALL DONE → {}.zip", output_prefix);

    Ok(())
}


// ============================================================
// ZIP HELPER
// ============================================================

fn zip_dir<P: AsRef<Path>>(src_dir: P, zip_file: &str)
    -> Result<(), Box<dyn std::error::Error>>
{
    let file = File::create(Path::new(zip_file))?;
    let mut zip = zip::ZipWriter::new(file);
    let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored);

    for entry in walkdir::WalkDir::new(&src_dir)
        .into_iter()
        .filter_map(|e| e.ok())
    {
        let path = entry.path();
        let name = path.strip_prefix(src_dir.as_ref())?;

        if path.is_file() {
            zip.start_file(name.to_str().unwrap(), options)?;
            let mut f = File::open(path)?;
            std::io::copy(&mut f, &mut zip)?;
        } else if !name.as_os_str().is_empty() {
            zip.add_directory(name.to_str().unwrap(), options)?;
        }
    }

    zip.finish()?;
    Ok(())
}

