# Copyright 2025 Softwell S.r.l.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests for BaseMiddleware.server property."""

from genro_asgi.middleware import BaseMiddleware
from genro_asgi.middleware.authentication import AuthMiddleware


class FakeServer:
    """Simulates AsgiServer for testing."""


class FakeDispatcher:
    """Simulates Dispatcher with a .server attribute."""

    __slots__ = ("server",)

    def __init__(self, server: FakeServer) -> None:
        self.server = server


async def _bare_app(scope, receive, send) -> None:
    """Bare ASGI app with no .app or .server attributes."""


class ConcreteMiddleware(BaseMiddleware):
    """Concrete middleware subclass for testing."""

    middleware_name = "_test_concrete"

    async def __call__(self, scope, receive, send) -> None:
        await self.app(scope, receive, send)


class ConcreteMiddleware2(BaseMiddleware):
    """Second concrete middleware for nested chain tests."""

    middleware_name = "_test_concrete_2"

    async def __call__(self, scope, receive, send) -> None:
        await self.app(scope, receive, send)


class TestMiddlewareServerProperty:
    """Tests for BaseMiddleware.server property."""

    def test_server_from_dispatcher(self) -> None:
        """MW(Dispatcher(server)) -> mw.server is server."""
        server = FakeServer()
        dispatcher = FakeDispatcher(server)
        mw = ConcreteMiddleware(dispatcher)

        assert mw.server is server

    def test_server_from_nested_chain(self) -> None:
        """MW1(MW2(Dispatcher(server))) -> mw1.server is server."""
        server = FakeServer()
        dispatcher = FakeDispatcher(server)
        mw2 = ConcreteMiddleware2(dispatcher)
        mw1 = ConcreteMiddleware(mw2)

        assert mw1.server is server

    def test_server_none_when_no_dispatcher(self) -> None:
        """MW(bare_app) -> mw.server is None."""
        mw = ConcreteMiddleware(_bare_app)

        assert mw.server is None

    def test_server_cached_after_first_access(self) -> None:
        """Second access returns cached result without re-walking."""
        server = FakeServer()
        dispatcher = FakeDispatcher(server)
        mw = ConcreteMiddleware(dispatcher)

        _ = mw.server
        assert mw._cached_server is server

    def test_server_none_cached(self) -> None:
        """None result is also cached (no re-walk on standalone MW)."""
        mw = ConcreteMiddleware(_bare_app)

        _ = mw.server
        assert mw._cached_server is None

    def test_subclass_inherits_server_property(self) -> None:
        """AuthMiddleware (subclass) also has working .server property."""
        server = FakeServer()
        dispatcher = FakeDispatcher(server)
        auth_mw = AuthMiddleware(
            dispatcher,
            tokens={"type": "bearer", "t1": {"token": "tk_test", "tags": "read"}},
        )

        assert auth_mw.server is server
