from __future__ import annotations

from datetime import datetime, timezone
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.friendships.dependencies import get_friendship_service
from v1.friendships.schemas import (
    FriendRequestCreate,
    FriendRequestResponse,
    FriendResponse,
    UserBasicInfo,
)
from v1.friendships.service import FriendshipService
from v1.users.dependencies import get_current_user


def _raise_unauthorized() -> CurrentUser:
    raise HTTPException(status_code=401, detail="Unauthorized")


class FakeFriendshipService(FriendshipService):
    def __init__(self) -> None:
        pass

    async def send_request(self, request: FriendRequestCreate) -> FriendRequestResponse:
        return FriendRequestResponse(
            id=UUID("11111111-1111-1111-1111-111111111111"),
            sender=UserBasicInfo(id="user-1", username="sender", avatar_url=None),
            recipient=UserBasicInfo(id="user-2", username="recipient", avatar_url=None),
            content=request.content,
            status="pending",
            created_at=datetime.now(timezone.utc),
        )

    async def accept_request(self, friendship_id: UUID) -> FriendRequestResponse:
        return FriendRequestResponse(
            id=friendship_id,
            sender=UserBasicInfo(id="user-1", username="sender", avatar_url=None),
            recipient=UserBasicInfo(id="user-2", username="recipient", avatar_url=None),
            content="Hello!",
            status="accepted",
            created_at=datetime.now(timezone.utc),
        )

    async def decline_request(self, friendship_id: UUID) -> FriendRequestResponse:
        return FriendRequestResponse(
            id=friendship_id,
            sender=UserBasicInfo(id="user-1", username="sender", avatar_url=None),
            recipient=UserBasicInfo(id="user-2", username="recipient", avatar_url=None),
            content="Hello!",
            status="rejected",
            created_at=datetime.now(timezone.utc),
        )

    async def cancel_request(self, friendship_id: UUID) -> FriendRequestResponse:
        return FriendRequestResponse(
            id=friendship_id,
            sender=UserBasicInfo(id="user-1", username="sender", avatar_url=None),
            recipient=UserBasicInfo(id="user-2", username="recipient", avatar_url=None),
            content="Hello!",
            status="canceled",
            created_at=datetime.now(timezone.utc),
        )

    async def get_inbox(self) -> list[FriendRequestResponse]:
        return [
            FriendRequestResponse(
                id=UUID("11111111-1111-1111-1111-111111111111"),
                sender=UserBasicInfo(id="user-1", username="sender", avatar_url=None),
                recipient=UserBasicInfo(
                    id="user-2", username="recipient", avatar_url=None
                ),
                content="Hello!",
                status="pending",
                created_at=datetime.now(timezone.utc),
            )
        ]

    async def get_outgoing_requests(self) -> list[FriendRequestResponse]:
        return [
            FriendRequestResponse(
                id=UUID("22222222-2222-2222-2222-222222222222"),
                sender=UserBasicInfo(id="user-1", username="sender", avatar_url=None),
                recipient=UserBasicInfo(
                    id="user-3", username="target", avatar_url=None
                ),
                content=None,
                status="pending",
                created_at=datetime.now(timezone.utc),
            )
        ]

    async def get_friends_list(self) -> list[FriendResponse]:
        return [
            FriendResponse(
                id=UUID("33333333-3333-3333-3333-333333333333"),
                friend=UserBasicInfo(id="user-2", username="friend", avatar_url=None),
                status="active",
                created_at=datetime.now(timezone.utc),
                accepted_at=datetime.now(timezone.utc),
            )
        ]

    async def remove_friend(self, friend_id: UUID) -> FriendResponse:
        return FriendResponse(
            id=UUID("33333333-3333-3333-3333-333333333333"),
            friend=UserBasicInfo(id=str(friend_id), username="friend", avatar_url=None),
            status="active",
            created_at=datetime.now(timezone.utc),
            accepted_at=datetime.now(timezone.utc),
        )


def _override_friendship_service(
    service: FriendshipService,
) -> Callable[[], FriendshipService]:
    def _get_service() -> FriendshipService:
        return service

    return _get_service


def _get_fake_current_user() -> CurrentUser:
    return CurrentUser(
        id=UUID("00000000-0000-0000-0000-000000000001"),
        email="test@example.com",
    )


