"""CLI entry point for regimen."""

from __future__ import annotations

import argparse
import sys
import time
from pathlib import Path

from regimen.parser import parse, Step
from regimen.runner import run_command
from regimen.session import Session, StepResult, make_slug


def _load_session(slug: str) -> Session:
    """Load a session or exit with an error."""
    try:
        return Session.load(slug)
    except FileNotFoundError as exc:
        print(f"Error: {exc}", file=sys.stderr)
        sys.exit(1)


def _run_step(session: Session, step: Step, default_timeout: int) -> StepResult:
    """Run a step's commands and return the result."""
    outputs = [
        run_command(cmd.code, timeout=cmd.timeout or default_timeout)
        for cmd in step.commands
    ]

    prev_count = 0
    if len(session.results) > session.current_step:
        prev_count = session.results[session.current_step].run_count

    return StepResult(outputs=outputs, run_count=prev_count + 1)


def _save_result(session: Session, result: StepResult) -> None:
    """Store result and advance the cursor."""
    if len(session.results) > session.current_step:
        session.results[session.current_step] = result
    else:
        session.results.append(result)
    session.current_step += 1
    if session.current_step >= session.total_steps:
        session.status = "completed"
    session.save()


def _format_step(index: int, total: int, step: Step, result: StepResult) -> str:
    """Format a step result for display."""
    lines = []
    header = f"Step {index + 1}/{total}: {step.title}"
    if result.run_count > 1:
        header += f" (run #{result.run_count})"
    lines.append(header)
    lines.append("")
    if step.prose:
        lines.append(step.prose)
        lines.append("")
    for out in result.outputs:
        lines.append(f"$ {out['command']}")
        if out["stdout"]:
            lines.append(out["stdout"].rstrip())
        if out["stderr"]:
            lines.append(f"[stderr] {out['stderr'].rstrip()}")
        lines.append(f"[exit {out['exit_code']}]")
        lines.append("")
    return "\n".join(lines)


def cmd_start(args: argparse.Namespace) -> None:
    """Start a new regimen session."""
    path = Path(args.file)
    if not path.exists():
        print(f"Error: file not found: {args.file}", file=sys.stderr)
        sys.exit(1)

    regimen = parse(path.read_text())
    if not regimen.steps:
        print("Error: no steps found in file", file=sys.stderr)
        sys.exit(1)

    slug = make_slug(args.file)
    session = Session(
        slug=slug,
        file_path=str(path.resolve()),
        regimen_title=regimen.title,
        total_steps=len(regimen.steps),
        created_at=time.time(),
    )
    session.save()

    print(f"Session: {slug}")
    print(f"Regimen: {regimen.title}")
    print(f"Steps: {len(regimen.steps)}")
    for i, step in enumerate(regimen.steps):
        print(f"  {i + 1}. {step.title}")


def cmd_step(args: argparse.Namespace) -> None:
    """Run the next step in the session."""
    session = _load_session(args.slug)

    if session.status != "active":
        print(f"Session is {session.status}.", file=sys.stderr)
        sys.exit(1)

    if session.current_step >= session.total_steps:
        session.status = "completed"
        session.save()
        print("All steps completed.")
        return

    regimen = parse(Path(session.file_path).read_text())
    step = regimen.steps[session.current_step]
    result = _run_step(session, step, args.timeout)
    _save_result(session, result)
    print(_format_step(session.current_step - 1, session.total_steps, step, result))


def cmd_rerun(args: argparse.Namespace) -> None:
    """Rerun the most recently executed step."""
    session = _load_session(args.slug)

    if session.current_step == 0:
        print("No step has been run yet.", file=sys.stderr)
        sys.exit(1)

    session.current_step -= 1

    regimen = parse(Path(session.file_path).read_text())
    step = regimen.steps[session.current_step]
    result = _run_step(session, step, args.timeout)
    _save_result(session, result)
    print(_format_step(session.current_step - 1, session.total_steps, step, result))


def cmd_status(args: argparse.Namespace) -> None:
    """Show session status."""
    session = _load_session(args.slug)

    print(f"Session: {session.slug}")
    print(f"Regimen: {session.regimen_title}")
    print(f"Status: {session.status}")
    print(f"Progress: {session.current_step}/{session.total_steps}")
    print()

    regimen = parse(Path(session.file_path).read_text())
    for i, step in enumerate(regimen.steps):
        if i < len(session.results):
            r = session.results[i]
            failed = any(o["exit_code"] != 0 for o in r.outputs)
            marker = "FAIL" if failed else "PASS"
            runs = f" (x{r.run_count})" if r.run_count > 1 else ""
            print(f"  [{marker}] {i + 1}. {step.title}{runs}")
        elif i == session.current_step:
            print(f"  [ >> ] {i + 1}. {step.title}")
        else:
            print(f"  [    ] {i + 1}. {step.title}")


def cmd_abort(args: argparse.Namespace) -> None:
    """Abort a session."""
    session = _load_session(args.slug)
    session.status = "aborted"
    session.save()
    print(f"Session {session.slug} aborted at step {session.current_step}/{session.total_steps}.")


def cmd_list(args: argparse.Namespace) -> None:
    """List all sessions."""
    session_dir = Path(".regimen")
    if not session_dir.exists():
        print("No sessions found.")
        return

    files = sorted(session_dir.glob("*.json"))
    if not files:
        print("No sessions found.")
        return

    for f in files:
        try:
            s = Session.load(f.stem)
            print(f"  {s.slug}  [{s.status}]  {s.current_step}/{s.total_steps}  {s.regimen_title}")
        except Exception:
            pass


def main() -> None:
    """Entry point."""
    ap = argparse.ArgumentParser(prog="regimen", description="Step-through verification runner")
    sub = ap.add_subparsers(dest="command")

    p_start = sub.add_parser("start", help="Start a new session from a markdown file")
    p_start.add_argument("file", help="Path to the regimen markdown file")

    p_step = sub.add_parser("step", help="Run the next step")
    p_step.add_argument("slug", help="Session slug")
    p_step.add_argument("--timeout", type=int, default=30, help="Command timeout in seconds")

    p_rerun = sub.add_parser("rerun", help="Rerun the last step")
    p_rerun.add_argument("slug", help="Session slug")
    p_rerun.add_argument("--timeout", type=int, default=30, help="Command timeout in seconds")

    p_status = sub.add_parser("status", help="Show session status")
    p_status.add_argument("slug", help="Session slug")

    p_abort = sub.add_parser("abort", help="Abort a session")
    p_abort.add_argument("slug", help="Session slug")

    sub.add_parser("list", help="List all sessions")

    args = ap.parse_args()
    if not args.command:
        ap.print_help()
        sys.exit(1)

    dispatch = {
        "start": cmd_start,
        "step": cmd_step,
        "rerun": cmd_rerun,
        "status": cmd_status,
        "abort": cmd_abort,
        "list": cmd_list,
    }
    dispatch[args.command](args)


if __name__ == "__main__":
    main()
