"""Tests for the Slack view classes and V1 conversation handling.

Focuses on V1 conversation scenarios:
1. V1 conversation creation
2. Paused sandbox resumption for V1 conversations
"""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from integrations.slack.slack_view import (
    SlackNewConversationView,
    SlackUpdateExistingConversationView,
)
from jinja2 import DictLoader, Environment
from storage.slack_conversation import SlackConversation
from storage.slack_user import SlackUser

from openhands.app_server.sandbox.sandbox_models import SandboxStatus
from openhands.app_server.user_auth.user_auth import UserAuth

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


@pytest.fixture
def mock_jinja_env():
    """Create a mock Jinja environment with test templates."""
    templates = {
        'user_message_conversation_instructions.j2': 'Previous messages: {{ messages|join(", ") }}\nUser: {{ username }}\nURL: {{ conversation_url }}'
    }
    return Environment(loader=DictLoader(templates))


@pytest.fixture
def mock_slack_user():
    """Create a mock SlackUser."""
    user = SlackUser()
    user.slack_user_id = 'U1234567890'
    user.keycloak_user_id = 'test-user-123'
    user.slack_display_name = 'Test User'
    return user


@pytest.fixture
def mock_user_auth():
    """Create a mock UserAuth."""
    auth = MagicMock(spec=UserAuth)
    auth.get_provider_tokens = AsyncMock(return_value={})
    auth.get_secrets = AsyncMock(return_value=MagicMock(custom_secrets={}))
    return auth


@pytest.fixture
def slack_new_conversation_view(mock_slack_user, mock_user_auth):
    """Create a SlackNewConversationView instance."""
    return SlackNewConversationView(
        bot_access_token='xoxb-test-token',
        user_msg='Hello OpenHands!',
        slack_user_id='U1234567890',
        slack_to_openhands_user=mock_slack_user,
        saas_user_auth=mock_user_auth,
        channel_id='C1234567890',
        message_ts='1234567890.123456',
        thread_ts=None,
        selected_repo='owner/repo',
        should_extract=True,
        send_summary_instruction=True,
        conversation_id='',
        team_id='T1234567890',
    )


@pytest.fixture
def slack_update_conversation_view_v1(mock_slack_user, mock_user_auth):
    """Create a SlackUpdateExistingConversationView instance for V1."""
    conversation_id = '12345678-1234-5678-1234-567812345678'
    mock_conversation = SlackConversation(
        conversation_id=conversation_id,
        channel_id='C1234567890',
        keycloak_user_id='test-user-123',
        parent_id='1234567890.123456',
        v1_enabled=True,
    )
    return SlackUpdateExistingConversationView(
        bot_access_token='xoxb-test-token',
        user_msg='Follow up message',
        slack_user_id='U1234567890',
        slack_to_openhands_user=mock_slack_user,
        saas_user_auth=mock_user_auth,
        channel_id='C1234567890',
        message_ts='1234567890.123457',
        thread_ts='1234567890.123456',
        selected_repo=None,
        should_extract=True,
        send_summary_instruction=True,
        conversation_id=conversation_id,
        slack_conversation=mock_conversation,
        team_id='T1234567890',
    )


# ---------------------------------------------------------------------------
# Test: V1 Conversation Creation
# ---------------------------------------------------------------------------


class TestV1ConversationCreation:
    """Test V1 conversation creation in Slack integration."""

    @patch.object(SlackNewConversationView, '_create_v1_conversation')
    async def test_v1_conversation_creation(
        self,
        mock_create_v1,
        slack_new_conversation_view,
        mock_jinja_env,
    ):
        """Test that V1 conversations are created correctly."""
        # Setup mocks
        mock_create_v1.return_value = None

        # Execute
        result = await slack_new_conversation_view.create_or_update_conversation(
            mock_jinja_env
        )

        # Verify
        assert result == slack_new_conversation_view.conversation_id
        mock_create_v1.assert_called_once()


# ---------------------------------------------------------------------------
# Test: Message Routing to V1
# ---------------------------------------------------------------------------


