import pytest

from skytale_sdk.errors import (
    AuthError,
    ChannelError,
    MlsError,
    QuotaExceededError,
    SkytaleError,
    TransportError,
)


class TestSkytaleError:
    def test_default_code(self):
        e = SkytaleError("something broke")
        assert e.code == "unknown_error"

    def test_custom_code(self):
        e = SkytaleError("fail", code="custom_code")
        assert e.code == "custom_code"

    def test_default_doc_url(self):
        e = SkytaleError("fail", code="test_code")
        assert e.doc_url == "https://skytale.sh/docs/sdk/errors#test_code"

    def test_custom_doc_url(self):
        e = SkytaleError("fail", doc_url="https://custom.url")
        assert e.doc_url == "https://custom.url"

    def test_http_status(self):
        e = SkytaleError("fail", http_status=500)
        assert e.http_status == 500

    def test_http_status_none_by_default(self):
        e = SkytaleError("fail")
        assert e.http_status is None

    def test_is_runtime_error(self):
        e = SkytaleError("fail")
        assert isinstance(e, RuntimeError)

    def test_message(self):
        e = SkytaleError("something went wrong")
        assert str(e) == "something went wrong"


class TestSubclasses:
    def test_auth_error(self):
        e = AuthError("bad token")
        assert e.code == "auth_failed"
        assert isinstance(e, SkytaleError)
        assert isinstance(e, RuntimeError)

    def test_transport_error(self):
        e = TransportError("connection refused")
        assert e.code == "transport_error"
        assert isinstance(e, SkytaleError)

    def test_channel_error(self):
        e = ChannelError("invalid channel name")
        assert e.code == "channel_error"
        assert isinstance(e, SkytaleError)

    def test_quota_exceeded_error(self):
        e = QuotaExceededError("limit reached")
        assert e.code == "quota_exceeded"
        assert isinstance(e, SkytaleError)

    def test_mls_error(self):
        e = MlsError("decryption failed")
        assert e.code == "mls_error"
        assert isinstance(e, SkytaleError)


class TestSubclassOverrides:
    def test_auth_error_custom_code(self):
        e = AuthError("fail", code="token_expired")
        assert e.code == "token_expired"

    def test_transport_error_with_status(self):
        e = TransportError("timeout", http_status=504)
        assert e.http_status == 504

    def test_channel_error_doc_url(self):
        e = ChannelError("bad name", doc_url="https://docs.example.com")
        assert e.doc_url == "https://docs.example.com"


class TestCatchHierarchy:
    def test_catch_as_skytale_error(self):
        with pytest.raises(SkytaleError):
            raise AuthError("bad token")

    def test_catch_as_runtime_error(self):
        with pytest.raises(RuntimeError):
            raise ChannelError("bad channel")

    def test_specific_catch(self):
        with pytest.raises(MlsError):
            raise MlsError("key error")

    def test_catch_order(self):
        """More specific exceptions should be caught before general ones."""
        try:
            raise QuotaExceededError("over limit", http_status=429)
        except QuotaExceededError as e:
            assert e.http_status == 429
        except SkytaleError:
            pytest.fail("Should have been caught by QuotaExceededError")
