"""Tests for ``mgf.apiprobe.exceptions``."""

from __future__ import annotations

import pytest

from mgf.apiprobe.exceptions import (
    ApiprobeConfigError,
    ApiprobeError,
    AuthError,
    MatcherError,
    SchemaError,
    SuiteLogicError,
    TransportError,
)


class TestApiprobeError:
    def test_subclasses_apperror(self) -> None:
        from mgf.common.exceptions import AppError

        assert issubclass(ApiprobeError, AppError)

    def test_can_be_raised_and_caught(self) -> None:
        with pytest.raises(ApiprobeError, match="boom"):
            raise ApiprobeError("boom")


class TestConfigErrors:
    def test_apiprobe_config_error_subclasses_both_roots(self) -> None:
        from mgf.common.exceptions import ConfigError

        assert issubclass(ApiprobeConfigError, ConfigError)
        assert issubclass(ApiprobeConfigError, ApiprobeError)

    def test_schema_error_subclasses_apiprobe_config_error(self) -> None:
        assert issubclass(SchemaError, ApiprobeConfigError)

    def test_matcher_error_subclasses_apiprobe_config_error(self) -> None:
        assert issubclass(MatcherError, ApiprobeConfigError)


class TestOperationErrors:
    def test_transport_error_subclasses_both_roots(self) -> None:
        from mgf.common.exceptions import OperationError

        assert issubclass(TransportError, OperationError)
        assert issubclass(TransportError, ApiprobeError)

    def test_transport_error_carries_machine_readable_fields(self) -> None:
        exc = TransportError(url="https://example.com/api", cause="DNS NXDOMAIN")
        assert exc.url == "https://example.com/api"
        assert exc.cause == "DNS NXDOMAIN"
        assert "https://example.com/api" in str(exc)
        assert "DNS NXDOMAIN" in str(exc)

    def test_auth_error_carries_flow_name_and_reason(self) -> None:
        exc = AuthError(flow_name="bearer", reason="env APIPROBE_TOKEN unset")
        assert exc.flow_name == "bearer"
        assert exc.reason == "env APIPROBE_TOKEN unset"
        assert "bearer" in str(exc)

    def test_suite_logic_error_subclasses_apiprobe_error(self) -> None:
        assert issubclass(SuiteLogicError, ApiprobeError)


class TestExceptCatchability:
    """Confirm consumers can catch broadly via ApiprobeError."""

    @pytest.mark.parametrize(
        "exc_cls",
        [ApiprobeConfigError, SchemaError, MatcherError, SuiteLogicError],
    )
    def test_caught_by_apiprobe_error(self, exc_cls: type[ApiprobeError]) -> None:
        with pytest.raises(ApiprobeError):
            raise exc_cls("x")

    def test_transport_error_caught_by_apiprobe_error(self) -> None:
        with pytest.raises(ApiprobeError):
            raise TransportError(url="x", cause="y")

    def test_auth_error_caught_by_apiprobe_error(self) -> None:
        with pytest.raises(ApiprobeError):
            raise AuthError(flow_name="x", reason="y")


def test_transport_error_message_carries_actionable_hint() -> None:
    """EH-09: the message ends in an actionable next step (substring-pinned)."""
    from mgf.apiprobe.exceptions import TransportError

    assert "Verify the host is reachable" in str(TransportError(url="https://x/y", cause="timeout"))


def test_auth_error_message_carries_actionable_hint() -> None:
    """EH-09: the message ends in an actionable next step (substring-pinned)."""
    from mgf.apiprobe.exceptions import AuthError

    assert "Check the flow's credentials" in str(AuthError(flow_name="bearer", reason="no token"))
