"""Integration tests for multi-user session isolation in remote-hosted mode.

These tests compose PluginRegistry + PluginHub to verify that users
cannot see or interact with each other's Unity instances.
"""

import asyncio
from unittest.mock import AsyncMock

import pytest

from core.config import config
from transport.plugin_hub import NoUnitySessionError, PluginHub
from transport.plugin_registry import PluginRegistry


@pytest.fixture(autouse=True)
def _reset_plugin_hub():
    old_registry = PluginHub._registry
    old_connections = PluginHub._connections.copy()
    old_pending = PluginHub._pending.copy()
    old_lock = PluginHub._lock
    old_loop = PluginHub._loop

    yield

    PluginHub._registry = old_registry
    PluginHub._connections = old_connections
    PluginHub._pending = old_pending
    PluginHub._lock = old_lock
    PluginHub._loop = old_loop


async def _setup_two_user_registry():
    """Set up a registry with two users, each having Unity instances.

    Returns the configured registry. Also configures PluginHub to use it.
    """
    registry = PluginRegistry()
    loop = asyncio.get_running_loop()
    PluginHub.configure(registry, loop)

    await registry.register("sess-A1", "ProjectAlpha", "hashA1", "2022.3", user_id="userA")
    await registry.register("sess-B1", "ProjectBeta", "hashB1", "2022.3", user_id="userB")
    await registry.register("sess-A2", "ProjectGamma", "hashA2", "2022.3", user_id="userA")

    return registry


class TestMultiUserSessionFiltering:
    @pytest.mark.asyncio
    async def test_get_sessions_filters_by_user(self):
        """PluginHub.get_sessions(user_id=X) returns only X's sessions."""
        await _setup_two_user_registry()

        sessions_a = await PluginHub.get_sessions(user_id="userA")
        assert len(sessions_a.sessions) == 2
        project_names = {s.project for s in sessions_a.sessions.values()}
        assert project_names == {"ProjectAlpha", "ProjectGamma"}

        sessions_b = await PluginHub.get_sessions(user_id="userB")
        assert len(sessions_b.sessions) == 1
        assert next(iter(sessions_b.sessions.values())
                    ).project == "ProjectBeta"

    @pytest.mark.asyncio
    async def test_get_sessions_no_filter_returns_all_in_local_mode(self):
        """In local mode, PluginHub.get_sessions() without user_id returns everything."""
        await _setup_two_user_registry()

        all_sessions = await PluginHub.get_sessions()
        assert len(all_sessions.sessions) == 3

    @pytest.mark.asyncio
    async def test_get_sessions_no_filter_raises_in_remote_hosted(self, monkeypatch):
        """In remote-hosted mode, PluginHub.get_sessions() without user_id raises."""
        monkeypatch.setattr(config, "http_remote_hosted", True)
        await _setup_two_user_registry()

        with pytest.raises(ValueError, match="requires user_id"):
            await PluginHub.get_sessions()


class TestResolveSessionIdIsolation:
    @pytest.mark.asyncio
    async def test_resolve_session_for_own_hash(self, monkeypatch):
        """User A can resolve their own project hash."""
        monkeypatch.setattr(config, "http_remote_hosted", True)
        await _setup_two_user_registry()

        session_id = await PluginHub._resolve_session_id("hashA1", user_id="userA")
        assert session_id == "sess-A1"

    @pytest.mark.asyncio
    async def test_cannot_resolve_other_users_hash(self, monkeypatch):
        """User A cannot resolve User B's project hash."""
        monkeypatch.setattr(config, "http_remote_hosted", True)
        monkeypatch.setenv("UNITY_MCP_SESSION_RESOLVE_MAX_WAIT_S", "0.1")
        await _setup_two_user_registry()

        # userA tries to resolve userB's hash -> should not find it
        with pytest.raises(NoUnitySessionError):
            await PluginHub._resolve_session_id("hashB1", user_id="userA")


class TestInstanceListResourceIsolation:
    @pytest.mark.asyncio
    async def test_unity_instances_resource_filters_by_user(self, monkeypatch):
        """The unity_instances resource should pass user_id and return filtered results."""
        monkeypatch.setattr(config, "http_remote_hosted", True)
        monkeypatch.setattr(config, "transport_mode", "http")
        await _setup_two_user_registry()

        from services.resources.unity_instances import unity_instances
        from tests.integration.test_helpers import DummyContext

        ctx = DummyContext()
        await ctx.set_state("user_id", "userA")

        result = await unity_instances(ctx)

        assert result["success"] is True
        assert result["instance_count"] == 2
        instance_names = {i["name"] for i in result["instances"]}
        assert instance_names == {"ProjectAlpha", "ProjectGamma"}
        assert "ProjectBeta" not in instance_names


class TestSetActiveInstanceIsolation:
    @pytest.mark.asyncio
    async def test_set_active_instance_only_sees_own_sessions(self, monkeypatch):
        """set_active_instance should only offer sessions belonging to the current user."""
        monkeypatch.setattr(config, "http_remote_hosted", True)
        monkeypatch.setattr(config, "transport_mode", "http")
        await _setup_two_user_registry()

        from services.tools.set_active_instance import set_active_instance
        from transport.unity_instance_middleware import UnityInstanceMiddleware
        from tests.integration.test_helpers import DummyContext

        middleware = UnityInstanceMiddleware()
        monkeypatch.setattr(
            "services.tools.set_active_instance.get_unity_instance_middleware",
            lambda: middleware,
        )

        ctx = DummyContext()
        await ctx.set_state("user_id", "userA")

        result = await set_active_instance(ctx, "ProjectAlpha@hashA1")
        assert result["success"] is True
        assert await middleware.get_active_instance(ctx) == "ProjectAlpha@hashA1"

    @pytest.mark.asyncio
    async def test_set_active_instance_rejects_other_users_instance(self, monkeypatch):
        """set_active_instance should not find another user's instance."""
        monkeypatch.setattr(config, "http_remote_hosted", True)
        monkeypatch.setattr(config, "transport_mode", "http")
        await _setup_two_user_registry()

        from services.tools.set_active_instance import set_active_instance
        from transport.unity_instance_middleware import UnityInstanceMiddleware
        from tests.integration.test_helpers import DummyContext

        middleware = UnityInstanceMiddleware()
        monkeypatch.setattr(
            "services.tools.set_active_instance.get_unity_instance_middleware",
            lambda: middleware,
        )

        ctx = DummyContext()
        await ctx.set_state("user_id", "userA")

        # UserA tries to select UserB's instance -> should fail
        result = await set_active_instance(ctx, "ProjectBeta@hashB1")
        assert result["success"] is False