class TestSendFriendRequest:
    def test_send_friend_request_returns_201(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.post(
                "/api/v1/friends/requests",
                json={
                    "target_user_id": "22222222-2222-2222-2222-222222222222",
                    "content": "Hello!",
                },
            )
            assert response.status_code == 201
            body = response.json()
            assert body["status"] == "pending"
            assert body["sender"]["username"] == "sender"
            assert body["recipient"]["username"] == "recipient"
        finally:
            app.dependency_overrides = {}

    def test_send_friend_request_missing_target_returns_422(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.post("/api/v1/friends/requests", json={})
            assert response.status_code == 422
            assert response.headers["content-type"].startswith(
                "application/problem+json"
            )
        finally:
            app.dependency_overrides = {}

    def test_send_friend_request_invalid_uuid_returns_422(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.post(
                "/api/v1/friends/requests",
                json={"target_user_id": "invalid-uuid"},
            )
            assert response.status_code == 422
            assert response.headers["content-type"].startswith(
                "application/problem+json"
            )
        finally:
            app.dependency_overrides = {}


class TestGetInbox:
    def test_get_inbox_returns_200(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.get("/api/v1/friends/requests/inbox")
            assert response.status_code == 200
            body = response.json()
            assert isinstance(body, list)
            assert len(body) == 1
            assert body[0]["status"] == "pending"
        finally:
            app.dependency_overrides = {}


class TestGetOutgoingRequests:
    def test_get_outgoing_requests_returns_200(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.get("/api/v1/friends/requests/outgoing")
            assert response.status_code == 200
            body = response.json()
            assert isinstance(body, list)
            assert len(body) == 1
            assert body[0]["status"] == "pending"
        finally:
            app.dependency_overrides = {}


class TestAcceptFriendRequest:
    def test_accept_friend_request_returns_200(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.post(
                "/api/v1/friends/requests/11111111-1111-1111-1111-111111111111/accept",
                json={},
            )
            assert response.status_code == 200
            body = response.json()
            assert body["status"] == "accepted"
        finally:
            app.dependency_overrides = {}


class TestDeclineFriendRequest:
    def test_decline_friend_request_returns_200(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.post(
                "/api/v1/friends/requests/11111111-1111-1111-1111-111111111111/decline",
                json={},
            )
            assert response.status_code == 200
            body = response.json()
            assert body["status"] == "rejected"
        finally:
            app.dependency_overrides = {}


class TestCancelFriendRequest:
    def test_cancel_friend_request_returns_204(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.delete(
                "/api/v1/friends/requests/11111111-1111-1111-1111-111111111111"
            )
            assert response.status_code == 204
            assert response.content == b""
        finally:
            app.dependency_overrides = {}


class TestGetFriendsList:
    def test_get_friends_list_returns_200(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.get("/api/v1/friends")
            assert response.status_code == 200
            body = response.json()
            assert isinstance(body, list)
            assert len(body) == 1
            assert body[0]["status"] == "active"
            assert body[0]["friend"]["username"] == "friend"
        finally:
            app.dependency_overrides = {}


class TestRemoveFriend:
    def test_remove_friend_returns_204(self) -> None:
        app.dependency_overrides[get_friendship_service] = _override_friendship_service(
            FakeFriendshipService()
        )
        app.dependency_overrides[get_current_user] = _get_fake_current_user

        client = TestClient(app)
        try:
            response = client.delete(
                "/api/v1/friends/33333333-3333-3333-3333-333333333333"
            )
            assert response.status_code == 204
            assert response.content == b""
        finally:
            app.dependency_overrides = {}


class TestFriendshipRequiresAuth:
    def test_send_request_without_auth_returns_401(self) -> None:
        app.dependency_overrides[get_current_user] = _raise_unauthorized

        client = TestClient(app)
        try:
            response = client.post(
                "/api/v1/friends/requests",
                json={
                    "target_user_id": "22222222-2222-2222-2222-222222222222",
                },
            )
            assert response.status_code == 401
        finally:
            app.dependency_overrides = {}

    def test_get_inbox_without_auth_returns_401(self) -> None:
        app.dependency_overrides[get_current_user] = _raise_unauthorized

        client = TestClient(app)
        try:
            response = client.get("/api/v1/friends/requests/inbox")
            assert response.status_code == 401
        finally:
            app.dependency_overrides = {}

    def test_get_friends_without_auth_returns_401(self) -> None:
        app.dependency_overrides[get_current_user] = _raise_unauthorized

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