"""Tests for launcher.updater module."""

from __future__ import annotations

import json
from unittest.mock import MagicMock, patch


class TestParseVersion:
    """Test semantic version parsing."""

    def test_parses_prefixed_version(self):
        """Should parse 'v6.5.3' into (6, 5, 3)."""
        from launcher.updater import _parse_version

        assert _parse_version("v6.5.3") == (6, 5, 3)

    def test_parses_unprefixed_version(self):
        """Should parse '6.5.3' without 'v' prefix."""
        from launcher.updater import _parse_version

        assert _parse_version("6.5.3") == (6, 5, 3)

    def test_parses_two_part_version(self):
        """Should parse 'v1.0' into (1, 0)."""
        from launcher.updater import _parse_version

        assert _parse_version("v1.0") == (1, 0)

    def test_handles_uppercase_v(self):
        """Should handle 'V6.5.3'."""
        from launcher.updater import _parse_version

        assert _parse_version("V6.5.3") == (6, 5, 3)

    def test_handles_invalid_input(self):
        """Should return empty tuple for garbage input."""
        from launcher.updater import _parse_version

        assert _parse_version("not-a-version") == ()

    def test_stops_at_non_numeric_segment(self):
        """Should stop parsing at non-numeric segment."""
        from launcher.updater import _parse_version

        assert _parse_version("1.2.beta") == (1, 2)


class TestFetchLatestRelease:
    """Test GitHub API fetching."""

    def test_returns_dict_on_success(self):
        """Should return release dict on successful fetch."""
        mock_data = json.dumps({"tag_name": "v7.0.0", "html_url": "https://github.com/releases/v7.0.0"}).encode()
        mock_response = MagicMock()
        mock_response.read.return_value = mock_data
        mock_response.__enter__ = lambda s: s
        mock_response.__exit__ = MagicMock(return_value=False)

        with patch("urllib.request.urlopen", return_value=mock_response):
            from launcher.updater import _fetch_latest_release

            result = _fetch_latest_release()

        assert result is not None
        assert result["tag_name"] == "v7.0.0"

    def test_returns_none_on_network_error(self):
        """Should return None on network failure."""
        import urllib.error

        with patch("urllib.request.urlopen", side_effect=urllib.error.URLError("timeout")):
            from launcher.updater import _fetch_latest_release

            result = _fetch_latest_release()

        assert result is None

    def test_returns_none_on_invalid_json(self):
        """Should return None when response is not valid JSON."""
        mock_response = MagicMock()
        mock_response.read.return_value = b"not json"
        mock_response.__enter__ = lambda s: s
        mock_response.__exit__ = MagicMock(return_value=False)

        with patch("urllib.request.urlopen", return_value=mock_response):
            from launcher.updater import _fetch_latest_release

            result = _fetch_latest_release()

        assert result is None


class TestCheckForUpdates:
    """Test the update check workflow."""

    def test_returns_true_when_newer_available(self):
        """Should return True when GitHub has a newer version."""
        with patch(
            "launcher.updater._fetch_latest_release",
            return_value={"tag_name": "v99.0.0", "html_url": "https://example.com"},
        ):
            from launcher.updater import check_for_updates

            result = check_for_updates(quiet=True)

        assert result is True

    def test_returns_false_when_current(self):
        """Should return False when already on latest."""
        from launcher import __version__

        with patch("launcher.updater._fetch_latest_release", return_value={"tag_name": f"v{__version__}"}):
            from launcher.updater import check_for_updates

            result = check_for_updates(quiet=True)

        assert result is False

    def test_quiet_mode_suppresses_output(self, capsys):
        """Should not print anything in quiet mode."""
        with patch(
            "launcher.updater._fetch_latest_release",
            return_value={"tag_name": "v99.0.0", "html_url": "https://example.com"},
        ):
            from launcher.updater import check_for_updates

            check_for_updates(quiet=True)

        captured = capsys.readouterr()
        assert captured.out == ""

    def test_handles_network_failure(self):
        """Should return False when GitHub is unreachable."""
        with patch("launcher.updater._fetch_latest_release", return_value=None):
            from launcher.updater import check_for_updates

            result = check_for_updates(quiet=True)

        assert result is False

    def test_handles_missing_tag_name(self):
        """Should return False when release has no tag_name."""
        with patch("launcher.updater._fetch_latest_release", return_value={"html_url": "https://example.com"}):
            from launcher.updater import check_for_updates

            result = check_for_updates(quiet=True)

        assert result is False

    def test_handles_empty_tag_name(self):
        """Should return False when tag_name is an empty string."""
        with patch("launcher.updater._fetch_latest_release", return_value={"tag_name": ""}):
            from launcher.updater import check_for_updates

            result = check_for_updates(quiet=True)

        assert result is False

    def test_non_quiet_prints_release_url_when_update_available(self, capsys):
        """Should print release URL when an update is available in non-quiet mode."""
        with patch(
            "launcher.updater._fetch_latest_release",
            return_value={"tag_name": "v99.0.0", "html_url": "https://github.com/releases/v99.0.0"},
        ):
            from launcher.updater import check_for_updates

            check_for_updates(quiet=False)

        captured = capsys.readouterr()
        assert "v99.0.0" in captured.out
        assert "https://github.com/releases/v99.0.0" in captured.out

    def test_non_quiet_uses_fallback_url_when_html_url_missing(self, capsys):
        """Should use GITHUB_RELEASES_URL as fallback when html_url is absent."""
        with patch(
            "launcher.updater._fetch_latest_release",
            return_value={"tag_name": "v99.0.0"},
        ):
            from launcher.updater import GITHUB_RELEASES_URL, check_for_updates

            check_for_updates(quiet=False)

        captured = capsys.readouterr()
        assert GITHUB_RELEASES_URL in captured.out


class TestParseVersionEdgeCases:
    """Additional edge cases for _parse_version."""

    def test_empty_string_returns_empty_tuple(self):
        from launcher.updater import _parse_version

        assert _parse_version("") == ()

    def test_whitespace_only_returns_empty_tuple(self):
        from launcher.updater import _parse_version

        assert _parse_version("  ") == ()

    def test_zero_version(self):
        from launcher.updater import _parse_version

        assert _parse_version("0.0.0") == (0, 0, 0)

    def test_prerelease_suffix_stops_at_non_numeric(self):
        """'1.2.0-beta.1' should parse as (1, 2, 0) stopping at '-beta'."""
        from launcher.updater import _parse_version

        # The segment "0-beta" is not a pure int, so it stops at "1.2" or handles gracefully.
        # The actual behavior: "0-beta".split(".") won't occur since split(".") gives ["1","2","0-beta"]
        # int("0-beta") raises ValueError → break → returns (1, 2)
        result = _parse_version("1.2.0-beta.1")
        assert result == (1, 2)

    def test_fetch_returns_none_on_http_error(self):
        """HTTPError (e.g. 404) should return None, not raise."""
        import urllib.error

        with patch("urllib.request.urlopen", side_effect=urllib.error.HTTPError("url", 404, "Not Found", {}, None)):  # type: ignore[arg-type]
            from launcher.updater import _fetch_latest_release

            result = _fetch_latest_release()

        assert result is None
