import pytest
from ellar.auth.guards import GuardHttpBearerAuth
from ellar.common import UseGuards
from ellar.common.constants import CONTROLLER_OPERATION_HANDLER_KEY, GUARDS_KEY
from ellar.reflect import reflect
from ellar.socket_io import GatewayRouterFactory, WebSocketGateway, subscribe_message
from ellar.socket_io.constants import (
    GATEWAY_MESSAGE_HANDLER_KEY,
    GATEWAY_METADATA,
    GATEWAY_OPTIONS,
    GATEWAY_WATERMARK,
    MESSAGE_METADATA,
)
from ellar.socket_io.model import GatewayBase, GatewayType
from ellar.utils import get_name


@WebSocketGateway(path="/ws", namespace="/some-namespace")
@UseGuards(GuardHttpBearerAuth)
class SampleWithoutGateway:
    pass


@WebSocketGateway(path="/ws", namespace="/some-namespace")
class SampleWithGateway(GatewayBase):
    pass


@WebSocketGateway
class SampleMarkAsGateway:
    pass


@pytest.mark.parametrize(
    "gateway, watermark, options",
    [
        (
            SampleWithoutGateway,
            True,
            {
                "async_mode": "asgi",
                "cors_allowed_origins": "*",
                "namespace": "/some-namespace",
            },
        ),
        (
            SampleWithGateway,
            False,
            {
                "async_mode": "asgi",
                "cors_allowed_origins": "*",
                "namespace": "/some-namespace",
            },
        ),
        (
            SampleMarkAsGateway,
            True,
            {"async_mode": "asgi", "cors_allowed_origins": "*"},
        ),
    ],
)
def test_websocket_gateway_works_without_gateway(gateway, watermark, options):
    assert isinstance(gateway, GatewayType)
    assert hasattr(gateway, "__GATEWAY_WATERMARK__") is watermark

    assert reflect.get_metadata_or_raise_exception(GATEWAY_WATERMARK, gateway) is True
    assert reflect.get_metadata_or_raise_exception(GATEWAY_OPTIONS, gateway) == options

    for key in GATEWAY_METADATA.keys:
        reflect.get_metadata_or_raise_exception(key, gateway)


def test_gateway_guard():
    assert reflect.get_metadata_or_raise_exception(
        GUARDS_KEY, SampleWithoutGateway
    ) == [GuardHttpBearerAuth]


def test_sub_message_building_works():
    @WebSocketGateway(path="/ws", namespace="/some-namespace")
    class SampleAGateway(GatewayBase):
        @subscribe_message
        def a_message(self):
            pass

    GatewayRouterFactory.build(SampleAGateway)

    message_handlers = reflect.get_metadata_or_raise_exception(
        GATEWAY_MESSAGE_HANDLER_KEY, SampleAGateway
    )
    assert len(message_handlers) == 1
    assert get_name(message_handlers[0]) == "a_message"
    message = reflect.get_metadata_or_raise_exception(
        MESSAGE_METADATA, message_handlers[0]
    )
    assert message == "a_message"
    assert (
        reflect.get_metadata_or_raise_exception(
            CONTROLLER_OPERATION_HANDLER_KEY, SampleAGateway().a_message
        ).controller
        is SampleAGateway
    )


# def test_sub_message_building_fails():
#     with pytest.raises(Exception) as ex:
#
#         @WebSocketGateway(path="/ws", namespace="/some-namespace")
#         class SampleBGateway(GatewayBase):
#             @subscribe_message
#             def b_message(self):
#                 pass
#
#         GatewayRouterFactory.build(SampleBGateway)
#
#     assert (
#         "SampleBGateway Gateway message handler tried to be processed more than once"
#         in str(ex.value)
#     )


def test_cant_use_gateway_decorator_on_function():
    with pytest.raises(Exception) as ex:

        @WebSocketGateway(path="/ws", namespace="/some-namespace")
        def sample_c_gateway():
            pass

    assert "WebSocketGateway is a class decorator" in str(ex.value)


def test_inheritance_works():
    @WebSocketGateway(path="/ws", namespace="/some-namespace")
    class InheritanceSupported(SampleWithoutGateway):
        @subscribe_message
        def b_message(self):
            pass

    GatewayRouterFactory.build(InheritanceSupported)

    message_handlers = reflect.get_metadata_or_raise_exception(
        GATEWAY_MESSAGE_HANDLER_KEY, InheritanceSupported
    )
    assert len(message_handlers) == 1
