"""Tests for the SQLAlchemy backend."""

from __future__ import annotations

import uuid

import pytest

from aura_auth._core.exceptions import UserAlreadyExistsError, UserNotFoundError
from aura_auth._core.protocols import BackendProtocol
from aura_auth.backend.sqlalchemy import SQLAlchemyBackend
from aura_auth.models.sqlalchemy import SQLAlchemyBaseUser


@pytest.mark.asyncio
async def test_backend_create_inserts_user(session_factory) -> None:
    backend: SQLAlchemyBackend[uuid.UUID] = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    user = await backend.create({"email": "a@example.com", "hashed_password": "hash"})
    assert user.id is not None
    assert user.email == "a@example.com"


@pytest.mark.asyncio
async def test_backend_create_duplicate_email_raises(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    await backend.create({"email": "dup@example.com", "hashed_password": "hash"})
    with pytest.raises(UserAlreadyExistsError):
        await backend.create({"email": "dup@example.com", "hashed_password": "hash"})


@pytest.mark.asyncio
async def test_backend_get_by_id_found(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    created = await backend.create({"email": "id@example.com", "hashed_password": "hash"})
    fetched = await backend.get_by_id(created.id)
    assert fetched is not None
    assert fetched.email == "id@example.com"


@pytest.mark.asyncio
async def test_backend_get_by_id_missing_returns_none(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    assert await backend.get_by_id(uuid.uuid4()) is None


@pytest.mark.asyncio
async def test_backend_get_by_email_found(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    await backend.create({"email": "lookup@example.com", "hashed_password": "hash"})
    user = await backend.get_by_email("lookup@example.com")
    assert user is not None


@pytest.mark.asyncio
async def test_backend_get_by_email_missing_returns_none(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    assert await backend.get_by_email("missing@example.com") is None


@pytest.mark.asyncio
async def test_backend_update_changes_fields(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    user = await backend.create({"email": "update@example.com", "hashed_password": "hash"})
    updated = await backend.update(user, {"is_verified": True})
    assert updated.is_verified is True


@pytest.mark.asyncio
async def test_backend_update_partial(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    user = await backend.create({"email": "partial@example.com", "hashed_password": "hash"})
    updated = await backend.update(user, {"is_active": False})
    assert updated.is_active is False
    assert updated.email == "partial@example.com"


@pytest.mark.asyncio
async def test_backend_delete_removes_user(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    user = await backend.create({"email": "delete@example.com", "hashed_password": "hash"})
    await backend.delete(user)
    assert await backend.get_by_id(user.id) is None


@pytest.mark.asyncio
async def test_backend_satisfies_protocol(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    assert isinstance(backend, BackendProtocol)


@pytest.mark.asyncio
async def test_backend_update_missing_user_raises(session_factory) -> None:
    backend = SQLAlchemyBackend(session_factory, SQLAlchemyBaseUser)
    user = await backend.create({"email": "gone@example.com", "hashed_password": "hash"})
    await backend.delete(user)
    with pytest.raises(UserNotFoundError):
        await backend.update(user, {"is_verified": True})
