"""Juvenal CLI — orchestrate AI coding agents through verified phases."""

import argparse
import sys

from juvenal import __version__


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="juvenal",
        description="Who guards the agents? Orchestrate AI coding agents through verified implementation phases.",
    )
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
    parser.add_argument("--plain", action="store_true", help="Plain text output (no Rich TUI)")
    sub = parser.add_subparsers(dest="command")

    # run
    run_p = sub.add_parser("run", help="Execute a workflow")
    run_p.add_argument("workflow", help="Path to workflow YAML, directory, or bare .md file")
    run_p.add_argument("--resume", action="store_true", help="Resume from last saved state")
    run_p.add_argument("--rewind", type=int, metavar="N", help="Rewind N phases back from the resume point")
    run_p.add_argument("--rewind-to", metavar="PHASE_ID", help="Rewind to a specific phase by ID")
    run_p.add_argument("--phase", help="Start from a specific phase")
    run_p.add_argument("--max-bounces", type=int, default=999, help="Max bounces across all phases (default: 999)")
    run_p.add_argument("--backend", choices=["claude", "codex"], default="codex", help="AI backend to use")
    run_p.add_argument("--dry-run", action="store_true", help="Show what would be done without executing")
    run_p.add_argument("--working-dir", help="Working directory for the agent")
    run_p.add_argument("--state-file", help="Path to state file (default: .juvenal-state.json)")
    run_p.add_argument(
        "--backoff", type=float, default=None, help="Base backoff delay in seconds between bounces (exponential)"
    )
    run_p.add_argument("--notify", action="append", default=[], help="Webhook URL for completion/failure notifications")
    run_p.add_argument("--checker", action="append", default=[], help="Inject checker on every implement phase")
    run_p.add_argument("--implementer", help="Prepend implementer role prompt to every implement phase")
    run_p.add_argument(
        "--preserve-context-on-bounce",
        action="store_true",
        help="Resume agent session when bouncing back (preserves conversation context)",
    )

    # plan
    plan_p = sub.add_parser("plan", help="Generate a workflow from a goal description")
    plan_p.add_argument("goal", help="Goal description")
    plan_p.add_argument("-o", "--output", default="workflow.yaml", help="Output file (default: workflow.yaml)")
    plan_p.add_argument("--backend", choices=["claude", "codex"], default="codex", help="AI backend to use")
    plan_p.add_argument("--checker", action="append", default=[], help="Inject checker on every implement phase")
    plan_p.add_argument("--implementer", help="Prepend implementer role prompt to every implement phase")

    # do
    do_p = sub.add_parser("do", help="Plan + immediately run a workflow")
    do_p.add_argument("goal", help="Goal description")
    do_p.add_argument("--backend", choices=["claude", "codex"], default="codex", help="AI backend to use")
    do_p.add_argument("--max-bounces", type=int, default=999, help="Max bounces across all phases (default: 999)")
    do_p.add_argument("--checker", action="append", default=[], help="Inject checker on every implement phase")
    do_p.add_argument("--implementer", help="Prepend implementer role prompt to every implement phase")
    do_p.add_argument(
        "--preserve-context-on-bounce",
        action="store_true",
        help="Resume agent session when bouncing back (preserves conversation context)",
    )

    # status
    status_p = sub.add_parser("status", help="Show workflow progress")
    status_p.add_argument("--state-file", help="Path to state file")

    # init
    init_p = sub.add_parser("init", help="Scaffold a workflow directory")
    init_p.add_argument("directory", nargs="?", default=".", help="Directory to scaffold (default: .)")
    init_p.add_argument("--template", default="default", help="Template to use (default: default)")

    # validate
    validate_p = sub.add_parser("validate", help="Validate a workflow definition")
    validate_p.add_argument("workflow", help="Path to workflow YAML, directory, or bare .md file")

    return parser


