import sys

import pytest

from sanic import Sanic


try:
    import sanic_ext  # noqa: F401

    SANIC_EXT_IN_ENV = True
except ImportError:
    SANIC_EXT_IN_ENV = False


@pytest.fixture
def stoppable_app(app):
    @app.before_server_start
    async def stop(*_):
        app.stop()

    return app


def test_ext_is_loaded(stoppable_app: Sanic, mock_sanic_ext, port):
    stoppable_app.run(single_process=True, port=port)
    mock_sanic_ext.Extend.assert_called_once_with(stoppable_app)


def test_ext_is_not_loaded(stoppable_app: Sanic, mock_sanic_ext, port):
    stoppable_app.config.AUTO_EXTEND = False
    stoppable_app.run(single_process=True, port=port)
    mock_sanic_ext.Extend.assert_not_called()


def test_extend_with_args(stoppable_app: Sanic, mock_sanic_ext, port):
    stoppable_app.extend(built_in_extensions=False)
    stoppable_app.run(single_process=True, port=port)
    mock_sanic_ext.Extend.assert_called_once_with(
        stoppable_app, built_in_extensions=False, config=None, extensions=None
    )


def test_access_object_sets_up_extension(app: Sanic, mock_sanic_ext):
    app.ext
    mock_sanic_ext.Extend.assert_called_once_with(app)


def test_extend_cannot_be_called_multiple_times(app: Sanic, mock_sanic_ext):
    app.extend()

    message = "Cannot extend Sanic after Sanic Extensions has been setup."
    with pytest.raises(RuntimeError, match=message):
        app.extend()
    mock_sanic_ext.Extend.assert_called_once_with(
        app, extensions=None, built_in_extensions=True, config=None
    )


@pytest.mark.skipif(
    SANIC_EXT_IN_ENV,
    reason="Running tests with sanic_ext already in the environment",
)
def test_fail_if_not_loaded(app: Sanic):
    del sys.modules["sanic_ext"]
    with pytest.raises(
        RuntimeError, match="Sanic Extensions is not installed.*"
    ):
        app.extend(built_in_extensions=False)


def test_can_access_app_ext_while_running(
    app: Sanic, mock_sanic_ext, ext_instance, port
):
    class IceCream:
        flavor: str

    @app.before_server_start
    async def injections(*_):
        app.ext.injection(IceCream)
        app.stop()

    app.run(single_process=True, port=port)
    ext_instance.injection.assert_called_with(IceCream)
