"""Command-line interface for planning, running, and validating studies."""

from __future__ import annotations

import argparse
import importlib
import json
from pathlib import Path
from typing import Any

from .io import atomic_write_json
from .plan import build_trial_plan, load_document, validate_trial_plan
from .runner import run_trial_plan
from .validate import validate_batch


def _load_executor(specification: str):
    if ":" not in specification:
        raise ValueError("executor must use module:function syntax")
    module_name, attribute_name = specification.split(":", 1)
    module = importlib.import_module(module_name)
    executor = getattr(module, attribute_name)
    if not callable(executor):
        raise TypeError(f"{specification} is not callable")
    return executor


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Reproducible experiment runner")
    subparsers = parser.add_subparsers(dest="command", required=True)

    plan_parser = subparsers.add_parser("plan", help="expand a study specification")
    plan_parser.add_argument("--spec", type=Path, required=True)
    plan_parser.add_argument("--output", type=Path, required=True)

    run_parser = subparsers.add_parser("run", help="execute an immutable plan")
    run_parser.add_argument("--plan", type=Path, required=True)
    run_parser.add_argument("--batch-dir", type=Path, required=True)
    run_parser.add_argument("--executor", default=None)
    run_parser.add_argument("--dry-run", action="store_true")
    run_parser.add_argument("--no-resume", action="store_true")
    run_parser.add_argument("--stop-on-error", action="store_true")
    run_parser.add_argument("--max-trials", type=int, default=None)

    validate_parser = subparsers.add_parser("validate", help="validate a batch")
    validate_parser.add_argument("--batch-dir", type=Path, required=True)
    validate_parser.add_argument("--allow-incomplete", action="store_true")
    return parser


def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    result: dict[str, Any]
    if args.command == "plan":
        plan = build_trial_plan(load_document(args.spec))
        atomic_write_json(args.output, plan)
        result = {
            "kind": "plan_summary",
            "schema_version": plan["schema_version"],
            "study_id": plan["study_id"],
            "split": plan["split"],
            "pair_count": plan["pair_count"],
            "trial_count": plan["trial_count"],
            "plan_hash": plan["plan_hash"],
            "output": str(args.output.resolve()),
        }
    elif args.command == "run":
        plan = load_document(args.plan)
        validate_trial_plan(plan)
        executor = None if args.executor is None else _load_executor(args.executor)
        result = run_trial_plan(
            plan,
            args.batch_dir,
            executor,
            resume=not args.no_resume,
            dry_run=args.dry_run,
            continue_on_error=not args.stop_on_error,
            max_trials=args.max_trials,
        )
    else:
        result = validate_batch(
            args.batch_dir,
            require_complete=not args.allow_incomplete,
        )
    print(json.dumps(result, indent=2, ensure_ascii=False, sort_keys=True))
    return 0 if result.get("valid", True) else 1


if __name__ == "__main__":
    raise SystemExit(main())
