"""Helper script for tdd_invariant_persistence.robot (bug #1022).

Exercises InvariantService cross-invocation persistence at the integration
level.  Each subcommand simulates a fresh CLI process by creating a new
InvariantService instance, mirroring how the real CLI works (each
``python -m cleveragents`` call gets its own service).

Bug #1022: InvariantService stores invariants in an in-memory dict only.
Invariants added in one CLI invocation are lost when the process exits.

This helper is called from Robot Framework via ``Run Process``.
"""

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
from cleveragents.core.exceptions import NotFoundError  # noqa: E402
from cleveragents.domain.models.core.invariant import InvariantScope  # noqa: E402

runner = CliRunner()


def add_then_list_project() -> None:
    """Add a project invariant in invocation 1, list in invocation 2.

    Simulates two separate CLI invocations by using fresh InvariantService
    instances.  The list in invocation 2 should show the invariant added
    in invocation 1 — but it won't because of bug #1022.
    """
    # Invocation 1: add
    svc1 = InvariantService()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc1):
        add_result = runner.invoke(
            invariant_app,
            ["add", "--project", "local/test-proj", "Must validate inputs"],
        )
    if add_result.exit_code != 0:
        print(f"FAIL-ADD: exit={add_result.exit_code} out={add_result.output}")
        sys.exit(1)

    # Invocation 2: list (fresh service — simulates new process)
    svc2 = InvariantService()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc2):
        list_result = runner.invoke(
            invariant_app, ["list", "--project", "local/test-proj"]
        )

    # The list output should contain the invariant — if it doesn't, bug exists
    if "Must validate inputs" in list_result.output:
        print("invariant-persist-project-ok")
    else:
        print(
            f"FAIL-PERSIST: invariant not found in second invocation. "
            f"output:\n{list_result.output}"
        )
        sys.exit(1)


def add_then_list_global() -> None:
    """Add a global invariant in invocation 1, list in invocation 2."""
    svc1 = InvariantService()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc1):
        add_result = runner.invoke(
            invariant_app,
            ["add", "--global", "Never expose credentials"],
        )
    if add_result.exit_code != 0:
        print(f"FAIL-ADD: exit={add_result.exit_code} out={add_result.output}")
        sys.exit(1)

    svc2 = InvariantService()
    with patch("cleveragents.cli.commands.invariant._get_service", return_value=svc2):
        list_result = runner.invoke(invariant_app, ["list", "--global"])

    if "Never expose credentials" in list_result.output:
        print("invariant-persist-global-ok")
    else:
        print(
            f"FAIL-PERSIST: invariant not found in second invocation. "
            f"output:\n{list_result.output}"
        )
        sys.exit(1)


def add_then_remove_cross_instance() -> None:
    """Add invariant in instance 1, remove by ID in instance 2."""
    svc1 = InvariantService()
    inv = svc1.add_invariant(
        text="Temporary constraint",
        scope=InvariantScope.PROJECT,
        source_name="local/temp",
    )
    inv_id = inv.id

    # Fresh instance — simulates new CLI process
    svc2 = InvariantService()
    try:
        svc2.remove_invariant(inv_id)
        print("invariant-cross-remove-ok")
    except NotFoundError as exc:
        print(f"FAIL-REMOVE: {type(exc).__name__}: {exc}")
        sys.exit(1)


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

COMMANDS = {
    "add-then-list-project": add_then_list_project,
    "add-then-list-global": add_then_list_global,
    "add-then-remove-cross": add_then_remove_cross_instance,
}

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]()
