"""Test fixtures for API endpoint tests.

Provides fixtures for testing FastAPI endpoints with mocked dependencies.
"""

from contextlib import asynccontextmanager
from datetime import UTC, datetime
from unittest.mock import MagicMock, patch
from uuid import uuid4

import fakeredis.aioredis
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlalchemy.ext.asyncio import AsyncSession

from app.api import deps as api_deps
from app.api.auth import router as auth_router
from app.api.users import router as users_router
from app.db.models import User
from app.services.jwt_service import create_access_token, create_refresh_token


@pytest.fixture
def fake_redis():
    """Provide a fake Redis instance for testing."""
    return fakeredis.aioredis.FakeRedis(decode_responses=True)


@pytest.fixture
def mock_get_redis(fake_redis):
    """Mock the get_redis context manager to use fake Redis."""

    @asynccontextmanager
    async def _mock_get_redis():
        yield fake_redis

    return _mock_get_redis


@pytest.fixture
def test_user():
    """Create a test user object.

    Returns a User model instance that can be used in tests.
    The user is not persisted to database.
    """
    user = User(
        email="test@example.com",
        display_name="Test User",
        avatar_url="https://example.com/avatar.jpg",
        oauth_provider="google",
        oauth_id="google-123",
        is_premium=False,
        premium_until=None,
    )
    # Manually set the ID since we're not using database
    user.id = str(uuid4())
    user.created_at = datetime.now(UTC)
    user.updated_at = datetime.now(UTC)
    user.last_login = None
    user.linked_accounts = []
    return user


@pytest.fixture
def premium_user(test_user):
    """Create a premium test user."""
    from datetime import timedelta

    test_user.is_premium = True
    test_user.premium_until = datetime.now(UTC) + timedelta(days=30)
    return test_user


@pytest.fixture
def access_token(test_user):
    """Create a valid access token for the test user."""
    from uuid import UUID

    user_id = UUID(test_user.id) if isinstance(test_user.id, str) else test_user.id
    return create_access_token(user_id)


@pytest.fixture
def refresh_token_data(test_user):
    """Create a valid refresh token and JTI for the test user."""
    from uuid import UUID

    user_id = UUID(test_user.id) if isinstance(test_user.id, str) else test_user.id
    token, jti = create_refresh_token(user_id)
    return {"token": token, "jti": jti, "user_id": user_id}


@pytest.fixture
def mock_db_session():
    """Create a mock database session."""
    return MagicMock(spec=AsyncSession)


@pytest.fixture
def app(mock_get_redis, mock_db_session):
    """Create a test FastAPI app with mocked Redis and DB.

    This creates a minimal app with just the auth and users routers,
    with Redis and database mocked.
    """
    # Create test app (no lifespan since we're mocking everything)
    test_app = FastAPI()
    test_app.include_router(auth_router, prefix="/api")
    test_app.include_router(users_router, prefix="/api")

    # Override get_db dependency to return mock session
    async def override_get_db():
        yield mock_db_session

    test_app.dependency_overrides[api_deps.get_db] = override_get_db

    # Patch get_redis globally for this app
    with (
        patch("app.api.auth.get_redis", mock_get_redis),
        patch("app.services.token_store.get_redis", mock_get_redis),
    ):
        yield test_app

    # Clean up overrides
    test_app.dependency_overrides.clear()


@pytest.fixture
def client(app):
    """Create a test client for the app."""
    return TestClient(app)
