from __future__ import annotations

import importlib
import sys

import pytest
from abc import abstractmethod

from abcreg import AnnotatedABC


class Queryable(AnnotatedABC):
    @abstractmethod
    def query(self, **kwargs) -> str: ...


class BaseQueryable(AnnotatedABC):
    @abstractmethod
    def query(self, **kwargs) -> str: ...


class ExtendedQueryable(BaseQueryable):
    @abstractmethod
    def query(self, **kwargs) -> str: ...

    @abstractmethod
    def fetch(self) -> int: ...


def test_direct_subclass_matching_annotations() -> None:
    class Good(Queryable):
        def query(self, **kwargs) -> str:
            return "ok"

    assert issubclass(Good, Queryable)
    assert Good().query() == "ok"


def test_direct_subclass_missing_method() -> None:
    with pytest.raises(TypeError, match="missing or mismatched 'query'"):

        class Bad(Queryable):
            pass


def test_direct_subclass_wrong_return_annotation() -> None:
    with pytest.raises(TypeError, match="missing or mismatched 'query'"):

        class Bad(Queryable):
            def query(self, **kwargs) -> int:
                return 1


def test_virtual_register_compliant() -> None:
    class Legacy:
        def query(self, **kwargs) -> str:
            return "legacy"

    Queryable.register(Legacy)
    assert issubclass(Legacy, Queryable)
    assert isinstance(Legacy(), Queryable)


def test_virtual_register_non_compliant() -> None:
    class Bad:
        def query(self, **kwargs) -> int:
            return 1

    with pytest.raises(TypeError, match="missing or mismatched 'query'"):
        Queryable.register(Bad)
    assert not issubclass(Bad, Queryable)


def test_inherited_abstract_methods() -> None:
    class Good(ExtendedQueryable):
        def query(self, **kwargs) -> str:
            return "ok"

        def fetch(self) -> int:
            return 42

    assert Good().fetch() == 42


def test_inherited_abstract_methods_missing() -> None:
    with pytest.raises(TypeError, match="missing or mismatched 'fetch'"):

        class Bad(ExtendedQueryable):
            def query(self, **kwargs) -> str:
                return "ok"


def test_abstract_intermediate_abc() -> None:
    class Partial(Queryable):
        @abstractmethod
        def query(self, **kwargs) -> str: ...

    assert issubclass(Partial, Queryable)


def test_get_annotations_from_inspect_on_python_before_3_14() -> None:
    if sys.version_info >= (3, 14):
        pytest.skip("get_annotations lives in annotationlib on Python 3.14+")

    import inspect

    from abcreg import _annotations

    importlib.reload(_annotations)
    assert _annotations.get_annotations is inspect.get_annotations


def test_get_annotations_from_annotationlib_on_python_3_14() -> None:
    if sys.version_info < (3, 14):
        pytest.skip("annotationlib.get_annotations is only available on Python 3.14+")

    import annotationlib

    from abcreg import _annotations

    importlib.reload(_annotations)
    assert _annotations.get_annotations is annotationlib.get_annotations
