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

use std::error::Error;
use std::io::Write; // REQUIRED for stdin.write_all()
mod tools;
mod oss;

// Keep these in sync with Cargo.toml (and README).
const CLI_NAME: &str = "rsand";
const CLI_VERSION: &str = env!("CARGO_PKG_VERSION");
const CLI_DESC: &str = env!("CARGO_PKG_DESCRIPTION");

fn print_version() {
    // Typical CLI format
    println!("{CLI_NAME} {CLI_VERSION}");
    // Optional: also show description
    // println!("{CLI_DESC}");
}


fn print_help() {
    let help_text = format!(
    r#"

                        ┏━━━━━━━━━┓ ┏┓
                        ┃┳━┓┏┓┏┓┏┓┃┏┫┃
                        ┃┣┳┛┗┓┣┫┃┃┃┃┃┃
                        ┗┛┗━━┛┃┗┛┗┛┗┛┃
                              ┗━━━━━━┛

RSAND CLI - {desc} 

Version {ver}

USAGE: rsand <command> [options]

Commands:
  spun      CLR normalization
  plaw      Powerlaw analysis
  weave     Direction & Sign inference
  ecocart   Cartesian eco network charts
  oss       Full pipeline (spun+weave+ecocart)

----------------------------------------------------------------------
          AVAILABLE COMMANDS AND OPTIONS FOR EACH COMMANDS
----------------------------------------------------------------------
>>> rsand spun - Compositional Normlalisation (Converts compositional OTU table into CLR Normalised taxa list.)

Usage: spun -i <input.tsv> -o <output.csv> [-c <1|2|3|4|5|6|7>] [-f clr|abun|log1p|logret]

----------------------------------------------------------------------
>>> rsand plaw - Uses abundance csv from spun to analyse the community distribution using powerlaw

Usage: rsand plaw -i <input.csv> -m <metadata.csv> -mc <meta_column> [-o <prefix>] [-mp <min_prevalence>]

----------------------------------------------------------------------
>>> rsand weave - Calculate Direction and Sign using regression asymmetry and local perturbation based on metadata conditions.

Usage: rsand weave -i <input.csv> -m <metadata.csv> -mc <meta_column>:[1 specific condition] [-mp <min_percent>] [-t <threshold>] ...
Optional flags:
[-o <output_prefix>]
[-t <threshold>]
[-mp <minimum cooccurrence prevalence>]
[-Sminval <minimum Spearman>]
[-Pminval <minimum Pearson>]
[-Smaxp <maximum Spearman pvalue>]
[-Pmaxp <maximum Pearson pvalue>]

----------------------------------------------------------------------
>>> rsand ecocart - Generates Eco-Cartesian Network Plots

Usage: rsand ecocart -i <INPUT_FILE> -m <META_FILT> -o <OUTPUT_FILE>
----------------------------------------------------------------------

Usage: rsand [COMMAND]

Commands:
  spun     rsand spun -i <input.tsv> -o <output.csv> ...
  weave    rsand weave -i <input.csv> -m <metadata.csv> ...
  ecocart  rsand ecocart -i <INPUT_FILE> -m <META> ...
  oss      rsand oss -i <input.tsv> -m <metadata.csv> -mc <meta_column> -c <1|2|3|4|5|6|7> -f clr -o <output_prefix> -mp 10 -t 3 ... 
  plaw     rsand p-law -i <input.tsv> ...
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help

  -V, --version

----------------------------------------------------------------------
>>> Example Workflow and Input Formats...

----------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------
>>> Example Input TSV file...  The pipe ( | ) separators are optional and used for classification/groupings 
---------------------------------------------------------------------------------------------------------------------------
clade   SRS147919       SRS023829       SRS143070       SRS1041157      SRS054659       SRS893358       SRS097889       SRS018888       SRS015388       SRS063127       SRS1041118      SRS.. ..
k__Bacteria|p__Bacteroidetes|c__Bacteroidia|o__Bacteroidales|f__Bacteroidaceae|g__Bacteroides   74.61803        91.66601        74.50192        59.34656        0       56.26974        62... ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Ruminococcaceae|g__Faecalibacterium 9.84468 0       0.74804 4.71532 0       4.80523 5.56619 0.89963 0.12407 0       2.41817 0.3.. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Lachnospiraceae|g__Roseburia        6.34502 0       2.26259 0.3778  0       0.65847 1.02814 0.89158 0.12674 1.536   1.09499 0.3.. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Ruminococcaceae|g__Ruminococcus     2.28114 0       0       21.7474 0       5.5596  9.46814 0.25891 0       0.14911 0.25347 0  .. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Lachnospiraceae|g__Coprococcus      1.02882 0       0.04807 0.32191 0       1.28272 0.02612 0.06588 0       0.17452 0.3179  0.0.. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Eubacteriaceae|g__Eubacterium       0.83001 0       2.96475 0.16504 0       2.13926 0.06733 0.03931 0.01472 0.10997 0.60317 0.0.. ..
k__Bacteria|p__Bacteroidetes|c__Bacteroidia|o__Bacteroidales|f__Rikenellaceae|g__Alistipes      0.69843 0       0.81156 8.45622 0       8.83071 0       9.76005 7.0088  6.47799 14.4463 15... ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Oscillospiraceae|g__Oscillibacter   0.47186 0       0.51835 0.9942  0       1.44503 0       0.64727 0       0       0.95457 0  .. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Ruminococcaceae|g__Gemmiger 0.46839 0       0.02971 0       0       0.0781  0       0.29977 0       0.09069 0       0.0781  0.1.. ..
k__Bacteria|p__Firmicutes|c__Bacilli|o__Lactobacillales|f__Streptococcaceae|g__Streptococcus    0.3406  0.00135 0.00223 0.05783 0       0.06358 0.01483 0       0       0.00274 0       0  .. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Lachnospiraceae|g__Blautia  0.32476 0.3832  0.11719 0.076   0       0.37649 0.95708 0.06191 0.27504 1.05974 0.15264 0.16352 0.2.. ..
k__Bacteria|p__Actinobacteria|c__Actinobacteria|o__Bifidobacteriales|f__Bifidobacteriaceae|g__Bifidobacterium   0.32231 0.05977 0.11603 0       0       0.06994 0       0       0.0679  0.3.. ..
k__Bacteria|p__Firmicutes|c__Negativicutes|o__Veillonellales|f__Veillonellaceae|g__Dialister    0.31358 0       0       0.37454 0       0.2259  0       0       0       0.03536 0       0  .. ..
k__Bacteria|p__Firmicutes|c__Negativicutes|o__Veillonellales|f__Veillonellaceae|g__Veillonella  0.2927  0.00779 0.10531 0       0       0       0       0       0       0       0       0  .. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Lachnospiraceae|g__Lachnospira      0.25959 0       1.13577 0       0       0       0.59224 0.13289 0       0.19468 0.3305  0.1.. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Lachnospiraceae|g__Fusicatenibacter 0.25747 0       0.03026 0.03206 0       0.06028 0.65502 0       0       1.3947  0.28941 0.0.. ..
k__Bacteria|p__Proteobacteria|c__Gammaproteobacteria|o__Pasteurellales|f__Pasteurellaceae|g__Haemophilus        0.23787 0       0.01334 0.00421 0       0       0       0       0       0  .. ..
k__Bacteria|p__Firmicutes|c__Clostridia|o__Clostridiales|f__Lachnospiraceae|g__Lachnospiraceae_unclassified     0.22082 0       1.89392 0.0164  0       0.33374 2.41282 0.08454 0.31729 0.4.. ..
k__Bacteria|p__Proteobacteria|c__Betaproteobacteria|o__Burkholderiales|f__Sutterellaceae|g__Sutterella  0.19719 0       0.00715 0       0       0       0.00208 0.0626  0.00153 0       0  .. ..

----------------------------------------------------------------------
>>> Example Metadata CSV file...
----------------------------------------------------------------------
samples,conditions
SRS147919,absent
SRS023829,present
SRS143070,absent
SRS1041157,absent
SRS054659,absent
SRS893358,absent
SRS097889,absent
SRS018888,present
SRS015388,absent
SRS063127,present
SRS1041118,absent
SRS104327,absent
SRS014412,present

"#,
        desc = CLI_DESC,
        ver = CLI_VERSION
    );

    print_with_pager_or_stdout(&help_text);
}

