"""Helper script for tdd_plan_explain_plan_id.robot integration tests.

TDD regression guard for issue #6325 / bug #968:
``plan explain`` must reject a plan_id with rc=1.  The old fallback that
resolved a plan_id to its root decision via ``list_decisions`` has been
removed.  Passing a plan_id (which is not a decision ULID) should produce
rc=1 and an error message containing "not found".
"""

from __future__ import annotations

import os
import shutil
import subprocess
import sys
import tempfile
from collections.abc import Callable
from pathlib import Path
from typing import NoReturn

# Ensure local source tree AND robot/ directory are importable.
_ROOT: Path = Path(__file__).resolve().parents[1]
_SRC: str = str(_ROOT / "src")
_ROBOT: str = str(_ROOT / "robot")
for _p in (_SRC, _ROBOT):
    if _p not in sys.path:
        sys.path.insert(0, _p)

from ulid import ULID  # noqa: E402

from cleveragents.application.container import reset_container  # noqa: E402
from cleveragents.config.settings import Settings  # noqa: E402

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _fail(msg: str) -> NoReturn:
    """Print failure message to stderr and exit with code 1."""
    print(msg, file=sys.stderr)
    sys.exit(1)


def _make_subprocess_env() -> dict[str, str]:
    """Build environment for subprocess calls with ``NO_COLOR=1``."""
    env: dict[str, str] = os.environ.copy()
    env["NO_COLOR"] = "1"
    return env


def _setup_isolated_home() -> tuple[str, str | None]:
    """Create and activate an isolated CLEVERAGENTS_HOME for this run."""
    tmp_home = tempfile.mkdtemp(prefix="tdd_plan_explain_968_")
    previous_home = os.environ.get("CLEVERAGENTS_HOME")
    os.environ["CLEVERAGENTS_HOME"] = tmp_home
    reset_container()
    Settings._instance = None
    return tmp_home, previous_home


def _restore_isolated_home(tmp_home: str, previous_home: str | None) -> None:
    """Restore prior environment and clean up temporary home directory."""
    if previous_home is not None:
        os.environ["CLEVERAGENTS_HOME"] = previous_home
    else:
        os.environ.pop("CLEVERAGENTS_HOME", None)
    reset_container()
    Settings._instance = None
    shutil.rmtree(tmp_home, ignore_errors=True)


def _run_plan_explain(plan_id: str) -> subprocess.CompletedProcess[str]:
    """Run ``plan explain <plan_id>`` via subprocess."""
    try:
        return subprocess.run(
            [
                sys.executable,
                "-m",
                "cleveragents",
                "plan",
                "explain",
                plan_id,
                "--format",
                "plain",
            ],
            capture_output=True,
            text=True,
            timeout=45,
            cwd=str(_ROOT),
            env=_make_subprocess_env(),
        )
    except subprocess.TimeoutExpired:
        _fail(f"plan explain {plan_id} timed out after 45 seconds.")


# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------


def explain_with_plan_id() -> None:
    """Verify that ``plan explain <plan_id>`` is rejected with rc=1.

    Issue #6325: the command no longer falls back to list_decisions when the
    argument is not a decision ULID.  Passing a plan_id should exit with rc=1.
    """
    tmp_home, previous_home = _setup_isolated_home()
    try:
        plan_id: str = str(ULID())
        result: subprocess.CompletedProcess[str] = _run_plan_explain(plan_id)

        if result.returncode != 1:
            _fail(
                f"plan explain {plan_id} rc={result.returncode}, expected rc=1. "
                f"stdout: {result.stdout}\n"
                f"stderr: {result.stderr}\n"
                f"Issue #6325: the command should reject a plan_id with rc=1."
            )

        print("tdd-plan-explain-plan-id-rejected")
    finally:
        _restore_isolated_home(tmp_home, previous_home)


def explain_plan_id_shows_error() -> None:
    """Verify that the error output for a plan_id contains 'not found'.

    Issue #6325: the error message should clearly indicate the identifier
    was not found as a decision.
    """
    tmp_home, previous_home = _setup_isolated_home()
    try:
        plan_id: str = str(ULID())
        result: subprocess.CompletedProcess[str] = _run_plan_explain(plan_id)

        if result.returncode != 1:
            _fail(
                f"plan explain {plan_id} rc={result.returncode}, expected rc=1. "
                f"stdout: {result.stdout}\n"
                f"stderr: {result.stderr}"
            )

        combined: str = (result.stdout + result.stderr).lower()
        if "not found" not in combined:
            _fail(
                f"Expected 'not found' in plan explain output. "
                f"stdout: {result.stdout}\nstderr: {result.stderr}"
            )

        print("tdd-plan-explain-plan-id-error-ok")
    finally:
        _restore_isolated_home(tmp_home, previous_home)


# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------

_COMMANDS: dict[str, Callable[[], None]] = {
    "explain-with-plan-id": explain_with_plan_id,
    "explain-plan-id-shows-error": explain_plan_id_shows_error,
}

if __name__ == "__main__":
    if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
        print(
            f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>",
            file=sys.stderr,
        )
        sys.exit(1)
    cmd: Callable[[], None] = _COMMANDS[sys.argv[1]]
    cmd()