class TestMessageRouting:
    """Test that message sending routes to V1 method."""

    @patch.object(
        SlackUpdateExistingConversationView, 'send_message_to_v1_conversation'
    )
    async def test_message_routing_to_v1(
        self,
        mock_send_v1,
        slack_update_conversation_view_v1,
        mock_jinja_env,
    ):
        """Test that V1 conversations route to V1 message sending method."""
        # Setup
        mock_send_v1.return_value = None

        # Execute
        result = await slack_update_conversation_view_v1.create_or_update_conversation(
            mock_jinja_env
        )

        # Verify
        assert result == slack_update_conversation_view_v1.conversation_id
        mock_send_v1.assert_called_once_with(mock_jinja_env)


# ---------------------------------------------------------------------------
# Test: Paused Sandbox Resumption for V1 Conversations
# ---------------------------------------------------------------------------


class TestPausedSandboxResumption:
    """Test that paused sandboxes are resumed when sending messages to V1 conversations."""

    @patch('openhands.app_server.config.get_sandbox_service')
    @patch('openhands.app_server.config.get_app_conversation_info_service')
    @patch('openhands.app_server.config.get_httpx_client')
    @patch('openhands.app_server.event_callback.util.ensure_running_sandbox')
    @patch('openhands.app_server.event_callback.util.get_agent_server_url_from_sandbox')
    @patch.object(
        SlackUpdateExistingConversationView, '_get_instructions', new_callable=AsyncMock
    )
    async def test_paused_sandbox_resumption(
        self,
        mock_get_instructions,
        mock_get_agent_server_url,
        mock_ensure_running_sandbox,
        mock_get_httpx_client,
        mock_get_app_info_service,
        mock_get_sandbox_service,
        slack_update_conversation_view_v1,
        mock_jinja_env,
    ):
        """Test that paused sandboxes are resumed when sending messages to V1 conversations."""
        # Setup mocks
        mock_get_instructions.return_value = ('User message', '')

        # Mock app conversation info service
        mock_app_info_service = AsyncMock()
        mock_app_info = MagicMock()
        mock_app_info.sandbox_id = 'sandbox-123'
        mock_app_info_service.get_app_conversation_info.return_value = mock_app_info
        mock_get_app_info_service.return_value.__aenter__.return_value = (
            mock_app_info_service
        )

        # Mock sandbox service with paused sandbox that gets resumed
        mock_sandbox_service = AsyncMock()
        mock_paused_sandbox = MagicMock()
        mock_paused_sandbox.status = SandboxStatus.PAUSED
        mock_paused_sandbox.session_api_key = 'test-api-key'
        mock_paused_sandbox.exposed_urls = [
            MagicMock(name='AGENT_SERVER', url='http://localhost:8000')
        ]

        # After resume, sandbox becomes running
        mock_running_sandbox = MagicMock()
        mock_running_sandbox.status = SandboxStatus.RUNNING
        mock_running_sandbox.session_api_key = 'test-api-key'
        mock_running_sandbox.exposed_urls = [
            MagicMock(name='AGENT_SERVER', url='http://localhost:8000')
        ]

        mock_sandbox_service.get_sandbox.side_effect = [
            mock_paused_sandbox,
            mock_running_sandbox,
        ]
        mock_sandbox_service.resume_sandbox = AsyncMock()
        mock_get_sandbox_service.return_value.__aenter__.return_value = (
            mock_sandbox_service
        )

        # Mock ensure_running_sandbox to first raise RuntimeError, then return running sandbox
        mock_ensure_running_sandbox.side_effect = [
            RuntimeError('Sandbox not running: sandbox-123'),
            mock_running_sandbox,
        ]

        # Mock agent server URL
        mock_get_agent_server_url.return_value = 'http://localhost:8000'

        # Mock HTTP client
        mock_httpx_client = AsyncMock()
        mock_response = MagicMock()
        mock_response.raise_for_status = MagicMock()
        mock_httpx_client.post.return_value = mock_response
        mock_get_httpx_client.return_value.__aenter__.return_value = mock_httpx_client

        # Execute
        await slack_update_conversation_view_v1.send_message_to_v1_conversation(
            mock_jinja_env
        )

        # Verify sandbox was resumed
        mock_sandbox_service.resume_sandbox.assert_called_once_with('sandbox-123')
        mock_httpx_client.post.assert_called_once()
        mock_response.raise_for_status.assert_called_once()
