from __future__ import annotations

from types import SimpleNamespace
from typing import Any
from unittest.mock import patch

import pytest
from fastapi.responses import HTMLResponse
from fastapi.responses import RedirectResponse

from amsdal_ml.mcp_server.mcp_oauth.routes.login import login_page
from amsdal_ml.mcp_server.mcp_oauth.routes.login import login_submit
from amsdal_ml.mcp_server.mcp_oauth.services.errors import OAuthAuthUnavailableError
from amsdal_ml.mcp_server.mcp_oauth.services.errors import OAuthTokenGenerationError
from amsdal_ml.mcp_server.mcp_oauth.services.types import AUTH_COOKIE_NAME

HTTP_OK = 200
HTTP_UNAUTHORIZED = 401
HTTP_INTERNAL_SERVER_ERROR = 500
HTTP_SEE_OTHER = 303


@pytest.mark.asyncio
async def test_login_page_returns_html():
    response = await login_page(next_url='/next')

    assert isinstance(response, HTMLResponse)
    assert response.status_code == HTTP_OK


@pytest.mark.asyncio
async def test_login_submit_import_error_raises_oauth_auth_unavailable():
    request: Any = SimpleNamespace(base_url='https://app.example/', url=SimpleNamespace(scheme='https'))
    real_import = __import__

    def fake_import(name, globals_=None, locals_=None, fromlist=(), level=0):
        if name == 'amsdal.contrib.auth.models.login_session':
            msg = 'mocked import error'
            raise ImportError(msg)
        return real_import(name, globals_, locals_, fromlist, level)

    with patch('builtins.__import__', side_effect=fake_import):
        with pytest.raises(OAuthAuthUnavailableError):
            await login_submit(request=request, email='user@example.com', password='pwd', next_url='/next')


@pytest.mark.asyncio
async def test_login_submit_returns_401_when_session_save_fails(monkeypatch):
    from amsdal.contrib.auth.errors import AuthenticationError
    from amsdal.contrib.auth.models import login_session as login_session_module

    class FailingSession:
        def __init__(self, **kwargs):
            self.token = None

        async def asave(self):
            msg = 'bad credentials from provider'
            raise AuthenticationError(msg)

    monkeypatch.setattr(login_session_module, 'LoginSession', FailingSession)
    request: Any = SimpleNamespace(base_url='https://app.example/', url=SimpleNamespace(scheme='https'))

    response = await login_submit(request=request, email='user@example.com', password='pwd', next_url='/next')

    assert isinstance(response, HTMLResponse)
    assert response.status_code == HTTP_UNAUTHORIZED
    assert b'Invalid email or password' in response.body
    assert b'bad credentials from provider' not in response.body


@pytest.mark.asyncio
async def test_login_submit_returns_500_when_session_save_has_server_error(monkeypatch):
    from amsdal.contrib.auth.models import login_session as login_session_module

    class FailingSession:
        def __init__(self, **kwargs):
            self.token = None

        async def asave(self):
            msg = 'db unavailable: internal details'
            raise RuntimeError(msg)

    monkeypatch.setattr(login_session_module, 'LoginSession', FailingSession)
    request: Any = SimpleNamespace(base_url='https://app.example/', url=SimpleNamespace(scheme='https'))

    response = await login_submit(request=request, email='user@example.com', password='pwd', next_url='/next')

    assert isinstance(response, HTMLResponse)
    assert response.status_code == HTTP_INTERNAL_SERVER_ERROR
    assert b'Login temporarily unavailable. Please try again later.' in response.body
    assert b'db unavailable: internal details' not in response.body


@pytest.mark.asyncio
async def test_login_submit_raises_when_token_not_generated(monkeypatch):
    from amsdal.contrib.auth.models import login_session as login_session_module

    class NoTokenSession:
        def __init__(self, **kwargs):
            self.token = None

        async def asave(self):
            return None

    monkeypatch.setattr(login_session_module, 'LoginSession', NoTokenSession)
    request: Any = SimpleNamespace(base_url='https://app.example/', url=SimpleNamespace(scheme='https'))

    with pytest.raises(OAuthTokenGenerationError):
        await login_submit(request=request, email='user@example.com', password='pwd', next_url='/next')


@pytest.mark.asyncio
async def test_login_submit_success_sets_cookie_and_redirect(monkeypatch):
    from amsdal.contrib.auth.models import login_session as login_session_module
    from amsdal.contrib.auth.settings import auth_settings

    class SuccessSession:
        def __init__(self, **kwargs):
            self.token = 'session-token-123'

        async def asave(self):
            return None

    monkeypatch.setattr(login_session_module, 'LoginSession', SuccessSession)
    monkeypatch.setattr(auth_settings, 'AUTH_TOKEN_EXPIRATION', 3600, raising=False)
    request: Any = SimpleNamespace(base_url='https://app.example/', url=SimpleNamespace(scheme='https'))

    response = await login_submit(request=request, email='user@example.com', password='pwd', next_url='/dashboard')

    assert isinstance(response, RedirectResponse)
    assert response.status_code == HTTP_SEE_OTHER
    assert response.headers['location'] == '/dashboard'
    cookie_header = response.headers['set-cookie']
    assert f'{AUTH_COOKIE_NAME}=session-token-123' in cookie_header
    assert 'HttpOnly' in cookie_header
    assert 'Max-Age=3600' in cookie_header
    assert 'SameSite=lax' in cookie_header
