# -*- coding: utf-8 -*-
"""Location: ./tests/unit/mcpgateway/routers/test_teams_coverage.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Coverage tests for mcpgateway.routers.teams — error branches, edge cases.
"""

# Standard
import importlib
from datetime import datetime, timedelta, timezone
from unittest.mock import AsyncMock, MagicMock, patch
from uuid import uuid4

# Third-Party
import pytest
from fastapi import HTTPException, status
from sqlalchemy.orm import Session


# Patch RBAC decorators before importing teams module
def _noop_decorator(permission: str, resource_type=None):
    def decorator(func):
        return func

    return decorator


with patch("mcpgateway.middleware.rbac.require_permission", _noop_decorator):
    with patch("mcpgateway.middleware.rbac.require_admin_permission", lambda: (lambda f: f)):
        from mcpgateway.db import EmailTeam, EmailTeamInvitation, EmailTeamMember, EmailUser
        from mcpgateway.routers import teams
        from mcpgateway.services.team_invitation_service import TeamInvitationService
        from mcpgateway.services.team_management_service import TeamManagementService

        importlib.reload(teams)


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def db():
    return MagicMock(spec=Session)


@pytest.fixture
def user_ctx(db):
    return {"email": "user@test.com", "full_name": "User", "is_admin": False, "db": db}


@pytest.fixture
def admin_ctx(db):
    return {"email": "admin@test.com", "full_name": "Admin", "is_admin": True, "db": db}


@pytest.fixture
def mock_team():
    t = MagicMock(spec=EmailTeam)
    t.id = str(uuid4())
    t.name = "Team"
    t.slug = "team"
    t.description = "desc"
    t.created_by = "user@test.com"
    t.is_personal = False
    t.visibility = "private"
    t.max_members = 100
    t.created_at = datetime.now(timezone.utc)
    t.updated_at = datetime.now(timezone.utc)
    t.is_active = True
    t.get_member_count = MagicMock(return_value=1)
    return t


@pytest.fixture
def mock_member():
    m = MagicMock(spec=EmailTeamMember)
    m.id = str(uuid4())
    m.team_id = str(uuid4())
    m.user_email = "member@test.com"
    m.role = "member"
    m.joined_at = datetime.now(timezone.utc)
    m.invited_by = "owner@test.com"
    m.is_active = True
    return m


@pytest.fixture
def mock_invitation():
    inv = MagicMock(spec=EmailTeamInvitation)
    inv.id = str(uuid4())
    inv.team_id = str(uuid4())
    inv.email = "invited@test.com"
    inv.role = "member"
    inv.invited_by = "owner@test.com"
    inv.invited_at = datetime.now(timezone.utc)
    inv.expires_at = datetime.now(timezone.utc) + timedelta(days=7)
    inv.token = "tok-123"
    inv.is_active = True
    inv.is_expired = MagicMock(return_value=False)
    return inv


def _svc(**methods):
    """Build a patched TeamManagementService context manager."""
    svc = AsyncMock(spec=TeamManagementService)
    for k, v in methods.items():
        setattr(svc, k, v)
    return patch("mcpgateway.routers.teams.TeamManagementService", return_value=svc)


def _inv_svc(**methods):
    """Build a patched TeamInvitationService context manager."""
    svc = AsyncMock(spec=TeamInvitationService)
    for k, v in methods.items():
        setattr(svc, k, v)
    return patch("mcpgateway.routers.teams.TeamInvitationService", return_value=svc)


# ===========================================================================
# discover_public_teams — error branch
# ===========================================================================


def mock_permission_check(is_admin=False):
    """Helper context manager to mock PermissionService.check_platform_admin_permission."""
    from contextlib import contextmanager
    from unittest.mock import AsyncMock, patch

    @contextmanager
    def _mock():
        with patch("mcpgateway.routers.teams.PermissionService") as MockPermissionService:
            mock_perm_service = AsyncMock()
            mock_perm_service.check_platform_admin_permission = AsyncMock(return_value=is_admin)
            MockPermissionService.return_value = mock_perm_service
            yield mock_perm_service

    return _mock()


