"""Tests for SocketIOService.__init_singleton and singleton behavior."""

from unittest.mock import patch
import pytest
from flask import Flask

from local_deep_research.web.services.socket_service import SocketIOService


@pytest.fixture(autouse=True)
def reset_singleton():
    """Reset singleton before each test."""
    SocketIOService._instance = None
    yield
    SocketIOService._instance = None


@pytest.fixture
def minimal_app():
    """Create a minimal Flask app (no create_app() which triggers SocketIOService)."""
    app = Flask(__name__)
    app.config["SECRET_KEY"] = "test"
    app.config["TESTING"] = True
    return app


class TestSocketIOServiceSingleton:
    """Tests for singleton creation and behavior."""

    def test_requires_app_on_first_creation(self):
        """Should raise ValueError if no app provided on first init."""
        with pytest.raises(ValueError, match="Flask app must be specified"):
            SocketIOService()

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_creates_singleton_with_app(
        self, mock_env, mock_socketio, minimal_app
    ):
        mock_env.return_value = None

        service = SocketIOService(app=minimal_app)
        assert service is not None
        assert SocketIOService._instance is service

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_second_call_returns_same_instance(
        self, mock_env, mock_socketio, minimal_app
    ):
        mock_env.return_value = None

        first = SocketIOService(app=minimal_app)
        second = SocketIOService(app=minimal_app)
        assert first is second

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_second_call_without_app_returns_existing(
        self, mock_env, mock_socketio, minimal_app
    ):
        mock_env.return_value = None

        first = SocketIOService(app=minimal_app)
        second = SocketIOService()  # No app needed on second call
        assert first is second


class TestSocketIOServiceCorsConfig:
    """Tests for WebSocket CORS configuration in __init_singleton."""

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_default_cors_is_wildcard(
        self, mock_env, mock_socketio, minimal_app
    ):
        """No env var set -> permissive default (*)."""
        mock_env.return_value = None

        SocketIOService(app=minimal_app)
        mock_socketio.assert_called_once()
        call_kwargs = mock_socketio.call_args[1]
        assert call_kwargs["cors_allowed_origins"] == "*"

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_wildcard_env_setting(self, mock_env, mock_socketio, minimal_app):
        mock_env.return_value = "*"

        SocketIOService(app=minimal_app)
        call_kwargs = mock_socketio.call_args[1]
        assert call_kwargs["cors_allowed_origins"] == "*"

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_specific_origins(self, mock_env, mock_socketio, minimal_app):
        mock_env.return_value = "https://a.com, https://b.com"

        SocketIOService(app=minimal_app)
        call_kwargs = mock_socketio.call_args[1]
        assert call_kwargs["cors_allowed_origins"] == [
            "https://a.com",
            "https://b.com",
        ]

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_empty_env_disables_cors(
        self, mock_env, mock_socketio, minimal_app
    ):
        """Empty string env var -> same-origin only (None)."""
        mock_env.return_value = ""

        SocketIOService(app=minimal_app)
        call_kwargs = mock_socketio.call_args[1]
        assert call_kwargs["cors_allowed_origins"] is None

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_socketio_async_mode_threading(
        self, mock_env, mock_socketio, minimal_app
    ):
        mock_env.return_value = None

        SocketIOService(app=minimal_app)
        call_kwargs = mock_socketio.call_args[1]
        assert call_kwargs["async_mode"] == "threading"

    @patch("local_deep_research.web.services.socket_service.SocketIO")
    @patch("local_deep_research.settings.env_registry.get_env_setting")
    def test_socketio_path(self, mock_env, mock_socketio, minimal_app):
        mock_env.return_value = None

        SocketIOService(app=minimal_app)
        call_kwargs = mock_socketio.call_args[1]
        assert call_kwargs["path"] == "/socket.io"
