"""Run from an unpacked release: python -m ligninqc --help."""
import argparse
import json
import sys
from pathlib import Path
from . import __version__
from .analysis import InputError, compare_table, digest, read_csv, summarize


def write_outputs(out, result, title):
    from .report import generic_report
    serialized = json.dumps(result, ensure_ascii=False, indent=2, allow_nan=False) + "\n"
    rendered = generic_report(result, title)
    out = Path(out)
    out.mkdir(parents=True, exist_ok=True)
    (out / "results.json").write_text(serialized, encoding="utf-8")
    (out / "report.html").write_text(rendered, encoding="utf-8")
    return out


def main(argv=None):
    parser = argparse.ArgumentParser(description="LigninQC — offline reanalysis of published lignin data. Python 3.10+, no third-party dependencies.")
    parser.add_argument("--version", action="version", version=__version__)
    commands = parser.add_subparsers(dest="command", required=True)
    reproduce = commands.add_parser("reproduce", help="Recompute both bundled real cases and their source checks.")
    reproduce.add_argument("--out", default="results")
    analyze = commands.add_parser("analyze", help="Analyze a CSV with explicit descriptor, response and identifier columns.")
    analyze.add_argument("--csv", required=True, type=Path)
    analyze.add_argument("--x", required=True, help="Descriptor column; use a single unit/state definition.")
    analyze.add_argument("--y", required=True, help="Response column; do not mix assays or normalizations.")
    analyze.add_argument("--id", default="compound_id")
    analyze.add_argument("--direction", choices=["negative", "positive"], required=True)
    analyze.add_argument("--x-unit", required=True)
    analyze.add_argument("--y-unit", required=True)
    analyze.add_argument("--out", required=True)
    compare = commands.add_parser("compare", help="Test a numerical greater/less claim across aligned table rows.")
    compare.add_argument("--csv", required=True, type=Path)
    compare.add_argument("--index", required=True)
    compare.add_argument("--left", required=True)
    compare.add_argument("--right", required=True)
    compare.add_argument("--expected", choices=["greater", "less"], required=True)
    compare.add_argument("--min", type=float)
    compare.add_argument("--max", type=float)
    compare.add_argument("--unit", required=True)
    compare.add_argument("--out", required=True)
    verify = commands.add_parser("verify", help="Verify packaged file hashes, source transcription and frozen numerical results.")
    for command in (reproduce, analyze, compare):
        command.add_argument("--force", action="store_true", help="Replace generated output files in an existing output directory.")
    args = parser.parse_args(argv)
    try:
        if args.command == "verify":
            from .cases import verify_release
            print(json.dumps(verify_release(), ensure_ascii=False, indent=2))
            return 0
        for name in ("x_unit", "y_unit", "unit"):
            if hasattr(args, name) and not getattr(args, name).strip():
                raise InputError("Units must be nonempty; use 'dimensionless' when appropriate.")
        out = Path(args.out).resolve()
        if out.exists() and (not out.is_dir() or any(out.iterdir())) and not args.force:
            raise InputError("Output directory is not empty. Choose a new --out directory or use --force for generated outputs.")
        if args.command == "reproduce":
            from .cases import reproduce_cases
            from .report import case_report
            result = reproduce_cases()
            out = write_outputs(out, result, "LigninQC: published-data reanalysis")
            (out / "report.html").write_text(case_report(result), encoding="utf-8")
        elif args.command == "analyze":
            result = {"schema_version": 1, "software_version": __version__, "task": "csv_analysis",
                      "input": {"file": args.csv.name, "sha256": digest(args.csv)},
                      "units": {"descriptor": args.x_unit, "response": args.y_unit},
                      "analysis": summarize(read_csv(args.csv), args.x, args.y, args.id, args.direction)}
            write_outputs(out, result, "LigninQC: descriptor and response")
        else:
            if args.min is not None and args.max is not None and args.min > args.max:
                raise InputError("Minimum index cannot exceed maximum index.")
            result = {"schema_version": 1, "software_version": __version__, "task": "table_comparison",
                      "input": {"file": args.csv.name, "sha256": digest(args.csv)}, "unit": args.unit,
                      "comparison": compare_table(read_csv(args.csv), args.index, args.left, args.right, args.min, args.max, args.expected)}
            write_outputs(out, result, "LigninQC: numerical table comparison")
        print(f"Completed: {out / 'report.html'}\nMachine-readable results: {out / 'results.json'}")
        return 0
    except (InputError, OSError, ValueError, OverflowError) as exc:
        print(f"LigninQC input/verification error: {exc}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    sys.exit(main())