/// Print help through a pager when available; otherwise print to stdout.
/// Cross-platform behavior:
/// - Honors $PAGER when set (common on Linux/macOS; may also be set on Windows shells).
/// - On Linux/macOS: tries `less -R`, then `more`.
/// - On Windows: tries `more` via `cmd /C more`.
/// - If spawning fails, falls back to direct printing (no panic).
fn print_with_pager_or_stdout(text: &str) {
    // If user explicitly sets PAGER, use it.
    if let Ok(pager) = std::env::var("PAGER") {
        if try_spawn_pager(&pager, &[], text) {
            return;
        }
        // If PAGER is set but fails, fall through to defaults/fallback.
    }

    // Platform defaults
    if cfg!(windows) {
        // `more` is often a shell builtin; use cmd.
        if try_spawn_pager("cmd", &["/C", "more"], text) {
            return;
        }
        // Some Windows environments may have `more.com` available directly:
        if try_spawn_pager("more", &[], text) {
            return;
        }
    } else {
        if try_spawn_pager("less", &["-R"], text) {
            return;
        }
        if try_spawn_pager("more", &[], text) {
            return;
        }
    }

    // Last resort: print directly.
    print!("{text}");
}

fn try_spawn_pager(program: &str, args: &[&str], text: &str) -> bool {
    let mut child = match std::process::Command::new(program)
        .args(args)
        .stdin(std::process::Stdio::piped())
        .spawn()
    {
        Ok(c) => c,
        Err(_) => return false,
    };

    if let Some(stdin) = child.stdin.as_mut() {
        // Best-effort write; if it fails, just fall back by returning false.
        if stdin.write_all(text.as_bytes()).is_err() {
            return false;
        }
    } else {
        return false;
    }

    // Wait best-effort; pager might exit early.
    let _ = child.wait();
    true
}

