"""Step definitions for a2a_facade_optional_param_validation.feature.

Covers input validation for optional parameters in A2A facade handlers:

| Handler                        | Parameter   | Validation                          |
|--------------------------------|-------------|-------------------------------------|
| _handle_registry_list_tools    | namespace   | non-empty string or None            |
| _handle_registry_list_resources| type_name   | non-empty string or None            |
| _handle_plan_create            | arguments   | dict or None                        |
| _handle_plan_create            | created_by  | non-empty string or None            |
"""

from __future__ import annotations

import json
from typing import Any
from unittest.mock import MagicMock

from behave import given, then, use_step_matcher, when
from behave.runner import Context

try:
    from cleveragents.a2a.facade import A2aLocalFacade
    from cleveragents.a2a.models import A2aRequest
except ImportError:
    pass  # a2a module not available

use_step_matcher("re")


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


class _MockPlanIdentity:
    """Minimal PlanIdentity stub."""

    def __init__(self, plan_id: str = "MOCK-PLAN-001") -> None:
        self.plan_id = plan_id


class _MockPlan:
    """Minimal plan stub."""

    def __init__(self, plan_id: str = "MOCK-PLAN-001") -> None:
        self.identity = _MockPlanIdentity(plan_id)
        self.phase = MagicMock()
        self.phase.value = "strategize"
        self.state = MagicMock()
        self.state.value = "queued"


class _MockToolSpec:
    """Minimal ToolSpec stub."""

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


class _MockResource:
    """Minimal resource stub."""

    def __init__(self, resource_id: str, name: str, resource_type_name: str) -> None:
        self.resource_id = resource_id
        self.name = name
        self.resource_type_name = resource_type_name


def _build_mock_plan_lifecycle_service() -> MagicMock:
    svc = MagicMock()
    svc.use_action.return_value = _MockPlan()
    return svc


def _build_mock_tool_registry() -> MagicMock:
    registry = MagicMock()
    registry.list_tools.return_value = [
        _MockToolSpec("local/tool-a", "Tool A"),
    ]
    return registry


def _build_mock_resource_registry_service() -> MagicMock:
    svc = MagicMock()
    svc.list_resources.return_value = [
        _MockResource("RES-001", "my-repo", "git-checkout"),
    ]
    return svc


# ---------------------------------------------------------------------------
# Given steps
# ---------------------------------------------------------------------------


@given(r"an opt-val facade with no services")
def step_ov_facade_no_services(context: Context) -> None:
    context.ov_facade = A2aLocalFacade()


@given(r"an opt-val facade with a mock ToolRegistry")
def step_ov_facade_tool_registry(context: Context) -> None:
    context.ov_facade = A2aLocalFacade(
        services={"tool_registry": _build_mock_tool_registry()}
    )


@given(r"an opt-val facade with a mock ResourceRegistryService")
def step_ov_facade_resource_registry(context: Context) -> None:
    context.ov_facade = A2aLocalFacade(
        services={"resource_registry_service": _build_mock_resource_registry_service()}
    )


@given(r"an opt-val facade with a mock PlanLifecycleService")
def step_ov_facade_plan_lifecycle(context: Context) -> None:
    context.ov_facade = A2aLocalFacade(
        services={"plan_lifecycle_service": _build_mock_plan_lifecycle_service()}
    )


# ---------------------------------------------------------------------------
# When steps
# ---------------------------------------------------------------------------


@when(
    r'I dispatch opt-val operation "(?P<operation>[^"]+)" '
    r"with params (?P<params_json>.+)"
)
def step_ov_dispatch(context: Context, operation: str, params_json: str) -> None:
    params: dict[str, Any] = json.loads(params_json)
    request = A2aRequest(method=operation, params=params)
    context.ov_response = context.ov_facade.dispatch(request)


# ---------------------------------------------------------------------------
# Then steps
# ---------------------------------------------------------------------------


@then(r'the opt-val response status should be "(?P<status>[^"]+)"')
def step_ov_response_status(context: Context, status: str) -> None:
    assert (context.ov_response.error is None) == (status == "ok"), (
        f"Expected status '{status}', got error={context.ov_response.error}"
    )


@then(r'the opt-val response data should contain key "(?P<key>[^"]+)"')
def step_ov_data_has_key(context: Context, key: str) -> None:
    assert key in context.ov_response.result, (
        f"Expected key '{key}' in response data, "
        f"got: {list(context.ov_response.result.keys())}"
    )


@then(r'the opt-val response data key "(?P<key>[^"]+)" should equal "(?P<value>[^"]+)"')
def step_ov_data_key_equals(context: Context, key: str, value: str) -> None:
    actual = context.ov_response.result.get(key)
    assert str(actual) == value, f"Expected data['{key}'] = '{value}', got '{actual}'"


@then(r'the opt-val response error message should contain "(?P<fragment>[^"]+)"')
def step_ov_error_message_contains(context: Context, fragment: str) -> None:
    assert context.ov_response.error is not None, "No error in response"
    assert fragment in context.ov_response.error.message, (
        f"Expected message containing '{fragment}', "
        f"got '{context.ov_response.error.message}'"
    )


# Reset step matcher to parse (default) so subsequent step files are not affected
use_step_matcher("parse")
