"""Tests for UserManager orchestration."""

from __future__ import annotations

import asyncio

import pytest

from aura_auth._core.exceptions import InvalidCredentialsError, UserAlreadyExistsError, UserInactiveError
from aura_auth._core.schemas import LoginRequest, UserCreate
from aura_auth._core.security import PasswordHelper, TokenHelper
from aura_auth.backend.sqlalchemy import SQLAlchemyBackend
from aura_auth.manager import UserManager
from aura_auth.models.sqlalchemy import SQLAlchemyBaseUser
from aura_auth.strategies.password import PasswordStrategy


@pytest.mark.asyncio
async def test_manager_register_creates_user(manager: UserManager) -> None:
    user = await manager.register(UserCreate(email="mgr@example.com", password="password123"))
    assert user.email == "mgr@example.com"


@pytest.mark.asyncio
async def test_manager_register_duplicate_raises(manager: UserManager) -> None:
    payload = UserCreate(email="dupmgr@example.com", password="password123")
    await manager.register(payload)
    with pytest.raises(UserAlreadyExistsError):
        await manager.register(payload)


@pytest.mark.asyncio
async def test_manager_login_returns_token(manager: UserManager) -> None:
    await manager.register(UserCreate(email="loginmgr@example.com", password="password123"))
    token = await manager.login(LoginRequest(email="loginmgr@example.com", password="password123"))
    assert isinstance(token, str)
    assert len(token) > 0


@pytest.mark.asyncio
async def test_manager_login_invalid_raises(manager: UserManager) -> None:
    with pytest.raises(InvalidCredentialsError):
        await manager.login(LoginRequest(email="missing@example.com", password="bad"))


@pytest.mark.asyncio
async def test_manager_login_inactive_raises(manager: UserManager) -> None:
    user = await manager.register(UserCreate(email="inactivemgr@example.com", password="password123"))
    backend = manager._backend
    await backend.update(user, {"is_active": False})
    with pytest.raises(UserInactiveError):
        await manager.login(LoginRequest(email="inactivemgr@example.com", password="password123"))


@pytest.mark.asyncio
async def test_manager_verify_token_valid(manager: UserManager) -> None:
    await manager.register(UserCreate(email="verifymgr@example.com", password="password123"))
    token = await manager.login(LoginRequest(email="verifymgr@example.com", password="password123"))
    user = await manager.verify_token(token)
    assert user is not None
    assert user.email == "verifymgr@example.com"


@pytest.mark.asyncio
async def test_manager_verify_token_invalid_returns_none(manager: UserManager) -> None:
    assert await manager.verify_token("not-a-real-token") is None


@pytest.mark.asyncio
async def test_manager_verify_token_expired_returns_none(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    helper = PasswordHelper()
    short_tokens = TokenHelper("q" * 32, lifetime_seconds=1)
    verify = TokenHelper("q" * 32, lifetime_seconds=3600)
    strategy = PasswordStrategy(helper, short_tokens)
    short_manager = UserManager(backend, strategy, short_tokens, verify)
    await short_manager.register(UserCreate(email="expire@example.com", password="password123"))
    token = await short_manager.login(LoginRequest(email="expire@example.com", password="password123"))
    await asyncio.sleep(1.1)
    assert await short_manager.verify_token(token) is None


@pytest.mark.asyncio
async def test_manager_get_user(manager: UserManager) -> None:
    created = await manager.register(UserCreate(email="getuser@example.com", password="password123"))
    fetched = await manager.get_user(created.id)
    assert fetched is not None
    assert fetched.email == "getuser@example.com"


@pytest.mark.asyncio
async def test_manager_get_user_missing_returns_none(manager: UserManager) -> None:
    import uuid

    assert await manager.get_user(uuid.uuid4()) is None


@pytest.mark.asyncio
async def test_manager_full_flow(manager: UserManager) -> None:
    user = await manager.register(UserCreate(email="flow@example.com", password="password123"))
    token = await manager.login(LoginRequest(email="flow@example.com", password="password123"))
    current = await manager.verify_token(token)
    assert current is not None
    same = await manager.get_user(user.id)
    assert same is not None
    assert same.email == current.email
