"""Unit tests for the error hierarchy."""

from __future__ import annotations

import pytest

from routeros.errors import (
    AlreadyAsyncError,
    AsyncLoopEndedError,
    DeviceError,
    InvalidChallengeReceivedError,
    LoginError,
    NoChallengeReceivedError,
    RouterOSError,
    UnknownReplyError,
)
from routeros.sentence import Sentence


def _sentence(word: str, **kv) -> Sentence:
    words = [word] + [f"={k}={v}" for k, v in kv.items()]
    return Sentence.from_words(words)


class TestRouterOSError:
    def test_is_base_exception(self):
        assert issubclass(RouterOSError, Exception)

    def test_all_errors_inherit_from_base(self):
        for cls in [
            DeviceError,
            UnknownReplyError,
            LoginError,
            AlreadyAsyncError,
            AsyncLoopEndedError,
        ]:
            assert issubclass(cls, RouterOSError)

    def test_login_errors_inherit_from_login_error(self):
        assert issubclass(NoChallengeReceivedError, LoginError)
        assert issubclass(InvalidChallengeReceivedError, LoginError)


class TestDeviceError:
    def test_message_from_sentence(self):
        sen = _sentence("!trap", message="bad command")
        err = DeviceError(sen)
        assert err.message == "bad command"
        assert "bad command" in str(err)

    def test_message_fallback_when_missing(self):
        sen = _sentence("!trap")
        err = DeviceError(sen)
        assert "unknown error" in err.message.lower()

    def test_sentence_attribute(self):
        sen = _sentence("!fatal", message="fatal")
        err = DeviceError(sen)
        assert err.sentence is sen

    def test_is_exception(self):
        sen = _sentence("!trap", message="oops")
        err = DeviceError(sen)
        with pytest.raises(DeviceError):
            raise err


class TestUnknownReplyError:
    def test_contains_word(self):
        sen = Sentence(word="!bogus")
        err = UnknownReplyError(sen)
        assert "!bogus" in str(err)

    def test_sentence_attribute(self):
        sen = Sentence(word="!unknown")
        err = UnknownReplyError(sen)
        assert err.sentence is sen


class TestLoginErrors:
    def test_no_challenge_message(self):
        err = NoChallengeReceivedError()
        assert "challenge" in str(err).lower()

    def test_invalid_challenge_with_detail(self):
        err = InvalidChallengeReceivedError("non-hex")
        assert "non-hex" in str(err)

    def test_invalid_challenge_without_detail(self):
        err = InvalidChallengeReceivedError()
        assert "invalid" in str(err).lower()


class TestAlreadyAsyncError:
    def test_message(self):
        err = AlreadyAsyncError()
        assert "async" in str(err).lower()


class TestAsyncLoopEndedError:
    def test_message(self):
        err = AsyncLoopEndedError()
        assert "loop" in str(err).lower()
