"""Tests for the version check module."""

import json
import socket
from unittest.mock import patch, MagicMock

from spoq_server.version_check import (
    VersionCheckResult,
    check_version,
    warn_if_outdated,
)


# ---------------------------------------------------------------------------
# check_version
# ---------------------------------------------------------------------------

def _mock_urlopen(payload: dict):
    """Return a MagicMock that behaves like a urlopen context manager."""
    resp = MagicMock()
    resp.read.return_value = json.dumps(payload).encode()
    resp.__enter__ = lambda s: s
    resp.__exit__ = MagicMock(return_value=False)
    return resp


def test_detects_outdated_version():
    """Remote version differs from local — is_outdated should be True."""
    with patch(
        "spoq_server.version_check.urllib.request.urlopen",
        return_value=_mock_urlopen({"version": "99.0.0"}),
    ):
        result = check_version()
    assert result.is_outdated is True
    assert result.remote_version == "99.0.0"
    assert result.error is None


def test_detects_current_version():
    """Remote version matches local — is_outdated should be False."""
    from spoq_server import __version__

    with patch(
        "spoq_server.version_check.urllib.request.urlopen",
        return_value=_mock_urlopen({"version": __version__}),
    ):
        result = check_version()
    assert result.is_outdated is False
    assert result.remote_version == __version__
    assert result.error is None


def test_handles_network_failure():
    """Network errors are captured, not raised."""
    with patch(
        "spoq_server.version_check.urllib.request.urlopen",
        side_effect=ConnectionError("no network"),
    ):
        result = check_version()
    assert result.is_outdated is False
    assert result.error is not None
    assert "no network" in result.error


def test_handles_timeout():
    """Socket timeout is captured, not raised."""
    with patch(
        "spoq_server.version_check.urllib.request.urlopen",
        side_effect=socket.timeout("timed out"),
    ):
        result = check_version()
    assert result.is_outdated is False
    assert result.error is not None


def test_handles_malformed_json():
    """Invalid JSON from the remote is captured."""
    resp = MagicMock()
    resp.read.return_value = b"not json"
    resp.__enter__ = lambda s: s
    resp.__exit__ = MagicMock(return_value=False)

    with patch(
        "spoq_server.version_check.urllib.request.urlopen",
        return_value=resp,
    ):
        result = check_version()
    assert result.is_outdated is False
    assert result.error is not None


# ---------------------------------------------------------------------------
# warn_if_outdated
# ---------------------------------------------------------------------------

def test_warns_on_stderr_when_outdated(capsys):
    """Outdated version prints a warning to stderr."""
    result = VersionCheckResult(
        is_outdated=True,
        local_version="0.1.0",
        remote_version="0.2.0",
    )
    warn_if_outdated(result)
    captured = capsys.readouterr()
    assert "Update available" in captured.err
    assert "0.2.0" in captured.err
    assert "pipx upgrade spoq" in captured.err


def test_silent_when_current(capsys):
    """Current version produces no output."""
    result = VersionCheckResult(
        is_outdated=False,
        local_version="0.2.0",
        remote_version="0.2.0",
    )
    warn_if_outdated(result)
    captured = capsys.readouterr()
    assert captured.err == ""


# ---------------------------------------------------------------------------
# spoq_ping integration
# ---------------------------------------------------------------------------

def test_ping_includes_update_notice_when_outdated():
    """spoq_ping includes update fields when a newer version exists."""
    from spoq_server import server

    old = server._version_check_result
    try:
        server._version_check_result = VersionCheckResult(
            is_outdated=True,
            local_version="0.1.0",
            remote_version="0.2.0",
        )
        ping = json.loads(server.spoq_ping())
        assert ping["update_available"] == "0.2.0"
        assert "Update available" in ping["update_notice"]
    finally:
        server._version_check_result = old


def test_ping_omits_update_fields_when_current():
    """spoq_ping has no update fields when version is current."""
    from spoq_server import server

    old = server._version_check_result
    try:
        server._version_check_result = VersionCheckResult(
            is_outdated=False,
            local_version="0.2.0",
            remote_version="0.2.0",
        )
        ping = json.loads(server.spoq_ping())
        assert "update_available" not in ping
        assert "update_notice" not in ping
    finally:
        server._version_check_result = old