fn main() -> Result<(), Box<dyn Error>> {
    use crate::tools::{ecocart, plaw, spun, weave};

    let mut args: Vec<String> = std::env::args().collect();

    if args.len() == 1 {
        println!("rsand: Type `rsand --help` for manual.");
        return Ok(());
    }

    // Global flags (must work without subcommand)
    match args[1].as_str() {
        "--help" | "-h" => {
            print_help();
            return Ok(());
        }
        "--version" | "-V" | "-v" => {
            // You asked for -v/--version; many CLIs use -V, but we support both.
            print_version();
            return Ok(());
        }
        _ => {}
    }

    let subcmd = args[1].clone();
    args.drain(0..2); // remove "rsand" and subcommand

    match subcmd.as_str() {
        "spun" => spun::run(std::iter::once("spun".to_string()).chain(args).collect())?,
        "weave" => weave::run(std::iter::once("weave".to_string()).chain(args).collect())?,
        "ecocart" => ecocart::run(std::iter::once("ecocart".to_string()).chain(args).collect())?,
        "plaw" => plaw::run(std::iter::once("plaw".to_string()).chain(args).collect())?,
        "oss" => oss::run(std::iter::once("oss".to_string()).chain(args).collect())?,
        _ => {
            eprintln!("Unknown command: {subcmd}");
            eprintln!("Type `rsand --help` for usage.");
            // Conventional non-zero exit for CLI misuse:
            std::process::exit(2);
        }
    }

    Ok(())
}