class TestDiscoverPublicTeamsErrors:
    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(discover_public_teams=AsyncMock(side_effect=Exception("db"))):
            with pytest.raises(HTTPException) as exc:
                await teams.discover_public_teams(0, 50, current_user_ctx=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# update_team — missing branches
# ===========================================================================


class TestUpdateTeamMaxMembersCap:
    """Router-level enforcement: non-admin cannot set max_members > settings cap."""

    @pytest.mark.asyncio
    async def test_non_admin_create_exceeds_cap_rejected(self, user_ctx, db):
        """Non-admin create with max_members > cap returns 400."""
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(create_team=AsyncMock(side_effect=ValueError("max_members cannot exceed the configured limit of 100"))):
            from mcpgateway.schemas import TeamCreateRequest

            req = TeamCreateRequest(name="BigTeam", max_members=200)
            with pytest.raises(HTTPException) as exc:
                await teams.create_team(req, current_user_ctx=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "cannot exceed" in exc.value.detail

    @pytest.mark.asyncio
    async def test_admin_create_exceeds_cap_allowed(self, admin_ctx, db, mock_team):
        """Admin create with max_members > cap succeeds."""
        mock_team.max_members = 500
        with mock_permission_check(is_admin=admin_ctx.get("is_admin", False)), _svc(create_team=AsyncMock(return_value=mock_team), get_member_counts_batch_cached=AsyncMock(return_value={})):
            from mcpgateway.schemas import TeamCreateRequest

            req = TeamCreateRequest(name="BigTeam", max_members=500)
            result = await teams.create_team(req, current_user_ctx=admin_ctx, db=db)
            assert result.max_members == 500

    @pytest.mark.asyncio
    async def test_non_admin_create_at_cap_allowed(self, user_ctx, db, mock_team):
        """Non-admin create with max_members == cap succeeds."""
        mock_team.max_members = 100
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(create_team=AsyncMock(return_value=mock_team)):
            from mcpgateway.schemas import TeamCreateRequest

            req = TeamCreateRequest(name="Team", max_members=100)
            result = await teams.create_team(req, current_user_ctx=user_ctx, db=db)
            assert result.max_members == 100

    @pytest.mark.asyncio
    async def test_non_admin_update_exceeds_cap_rejected(self, user_ctx, db, mock_team):
        """Non-admin update with max_members > cap returns 400."""
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(get_user_role_in_team=AsyncMock(return_value="owner"), update_team=AsyncMock(side_effect=ValueError("max_members cannot exceed the configured limit of 100"))),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(max_members=200)
            with pytest.raises(HTTPException) as exc:
                await teams.update_team(mock_team.id, req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "cannot exceed" in exc.value.detail

    @pytest.mark.asyncio
    async def test_admin_update_exceeds_cap_allowed(self, admin_ctx, db, mock_team):
        """Admin update with max_members > cap succeeds."""
        updated = MagicMock()
        updated.max_members = 500
        updated.id = mock_team.id
        updated.name = mock_team.name
        updated.slug = mock_team.slug
        updated.description = mock_team.description
        updated.created_by = mock_team.created_by
        updated.is_personal = False
        updated.visibility = "private"
        updated.created_at = mock_team.created_at
        updated.updated_at = mock_team.updated_at
        updated.is_active = True
        updated.get_member_count = MagicMock(return_value=1)
        with (
            mock_permission_check(is_admin=admin_ctx.get("is_admin", False)),
            _svc(get_user_role_in_team=AsyncMock(return_value="owner"), update_team=AsyncMock(return_value=True), get_team_by_id=AsyncMock(return_value=updated)),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(max_members=500)
            result = await teams.update_team(mock_team.id, req, current_user=admin_ctx, db=db)
            assert result.max_members == 500

    @pytest.mark.asyncio
    async def test_non_admin_update_at_cap_allowed(self, user_ctx, db, mock_team):
        """Non-admin update with max_members == cap succeeds."""
        updated = MagicMock()
        updated.max_members = 100
        updated.id = mock_team.id
        updated.name = mock_team.name
        updated.slug = mock_team.slug
        updated.description = mock_team.description
        updated.created_by = mock_team.created_by
        updated.is_personal = False
        updated.visibility = "private"
        updated.created_at = mock_team.created_at
        updated.updated_at = mock_team.updated_at
        updated.is_active = True
        updated.get_member_count = MagicMock(return_value=1)
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_team=AsyncMock(return_value=True),
                get_team_by_id=AsyncMock(return_value=updated),
            ),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(max_members=100)
            result = await teams.update_team(mock_team.id, req, current_user=user_ctx, db=db)
            assert result.max_members == 100

    @pytest.mark.asyncio
    async def test_update_with_none_max_members_preserves(self, user_ctx, db, mock_team):
        """Update with no max_members field preserves the existing value."""
        mock_team.max_members = 75
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_team=AsyncMock(return_value=True),
                get_team_by_id=AsyncMock(return_value=mock_team),
            ),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(name="Renamed")
            result = await teams.update_team(mock_team.id, req, current_user=user_ctx, db=db)
            assert result.max_members == 75


class TestUpdateTeamErrors:
    @pytest.mark.asyncio
    async def test_team_not_found_after_update(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_team=AsyncMock(return_value=True),
                get_team_by_id=AsyncMock(return_value=None),
            ),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(name="X", description="Y")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team(mock_team.id, req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND
            assert "not found after update" in exc.value.detail

    @pytest.mark.asyncio
    async def test_value_error(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_team=AsyncMock(side_effect=ValueError("bad name")),
            ),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(name="X")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "bad name" in exc.value.detail

    @pytest.mark.asyncio
    async def test_unexpected_exception(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(side_effect=RuntimeError("crash")),
            ),
        ):
            from mcpgateway.schemas import TeamUpdateRequest

            req = TeamUpdateRequest(name="X")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# delete_team — exception branch
# ===========================================================================


class TestDeleteTeamErrors:
    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(side_effect=Exception("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.delete_team("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# list_team_members — cursor pagination, include_pagination, exception
# ===========================================================================


class TestListTeamMembersEdge:
    @pytest.mark.asyncio
    async def test_cursor_pagination(self, user_ctx, db, mock_member):
        mock_user = MagicMock(spec=EmailUser)
        members = [(mock_user, mock_member)]
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="member"),
                get_team_members=AsyncMock(return_value=(members, "next-cursor")),
            ),
        ):
            result = await teams.list_team_members("tid", cursor="abc", limit=10, include_pagination=False, current_user=user_ctx, db=db)
            assert isinstance(result, list)
            assert len(result) == 1

    @pytest.mark.asyncio
    async def test_include_pagination(self, user_ctx, db, mock_member):
        mock_user = MagicMock(spec=EmailUser)
        members = [(mock_user, mock_member)]
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="member"),
                get_team_members=AsyncMock(return_value=(members, "nc")),
            ),
        ):
            result = await teams.list_team_members("tid", cursor="abc", limit=10, include_pagination=True, current_user=user_ctx, db=db)
            assert hasattr(result, "members")
            assert result.next_cursor == "nc"

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(side_effect=Exception("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.list_team_members("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# update_team_member — all missing branches
# ===========================================================================


class TestUpdateTeamMemberErrors:
    @pytest.mark.asyncio
    async def test_not_owner(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(return_value="member")):
            from mcpgateway.schemas import TeamMemberUpdateRequest

            req = TeamMemberUpdateRequest(role="owner")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team_member("tid", "u@t.com", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_403_FORBIDDEN

    @pytest.mark.asyncio
    async def test_update_failed(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_member_role=AsyncMock(return_value=False),
            ),
        ):
            from mcpgateway.schemas import TeamMemberUpdateRequest

            req = TeamMemberUpdateRequest(role="owner")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team_member("tid", "u@t.com", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_member_not_found_after_update(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_member_role=AsyncMock(return_value=True),
                get_member=AsyncMock(return_value=None),
            ),
        ):
            from mcpgateway.schemas import TeamMemberUpdateRequest

            req = TeamMemberUpdateRequest(role="owner")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team_member("tid", "u@t.com", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND
            assert "after update" in exc.value.detail

    @pytest.mark.asyncio
    async def test_value_error(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                update_member_role=AsyncMock(side_effect=ValueError("bad role")),
            ),
        ):
            from mcpgateway.schemas import TeamMemberUpdateRequest

            req = TeamMemberUpdateRequest(role="member")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team_member("tid", "u@t.com", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(side_effect=RuntimeError("crash"))):
            from mcpgateway.schemas import TeamMemberUpdateRequest

            req = TeamMemberUpdateRequest(role="member")
            with pytest.raises(HTTPException) as exc:
                await teams.update_team_member("tid", "u@t.com", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# remove_team_member — missing branches
# ===========================================================================


class TestRemoveTeamMemberErrors:
    @pytest.mark.asyncio
    async def test_member_not_found(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_user_role_in_team=AsyncMock(return_value="owner"),
                remove_member_from_team=AsyncMock(return_value=False),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.remove_team_member("tid", "other@t.com", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.remove_team_member("tid", "other@t.com", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# invite_team_member — missing branches
# ===========================================================================


class TestInviteTeamMemberErrors:
    @pytest.mark.asyncio
    async def test_invitation_creation_failed(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(get_user_role_in_team=AsyncMock(return_value="owner")),
            _inv_svc(
                create_invitation=AsyncMock(return_value=None),
            ),
        ):
            from mcpgateway.schemas import TeamInviteRequest

            req = TeamInviteRequest(email="x@t.com", role="member")
            with pytest.raises(HTTPException) as exc:
                await teams.invite_team_member("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR

    @pytest.mark.asyncio
    async def test_value_error(self, user_ctx, db):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(get_user_role_in_team=AsyncMock(return_value="owner")),
            _inv_svc(
                create_invitation=AsyncMock(side_effect=ValueError("dup")),
            ),
        ):
            from mcpgateway.schemas import TeamInviteRequest

            req = TeamInviteRequest(email="x@t.com", role="member")
            with pytest.raises(HTTPException) as exc:
                await teams.invite_team_member("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(side_effect=RuntimeError("crash"))):
            from mcpgateway.schemas import TeamInviteRequest

            req = TeamInviteRequest(email="x@t.com", role="member")
            with pytest.raises(HTTPException) as exc:
                await teams.invite_team_member("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# list_team_invitations — missing branches
# ===========================================================================


class TestListTeamInvitationsErrors:
    @pytest.mark.asyncio
    async def test_not_owner(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(return_value="member")):
            with pytest.raises(HTTPException) as exc:
                await teams.list_team_invitations("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_403_FORBIDDEN

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.list_team_invitations("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# accept_team_invitation — missing branches
# ===========================================================================


class TestAcceptTeamInvitationErrors:
    @pytest.mark.asyncio
    async def test_value_error(self, user_ctx, db):
        with _inv_svc(accept_invitation=AsyncMock(side_effect=ValueError("expired"))):
            with pytest.raises(HTTPException) as exc:
                await teams.accept_team_invitation("tok", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with _inv_svc(accept_invitation=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.accept_team_invitation("tok", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# cancel_team_invitation — missing branches
# ===========================================================================


class TestCancelTeamInvitationErrors:
    @pytest.mark.asyncio
    async def test_invitation_not_found(self, user_ctx, db):
        mock_query = MagicMock()
        mock_filter = MagicMock()
        mock_query.filter = MagicMock(return_value=mock_filter)
        mock_filter.first = MagicMock(return_value=None)
        db.query = MagicMock(return_value=mock_query)

        with pytest.raises(HTTPException) as exc:
            await teams.cancel_team_invitation("inv-id", current_user=user_ctx, db=db)
        assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_not_owner_not_inviter(self, user_ctx, db, mock_invitation):
        mock_invitation.invited_by = "someone_else@test.com"
        mock_query = MagicMock()
        mock_filter = MagicMock()
        mock_query.filter = MagicMock(return_value=mock_filter)
        mock_filter.first = MagicMock(return_value=mock_invitation)
        db.query = MagicMock(return_value=mock_query)

        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_user_role_in_team=AsyncMock(return_value="member")):
            with pytest.raises(HTTPException) as exc:
                await teams.cancel_team_invitation(mock_invitation.id, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_403_FORBIDDEN

    @pytest.mark.asyncio
    async def test_revoke_failed(self, user_ctx, db, mock_invitation):
        mock_query = MagicMock()
        mock_filter = MagicMock()
        mock_query.filter = MagicMock(return_value=mock_filter)
        mock_filter.first = MagicMock(return_value=mock_invitation)
        db.query = MagicMock(return_value=mock_query)

        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(get_user_role_in_team=AsyncMock(return_value="owner")),
            _inv_svc(
                revoke_invitation=AsyncMock(return_value=False),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.cancel_team_invitation(mock_invitation.id, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        db.query = MagicMock(side_effect=RuntimeError("crash"))

        with pytest.raises(HTTPException) as exc:
            await teams.cancel_team_invitation("inv-id", current_user=user_ctx, db=db)
        assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# request_to_join_team — missing branches
# ===========================================================================


class TestRequestToJoinTeamErrors:
    @pytest.mark.asyncio
    async def test_team_not_found(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(return_value=None)):
            from mcpgateway.schemas import TeamJoinRequest

            req = TeamJoinRequest(message="hi")
            with pytest.raises(HTTPException) as exc:
                await teams.request_to_join_team("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_value_error(self, user_ctx, db, mock_team):
        mock_team.visibility = "public"
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value=None),
                create_join_request=AsyncMock(side_effect=ValueError("User has reached the maximum team limit of 50")),
            ),
        ):
            from mcpgateway.schemas import TeamJoinRequest

            req = TeamJoinRequest(message="hi")
            with pytest.raises(HTTPException) as exc:
                await teams.request_to_join_team(mock_team.id, req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "maximum team limit" in exc.value.detail

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(side_effect=RuntimeError("crash"))):
            from mcpgateway.schemas import TeamJoinRequest

            req = TeamJoinRequest(message="hi")
            with pytest.raises(HTTPException) as exc:
                await teams.request_to_join_team("tid", req, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# leave_team — missing branches
# ===========================================================================


class TestLeaveTeamErrors:
    @pytest.mark.asyncio
    async def test_team_not_found(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(return_value=None)):
            with pytest.raises(HTTPException) as exc:
                await teams.leave_team("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_not_a_member(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value=None),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.leave_team(mock_team.id, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "not a member" in exc.value.detail

    @pytest.mark.asyncio
    async def test_remove_failed_last_owner(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="owner"),
                remove_member_from_team=AsyncMock(return_value=False),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.leave_team(mock_team.id, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "last owner" in exc.value.detail

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.leave_team("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# list_team_join_requests — missing branches
# ===========================================================================


class TestListTeamJoinRequestsErrors:
    @pytest.mark.asyncio
    async def test_team_not_found(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(return_value=None)):
            with pytest.raises(HTTPException) as exc:
                await teams.list_team_join_requests("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_not_owner(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="member"),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.list_team_join_requests(mock_team.id, current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_403_FORBIDDEN

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.list_team_join_requests("tid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# approve_join_request — missing branches
# ===========================================================================


class TestApproveJoinRequestErrors:
    @pytest.mark.asyncio
    async def test_team_not_found(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(return_value=None)):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request("tid", "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_not_owner(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="member"),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request(mock_team.id, "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_403_FORBIDDEN

    @pytest.mark.asyncio
    async def test_member_none(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="owner"),
                approve_join_request=AsyncMock(return_value=None),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request(mock_team.id, "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_value_error_max_team_limit(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="owner"),
                approve_join_request=AsyncMock(side_effect=ValueError("User has reached the maximum team limit of 50")),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request(mock_team.id, "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "Cannot approve" in exc.value.detail

    @pytest.mark.asyncio
    async def test_value_error_max_members_limit(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="owner"),
                approve_join_request=AsyncMock(side_effect=ValueError("Team has reached its maximum member limit of 5")),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request(mock_team.id, "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "maximum member limit" in exc.value.detail

    @pytest.mark.asyncio
    async def test_value_error_generic(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="owner"),
                approve_join_request=AsyncMock(side_effect=ValueError("some other validation error")),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request(mock_team.id, "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_400_BAD_REQUEST
            assert "some other validation error" in exc.value.detail

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.approve_join_request("tid", "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR


# ===========================================================================
# reject_join_request — missing branches
# ===========================================================================


class TestRejectJoinRequestErrors:
    @pytest.mark.asyncio
    async def test_team_not_found(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(return_value=None)):
            with pytest.raises(HTTPException) as exc:
                await teams.reject_join_request("tid", "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_request_not_found(self, user_ctx, db, mock_team):
        with (
            mock_permission_check(is_admin=user_ctx.get("is_admin", False)),
            _svc(
                get_team_by_id=AsyncMock(return_value=mock_team),
                get_user_role_in_team=AsyncMock(return_value="owner"),
                reject_join_request=AsyncMock(return_value=False),
            ),
        ):
            with pytest.raises(HTTPException) as exc:
                await teams.reject_join_request(mock_team.id, "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_exception(self, user_ctx, db):
        with mock_permission_check(is_admin=user_ctx.get("is_admin", False)), _svc(get_team_by_id=AsyncMock(side_effect=RuntimeError("crash"))):
            with pytest.raises(HTTPException) as exc:
                await teams.reject_join_request("tid", "rid", current_user=user_ctx, db=db)
            assert exc.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