def cmd_run(args: argparse.Namespace) -> int:
    from juvenal.engine import Engine
    from juvenal.workflow import inject_checkers, inject_implementer, load_workflow, validate_workflow

    workflow = load_workflow(args.workflow)
    if args.implementer:
        workflow = inject_implementer(workflow, args.implementer)
    if args.checker:
        workflow = inject_checkers(workflow, args.checker)
    errors = validate_workflow(workflow)
    if errors:
        print(f"Workflow validation failed with {len(errors)} error(s):")
        for err in errors:
            print(f"  - {err}")
        return 1
    if args.backend:
        workflow.backend = args.backend
    if args.max_bounces:
        workflow.max_bounces = args.max_bounces
    if args.working_dir:
        workflow.working_dir = args.working_dir
    if args.backoff is not None:
        workflow.backoff = args.backoff
    if args.notify:
        workflow.notify.extend(args.notify)

    state_file = getattr(args, "state_file", None)
    engine = Engine(
        workflow,
        resume=args.resume,
        rewind=args.rewind,
        rewind_to=args.rewind_to,
        start_phase=args.phase,
        dry_run=args.dry_run,
        state_file=state_file,
        plain=args.plain,
        preserve_context_on_bounce=args.preserve_context_on_bounce,
    )
    return engine.run()


def cmd_plan(args: argparse.Namespace) -> int:
    from juvenal.engine import plan_workflow

    plan_workflow(args.goal, args.output, args.backend, plain=args.plain)
    if args.implementer:
        _inject_implementer_into_yaml(args.output, args.implementer)
    if args.checker:
        _inject_checkers_into_yaml(args.output, args.checker)
    return 0


def _inject_checkers_into_yaml(yaml_path: str, checker_specs: list[str]) -> None:
    """Post-process a generated YAML file to append checkers: entries to each implement phase."""
    import yaml

    from juvenal.workflow import parse_checker_string

    with open(yaml_path) as f:
        data = yaml.safe_load(f)

    parsed = [parse_checker_string(s) for s in checker_specs]

    for phase in data.get("phases", []):
        if phase.get("type", "implement") == "implement":
            existing = phase.get("checkers", [])
            phase["checkers"] = existing + parsed

    with open(yaml_path, "w") as f:
        yaml.dump(data, f, default_flow_style=False, sort_keys=False)


def _inject_implementer_into_yaml(yaml_path: str, role: str) -> None:
    """Post-process a generated YAML file to prepend an implementer role prompt to each implement phase."""
    import yaml

    from juvenal.workflow import _load_implementer_prompt

    preamble = _load_implementer_prompt(role)

    with open(yaml_path) as f:
        data = yaml.safe_load(f)

    for phase in data.get("phases", []):
        if phase.get("type", "implement") == "implement":
            phase["prompt"] = preamble + phase.get("prompt", "")

    with open(yaml_path, "w") as f:
        yaml.dump(data, f, default_flow_style=False, sort_keys=False)


def cmd_do(args: argparse.Namespace) -> int:
    import tempfile

    from juvenal.engine import Engine, plan_workflow
    from juvenal.workflow import inject_checkers, inject_implementer, load_workflow

    with tempfile.NamedTemporaryFile(suffix=".yaml", delete=False, mode="w") as f:
        plan_workflow(args.goal, f.name, args.backend, plain=args.plain)
        workflow = load_workflow(f.name)

    if args.implementer:
        workflow = inject_implementer(workflow, args.implementer)
    if args.checker:
        workflow = inject_checkers(workflow, args.checker)
    if args.backend:
        workflow.backend = args.backend
    if args.max_bounces:
        workflow.max_bounces = args.max_bounces

    engine = Engine(workflow, plain=args.plain, preserve_context_on_bounce=args.preserve_context_on_bounce)
    return engine.run()


def cmd_status(args: argparse.Namespace) -> int:
    from juvenal.state import PipelineState

    state = PipelineState.load(args.state_file)
    state.print_status()
    return 0


def cmd_init(args: argparse.Namespace) -> int:
    from juvenal.workflow import scaffold_workflow

    scaffold_workflow(args.directory, args.template)
    return 0


def cmd_validate(args: argparse.Namespace) -> int:
    from juvenal.workflow import load_workflow, validate_workflow

    workflow = load_workflow(args.workflow)
    errors = validate_workflow(workflow)
    if errors:
        print(f"Validation found {len(errors)} error(s):")
        for err in errors:
            print(f"  - {err}")
        return 1
    print(f"Workflow {workflow.name!r} is valid ({len(workflow.phases)} phases).")
    return 0


def main(argv: list[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    if not args.command:
        parser.print_help()
        return 1

    handlers = {
        "run": cmd_run,
        "plan": cmd_plan,
        "do": cmd_do,
        "status": cmd_status,
        "init": cmd_init,
        "validate": cmd_validate,
    }
    return handlers[args.command](args)


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