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

Each subcommand exercises the wired A2A facade with lightweight mock
services and prints a sentinel on success.
"""

from __future__ import annotations

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

# Ensure local source tree is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from cleveragents.a2a.errors import (  # noqa: E402
    map_domain_error,
)
from cleveragents.a2a.events import A2aEventQueue  # noqa: E402
from cleveragents.a2a.facade import A2aLocalFacade  # noqa: E402
from cleveragents.a2a.models import A2aRequest  # noqa: E402
from cleveragents.core.exceptions import (  # noqa: E402
    BusinessRuleViolation,
    PlanError,
    ResourceNotFoundError,
    ValidationError,
)

# ---------------------------------------------------------------------------
# Mock helpers
# ---------------------------------------------------------------------------


class _MockSession:
    def __init__(self) -> None:
        self.session_id = "INTEG-SESSION-001"


class _MockPlanIdentity:
    def __init__(self) -> None:
        self.plan_id = "INTEG-PLAN-001"


class _MockPlan:
    def __init__(self) -> None:
        self.identity = _MockPlanIdentity()
        self.phase = MagicMock()
        self.phase.value = "strategize"
        self.state = MagicMock()
        self.state.value = "queued"


class _MockToolSpec:
    def __init__(self, name: str, description: str) -> None:
        self.name = name
        self.description = description


class _MockResource:
    def __init__(self) -> None:
        self.resource_id = "RES-001"
        self.name = "test-resource"
        self.resource_type_name = "git-checkout"


def _mock_session_service() -> MagicMock:
    svc = MagicMock()
    svc.create.return_value = _MockSession()
    svc.delete.return_value = None
    return svc


def _mock_plan_lifecycle_service() -> MagicMock:
    svc = MagicMock()
    svc.use_action.return_value = _MockPlan()
    svc.execute_plan.return_value = _MockPlan()
    svc.get_plan.return_value = _MockPlan()
    svc.apply_plan.return_value = _MockPlan()
    return svc


def _mock_tool_registry() -> MagicMock:
    reg = MagicMock()
    reg.list_tools.return_value = [
        _MockToolSpec("local/echo", "Echo tool"),
    ]
    return reg


def _mock_resource_registry_service() -> MagicMock:
    svc = MagicMock()
    svc.list_resources.return_value = [_MockResource()]
    return svc


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


def wired_session_create() -> None:
    """Dispatch session.create through a wired facade."""
    facade = A2aLocalFacade(services={"session_service": _mock_session_service()})
    resp = facade.dispatch(A2aRequest(method="session.create"))
    if resp.result is not None and resp.result["session_id"] == "INTEG-SESSION-001":
        print("a2a-wired-session-create-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_session_close() -> None:
    """Dispatch session.close through a wired facade."""
    facade = A2aLocalFacade(services={"session_service": _mock_session_service()})
    resp = facade.dispatch(
        A2aRequest(
            method="session.close",
            params={"session_id": "INTEG-SESSION-001"},
        )
    )
    if resp.result is not None and resp.result["status"] == "closed":
        print("a2a-wired-session-close-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_plan_create() -> None:
    """Dispatch plan.create through a wired facade."""
    facade = A2aLocalFacade(
        services={"plan_lifecycle_service": _mock_plan_lifecycle_service()}
    )
    resp = facade.dispatch(
        A2aRequest(
            method="plan.create",
            params={"action_name": "local/test"},
        )
    )
    if resp.result is not None and resp.result["plan_id"] == "INTEG-PLAN-001":
        print("a2a-wired-plan-create-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_plan_status() -> None:
    """Dispatch plan.status through a wired facade."""
    facade = A2aLocalFacade(
        services={"plan_lifecycle_service": _mock_plan_lifecycle_service()}
    )
    resp = facade.dispatch(
        A2aRequest(
            method="plan.status",
            params={"plan_id": "INTEG-PLAN-001"},
        )
    )
    if resp.result is not None and resp.result["phase"] == "strategize":
        print("a2a-wired-plan-status-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_registry_tools() -> None:
    """Dispatch registry.list_tools through a wired facade."""
    facade = A2aLocalFacade(services={"tool_registry": _mock_tool_registry()})
    resp = facade.dispatch(A2aRequest(method="registry.list_tools"))
    tools = resp.result.get("tools", []) if resp.result is not None else []
    if resp.result is not None and len(tools) == 1:
        print("a2a-wired-registry-tools-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_registry_resources() -> None:
    """Dispatch registry.list_resources through a wired facade."""
    facade = A2aLocalFacade(
        services={"resource_registry_service": _mock_resource_registry_service()}
    )
    resp = facade.dispatch(A2aRequest(method="registry.list_resources"))
    resources = resp.result.get("resources", []) if resp.result is not None else []
    if resp.result is not None and len(resources) == 1:
        print("a2a-wired-registry-resources-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_context_stub() -> None:
    """Dispatch context.get and verify stub response."""
    facade = A2aLocalFacade()
    resp = facade.dispatch(A2aRequest(method="context.get"))
    if resp.result is not None and resp.result.get("stub") is True:
        print("a2a-wired-context-stub-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_event_subscribe() -> None:
    """Dispatch event.subscribe through a wired facade."""
    queue = A2aEventQueue()
    facade = A2aLocalFacade(services={"event_queue": queue})
    resp = facade.dispatch(A2aRequest(method="event.subscribe"))
    sub_id = resp.result.get("subscription_id", "") if resp.result is not None else ""
    if resp.result is not None and sub_id:
        print("a2a-wired-event-subscribe-ok")
    else:
        print(f"FAIL: {resp}", file=sys.stderr)
        sys.exit(1)


def wired_error_mapping() -> None:
    """Verify domain-to-A2A error code mapping."""
    from cleveragents.a2a import errors as _a2a_errors

    cases: list[tuple[Exception, int]] = [
        (
            ResourceNotFoundError(resource_type="x", resource_id="1"),
            _a2a_errors.NOT_FOUND,
        ),
        (ValidationError("bad"), _a2a_errors.VALIDATION_ERROR),
        (PlanError("fail"), _a2a_errors.PLAN_ERROR),
        (BusinessRuleViolation("invalid"), _a2a_errors.INVALID_STATE),
    ]
    for exc, expected_code in cases:
        code, _ = map_domain_error(exc)
        if code != expected_code:
            print(
                f"FAIL: expected {expected_code} for {type(exc).__name__}, got {code}",
                file=sys.stderr,
            )
            sys.exit(1)
    print("a2a-wired-error-mapping-ok")


# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------

_COMMANDS: dict[str, object] = {
    "wired-session-create": wired_session_create,
    "wired-session-close": wired_session_close,
    "wired-plan-create": wired_plan_create,
    "wired-plan-status": wired_plan_status,
    "wired-registry-tools": wired_registry_tools,
    "wired-registry-resources": wired_registry_resources,
    "wired-context-stub": wired_context_stub,
    "wired-event-subscribe": wired_event_subscribe,
    "wired-error-mapping": wired_error_mapping,
}


def main() -> None:
    if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
        cmds = "|".join(_COMMANDS)
        print(f"Usage: {sys.argv[0]} <{cmds}>", file=sys.stderr)
        sys.exit(2)
    fn = _COMMANDS[sys.argv[1]]
    if callable(fn):
        fn()


if __name__ == "__main__":
    main()
