"""
Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Unit tests for orchestration_errors: structured errors for the orchestration layer
(Step 06 - TZ section 6 Error Model).
"""

from __future__ import annotations

from mcp_proxy_adapter.core.errors import MicroserviceError
from mcp_proxy_adapter.core.orchestration.orchestration_errors import (
    ORCHESTRATION_ERROR_CODE,
    CommandNotFoundError,
    InvalidParameterError,
    MissingRequiredParametersError,
    OrchestrationError,
    ProxyDiscoveryError,
    SchemaUnavailableError,
    ServerNotFoundError,
    StaleCatalogError,
    ToolContextBuildError,
)


def test_orchestration_error_base_inherits_microservice_error() -> None:
    """OrchestrationError is a MicroserviceError for handler compatibility."""
    err = OrchestrationError("test")
    assert isinstance(err, MicroserviceError)
    assert err.code == ORCHESTRATION_ERROR_CODE
    assert err.message == "test"
    assert err.data == {}


def test_orchestration_error_with_data() -> None:
    """OrchestrationError stores data and uses it in to_dict."""
    err = OrchestrationError("msg", data={"key": "value"})
    assert err.data == {"key": "value"}
    d = err.to_dict()
    assert d["code"] == ORCHESTRATION_ERROR_CODE
    assert d["message"] == "msg"
    assert d["data"] == {"key": "value"}


def test_server_not_found_error() -> None:
    """ServerNotFoundError carries server_id and exposes it in data."""
    err = ServerNotFoundError(server_id="srv-1")
    assert isinstance(err, OrchestrationError)
    assert err.server_id == "srv-1"
    assert err.data.get("server_id") == "srv-1"
    assert "srv-1" in str(err.to_dict())


def test_server_not_found_error_with_custom_message() -> None:
    """ServerNotFoundError accepts custom message and extra data."""
    err = ServerNotFoundError(message="Custom", server_id="s2", data={"extra": 1})
    assert err.message == "Custom"
    assert err.data["server_id"] == "s2"
    assert err.data["extra"] == 1


def test_command_not_found_error() -> None:
    """CommandNotFoundError carries server_id and command_name."""
    err = CommandNotFoundError(server_id="srv-1", command_name="do_something")
    assert err.server_id == "srv-1"
    assert err.command_name == "do_something"
    assert err.data["server_id"] == "srv-1"
    assert err.data["command_name"] == "do_something"


def test_schema_unavailable_error() -> None:
    """SchemaUnavailableError carries server_id and command_name."""
    err = SchemaUnavailableError(server_id="srv-1", command_name="cmd")
    assert err.server_id == "srv-1"
    assert err.command_name == "cmd"
    assert err.data["command_name"] == "cmd"


def test_missing_required_parameters_error() -> None:
    """MissingRequiredParametersError carries parameter_names list."""
    err = MissingRequiredParametersError(
        server_id="srv-1",
        command_name="cmd",
        parameter_names=["a", "b"],
    )
    assert err.server_id == "srv-1"
    assert err.command_name == "cmd"
    assert err.parameter_names == ["a", "b"]
    assert err.data["parameter_names"] == ["a", "b"]


def test_missing_required_parameters_error_default_list() -> None:
    """MissingRequiredParametersError defaults parameter_names to empty list."""
    err = MissingRequiredParametersError()
    assert err.parameter_names == []


def test_invalid_parameter_error() -> None:
    """InvalidParameterError carries parameter_name and optional reason."""
    err = InvalidParameterError(
        server_id="srv-1",
        command_name="cmd",
        parameter_name="x",
        reason="expected integer",
    )
    assert err.server_id == "srv-1"
    assert err.command_name == "cmd"
    assert err.parameter_name == "x"
    assert err.reason == "expected integer"
    assert err.data["parameter_name"] == "x"
    assert err.data["reason"] == "expected integer"


def test_stale_catalog_error() -> None:
    """StaleCatalogError carries catalog_version and reason."""
    err = StaleCatalogError(catalog_version=42, reason="proxy timeout")
    assert err.catalog_version == 42
    assert err.reason == "proxy timeout"
    assert err.data["catalog_version"] == 42
    assert err.data["reason"] == "proxy timeout"


def test_proxy_discovery_error() -> None:
    """ProxyDiscoveryError carries reason."""
    err = ProxyDiscoveryError(reason="connection refused")
    assert err.reason == "connection refused"
    assert err.data["reason"] == "connection refused"


def test_tool_context_build_error() -> None:
    """ToolContextBuildError carries reason and snapshot_version."""
    err = ToolContextBuildError(
        reason="schema invalid",
        snapshot_version="v2",
    )
    assert err.reason == "schema invalid"
    assert err.snapshot_version == "v2"
    assert err.data["reason"] == "schema invalid"
    assert err.data["snapshot_version"] == "v2"


def test_all_orchestration_errors_are_catchable_as_orchestration_error() -> None:
    """All concrete orchestration errors can be caught as OrchestrationError."""
    errors = [
        ServerNotFoundError(server_id="s"),
        CommandNotFoundError(server_id="s", command_name="c"),
        SchemaUnavailableError(server_id="s", command_name="c"),
        MissingRequiredParametersError(server_id="s", command_name="c"),
        InvalidParameterError(server_id="s", command_name="c"),
        StaleCatalogError(reason="x"),
        ProxyDiscoveryError(reason="x"),
        ToolContextBuildError(reason="x"),
    ]
    for err in errors:
        assert isinstance(err, OrchestrationError)
        assert isinstance(err, MicroserviceError)
