from __future__ import annotations

from uuid import uuid4

import pytest
from sqlalchemy import Column, String, Table, select
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from core.db.base import Base
from models.profile import Profile


@pytest.fixture
async def db_engine():
    """Create in-memory SQLite engine for testing."""
    users_table = Table(
        "users",
        Base.metadata,
        Column("id", String, primary_key=True),
        schema="auth",
        extend_existing=True,
    )
    engine = create_async_engine(
        "sqlite+aiosqlite:///:memory:",
        echo=False,
    )
    async with engine.begin() as conn:
        await conn.exec_driver_sql("ATTACH DATABASE ':memory:' AS auth")
        await conn.run_sync(Base.metadata.create_all)
    yield engine
    Base.metadata.remove(users_table)
    await engine.dispose()


@pytest.fixture
async def db_session(db_engine):
    """Create a database session for testing."""
    async_session = async_sessionmaker(
        bind=db_engine,
        class_=AsyncSession,
        expire_on_commit=False,
    )
    async with async_session() as session:
        yield session
        await session.rollback()


@pytest.mark.asyncio
async def test_profile_model_create(db_session: AsyncSession) -> None:
    """Test creating a Profile model."""
    profile_id = uuid4()
    profile = Profile(
        id=profile_id,
        username="testuser",
    )
    db_session.add(profile)
    await db_session.commit()
    await db_session.refresh(profile)

    assert profile.id == profile_id
    assert profile.username == "testuser"
    assert profile.created_at is not None
    assert profile.updated_at is not None
    assert profile.deleted_at is None


@pytest.mark.asyncio
async def test_profile_model_get_by_id(db_session: AsyncSession) -> None:
    """Test retrieving a Profile by ID."""
    profile_id = uuid4()
    profile = Profile(
        id=profile_id,
        username="testuser",
    )
    db_session.add(profile)
    await db_session.commit()

    result = await db_session.get(Profile, profile_id)
    assert result is not None
    assert result.username == "testuser"


@pytest.mark.asyncio
async def test_profile_model_get_by_username(db_session: AsyncSession) -> None:
    """Test retrieving a Profile by username."""
    profile = Profile(
        id=uuid4(),
        username="testuser",
    )
    db_session.add(profile)
    await db_session.commit()

    result = await db_session.execute(
        select(Profile).where(Profile.username == "testuser")
    )
    found = result.scalar_one()
    assert found is not None
    assert found.username == "testuser"


@pytest.mark.asyncio
async def test_profile_model_update(db_session: AsyncSession) -> None:
    """Test updating a Profile."""
    profile = Profile(
        id=uuid4(),
        username="testuser",
        bio="Old bio",
    )
    db_session.add(profile)
    await db_session.commit()

    profile.bio = "New bio"
    await db_session.commit()
    await db_session.refresh(profile)

    assert profile.bio == "New bio"


@pytest.mark.asyncio
async def test_profile_model_allows_duplicate_usernames(
    db_session: AsyncSession,
) -> None:
    first = Profile(id=uuid4(), username="same_name")
    second = Profile(id=uuid4(), username="same_name")

    db_session.add(first)
    db_session.add(second)
    await db_session.commit()

    result = await db_session.execute(
        select(Profile).where(Profile.username == "same_name")
    )
    found = result.scalars().all()
    assert len(found) == 2
