from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.testclient import TestClient

from imia import AuthenticationMiddleware, HTTPBasicAuthenticator
from tests.conftest import inmemory_user_provider, password_verifier


async def app_view(request: Request) -> JSONResponse:
    return JSONResponse(
        {
            'is_authenticated': request.auth.is_authenticated,
            'user_id': request.auth.user_id,
            'user_name': request.auth.display_name,
        }
    )


def test_basic_authentication() -> None:
    """WWW-Basic authentication uses user data from URL to obtain user ID."""
    app = Starlette(
        debug=True,
        routes=[
            Route('/app', app_view, methods=['GET']),
        ],
        middleware=[
            Middleware(
                AuthenticationMiddleware,
                authenticators=[
                    HTTPBasicAuthenticator(user_provider=inmemory_user_provider, password_verifier=password_verifier),
                ],
            ),
        ],
    )
    test_client = TestClient(app)
    response = test_client.get('/app', auth=('root@localhost', 'pa$$word'))
    assert response.json()['is_authenticated'] is True
    assert response.json()['user_id'] == 'root@localhost'
    assert response.json()['user_name'] == 'Root'


def test_basic_authentication_with_invalid_password() -> None:
    app = Starlette(
        debug=True,
        routes=[
            Route('/app', app_view, methods=['GET']),
        ],
        middleware=[
            Middleware(
                AuthenticationMiddleware,
                authenticators=[
                    HTTPBasicAuthenticator(user_provider=inmemory_user_provider, password_verifier=password_verifier),
                ],
            ),
        ],
    )
    test_client = TestClient(app)
    response = test_client.get('/app', auth=('root@localhost', 'invaild'))
    assert response.json()['is_authenticated'] is False


def test_basic_authentication_with_empty_password() -> None:
    app = Starlette(
        debug=True,
        routes=[
            Route('/app', app_view, methods=['GET']),
        ],
        middleware=[
            Middleware(
                AuthenticationMiddleware,
                authenticators=[
                    HTTPBasicAuthenticator(user_provider=inmemory_user_provider, password_verifier=password_verifier),
                ],
            ),
        ],
    )
    test_client = TestClient(app)
    response = test_client.get('/app', auth=('root@localhost', ''))
    assert response.json()['is_authenticated'] is False


def test_basic_authentication_without_credentials() -> None:
    app = Starlette(
        debug=True,
        routes=[
            Route('/app', app_view, methods=['GET']),
        ],
        middleware=[
            Middleware(
                AuthenticationMiddleware,
                authenticators=[
                    HTTPBasicAuthenticator(user_provider=inmemory_user_provider, password_verifier=password_verifier),
                ],
            ),
        ],
    )
    test_client = TestClient(app)
    response = test_client.get('/app')
    assert 'www-authenticate' in response.headers


def test_basic_authentication_with_invalid_user() -> None:
    app = Starlette(
        debug=True,
        routes=[
            Route('/app', app_view, methods=['GET']),
        ],
        middleware=[
            Middleware(
                AuthenticationMiddleware,
                authenticators=[
                    HTTPBasicAuthenticator(user_provider=inmemory_user_provider, password_verifier=password_verifier),
                ],
            ),
        ],
    )
    test_client = TestClient(app)
    response = test_client.get('/app', auth=('missing@localhost', 'password'))
    assert response.json()['is_authenticated'] is False
