"""Helper script for invariant_cli.robot smoke tests.

Each subcommand is a self-contained check that prints a sentinel on success.
"""

from __future__ import annotations

import sys
from pathlib import Path
from unittest.mock import patch

_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from typer.testing import CliRunner  # noqa: E402

from cleveragents.application.services.invariant_service import (  # noqa: E402
    InvariantService,
)
from cleveragents.cli.commands.invariant import app as invariant_app  # noqa: E402

runner = CliRunner()


def _fresh_service() -> InvariantService:
    """Create a fresh in-memory service for each test."""
    return InvariantService()


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


def add_global() -> None:
    svc = _fresh_service()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(
            invariant_app, ["add", "--global", "Never delete production data"]
        )
    if result.exit_code == 0 and "Invariant added" in result.stdout:
        print("invariant-add-global-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def add_project() -> None:
    svc = _fresh_service()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(
            invariant_app, ["add", "--project", "myapp", "All API changes need tests"]
        )
    if result.exit_code == 0 and "Invariant added" in result.stdout:
        print("invariant-add-project-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def add_plan() -> None:
    svc = _fresh_service()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(
            invariant_app,
            ["add", "--plan", "01TESTPLANID00000000000000", "Use Python 3.13"],
        )
    if result.exit_code == 0 and "Invariant added" in result.stdout:
        print("invariant-add-plan-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def add_action() -> None:
    svc = _fresh_service()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(
            invariant_app,
            ["add", "--action", "local/code-coverage", "Min 80% coverage"],
        )
    if result.exit_code == 0 and "Invariant added" in result.stdout:
        print("invariant-add-action-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def list_empty() -> None:
    svc = _fresh_service()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(invariant_app, ["list"])
    if result.exit_code == 0 and "No invariants found" in result.stdout:
        print("invariant-list-empty-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def list_filter() -> None:
    svc = _fresh_service()
    svc.add_invariant("Global rule", scope=_scope("global"), source_name="system")
    svc.add_invariant("Project rule", scope=_scope("project"), source_name="myapp")
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(invariant_app, ["list", "--global"])
    if result.exit_code == 0 and "Global rule" in result.stdout:
        print("invariant-list-filter-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def remove() -> None:
    svc = _fresh_service()
    inv = svc.add_invariant(
        "To be removed", scope=_scope("global"), source_name="system"
    )
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(invariant_app, ["remove", "--yes", inv.id])
    if result.exit_code == 0 and "Invariant removed" in result.stdout:
        print("invariant-remove-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def scope_conflict() -> None:
    svc = _fresh_service()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(
            invariant_app,
            ["add", "--global", "--project", "myapp", "Conflicting scopes"],
        )
    # Should fail with a bad parameter error
    if result.exit_code != 0:
        print("invariant-scope-conflict-ok")
    else:
        print(f"FAIL: exit={result.exit_code} out={result.stdout}")
        sys.exit(1)


def list_json() -> None:
    svc = _fresh_service()
    svc.add_invariant("JSON test rule", scope=_scope("global"), source_name="system")
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc):
        result = runner.invoke(invariant_app, ["list", "--format", "json"])
    if result.exit_code == 0:
        # Verify output is valid JSON-like
        output = result.stdout.strip()
        if "JSON test rule" in output:
            print("invariant-list-json-ok")
            return
    print(f"FAIL: exit={result.exit_code} out={result.stdout}")
    sys.exit(1)


def _scope(name: str):
    from cleveragents.domain.models.core.invariant import InvariantScope

    return InvariantScope(name)


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

COMMANDS = {
    "add-global": add_global,
    "add-project": add_project,
    "add-plan": add_plan,
    "add-action": add_action,
    "list-empty": list_empty,
    "list-filter": list_filter,
    "remove": remove,
    "scope-conflict": scope_conflict,
    "list-json": list_json,
}

if __name__ == "__main__":
    cmd = sys.argv[1] if len(sys.argv) > 1 else None
    if cmd not in COMMANDS:
        print(f"Unknown command: {cmd}", file=sys.stderr)
        print(f"Available: {', '.join(sorted(COMMANDS))}", file=sys.stderr)
        sys.exit(2)
    COMMANDS[cmd]()
