from __future__ import annotations

from typing import Callable
from uuid import UUID

from fastapi import HTTPException
from fastapi.testclient import TestClient

from app import app
from core.auth.models import CurrentUser
from v1.users.dependencies import get_current_user, get_user_service
from v1.users.schemas import UserResponse, UserSearchRequest, UserUpdateRequest
from v1.users.service import UserService


class FakeUserService:
    """Fake service for integration testing."""

    def __init__(self, user: UserResponse) -> None:
        self._user = user
        self._search_results: list[UserResponse] = []

    def set_search_results(self, results: list[UserResponse]) -> None:
        self._search_results = results

    async def get_me(self) -> UserResponse:
        if self._user.id is None:
            raise HTTPException(status_code=404, detail="User not found")
        return self._user

    async def update_me(self, update: UserUpdateRequest) -> UserResponse:
        if self._user.id is None:
            raise HTTPException(status_code=404, detail="User not found")
        return UserResponse(
            id=self._user.id,
            username=(
                update.username if update.username is not None else self._user.username
            ),
            avatar_url=(
                update.avatar_url
                if update.avatar_url is not None
                else self._user.avatar_url
            ),
            bio=update.bio if update.bio is not None else self._user.bio,
        )

    async def get_by_username(self, username: str) -> UserResponse:
        if username != self._user.username:
            raise HTTPException(status_code=404, detail="User not found")
        return self._user

    async def search_users(self, request: UserSearchRequest) -> list[UserResponse]:
        if request.query:
            return self._search_results if self._search_results else [self._user]
        return []


def _override_user_service(
    service: FakeUserService,
) -> Callable[[], UserService]:
    def _get_service() -> UserService:
        return service  # type: ignore[return-value]

    return _get_service


def _override_current_user(user_id: UUID) -> Callable[[], CurrentUser]:
    def _get_user() -> CurrentUser:
        return CurrentUser(id=user_id)

    return _get_user


def test_get_me_returns_user() -> None:
    user_id = UUID("00000000-0000-0000-0000-000000000001")
    user = UserResponse(
        id=str(user_id),
        username="demo",
        avatar_url=None,
        bio=None,
    )
    app.dependency_overrides[get_user_service] = _override_user_service(
        FakeUserService(user)
    )
    app.dependency_overrides[get_current_user] = _override_current_user(user_id)

    client = TestClient(app)
    try:
        response = client.get("/api/v1/users/me")
        assert response.status_code == 200
        body = response.json()
        assert body["username"] == "demo"
    finally:
        app.dependency_overrides = {}


def test_patch_me_updates_user() -> None:
    user_id = UUID("00000000-0000-0000-0000-000000000001")
    user = UserResponse(
        id=str(user_id),
        username="demo",
        avatar_url=None,
        bio=None,
    )
    app.dependency_overrides[get_user_service] = _override_user_service(
        FakeUserService(user)
    )
    app.dependency_overrides[get_current_user] = _override_current_user(user_id)

    client = TestClient(app)
    try:
        response = client.patch(
            "/api/v1/users/me",
            json={"username": "updated"},
        )
        assert response.status_code == 200
        body = response.json()
        assert body["username"] == "updated"
    finally:
        app.dependency_overrides = {}


def test_patch_me_validation_error_returns_problem_details() -> None:
    user_id = UUID("00000000-0000-0000-0000-000000000001")
    user = UserResponse(
        id=str(user_id),
        username="demo",
        avatar_url=None,
        bio=None,
    )
    app.dependency_overrides[get_user_service] = _override_user_service(
        FakeUserService(user)
    )
    app.dependency_overrides[get_current_user] = _override_current_user(user_id)

    client = TestClient(app)
    try:
        response = client.patch("/api/v1/users/me", json={})
        assert response.status_code == 422
        assert response.headers["content-type"].startswith("application/problem+json")
        body = response.json()
        assert body["title"] == "Unprocessable Content"
        assert body["status"] == 422
    finally:
        app.dependency_overrides = {}


def test_search_users_returns_list() -> None:
    user_id = UUID("00000000-0000-0000-0000-000000000001")
    user = UserResponse(
        id=str(user_id),
        username="demo",
        avatar_url=None,
        bio=None,
    )
    app.dependency_overrides[get_user_service] = _override_user_service(
        FakeUserService(user)
    )

    client = TestClient(app)
    try:
        response = client.post(
            "/api/v1/users/search",
            json={"query": "demo"},
        )
        assert response.status_code == 200
        body = response.json()
        assert isinstance(body, list)
    finally:
        app.dependency_overrides = {}


def test_search_users_empty_query_returns_422() -> None:
    user_id = UUID("00000000-0000-0000-0000-000000000001")
    user = UserResponse(
        id=str(user_id),
        username="demo",
        avatar_url=None,
        bio=None,
    )
    app.dependency_overrides[get_user_service] = _override_user_service(
        FakeUserService(user)
    )

    client = TestClient(app)
    try:
        response = client.post(
            "/api/v1/users/search",
            json={"query": ""},
        )
        assert response.status_code == 422
    finally:
        app.dependency_overrides = {}


def test_get_user_by_username_returns_404() -> None:
    user = UserResponse(
        id="00000000-0000-0000-0000-000000000001",
        username="demo",
        avatar_url=None,
        bio=None,
    )
    app.dependency_overrides[get_user_service] = _override_user_service(
        FakeUserService(user)
    )

    client = TestClient(app)
    try:
        response = client.get("/api/v1/users/demo")
        assert response.status_code == 404
    finally:
        app.dependency_overrides = {}
