#!/usr/bin/python3
"""Tests for slpkg/main.py — check_for_repositories() and main() entry point."""

import sys
from unittest.mock import MagicMock, patch

import pytest

from slpkg.main import check_for_repositories, main


# ── check_for_repositories ────────────────────────────────────────────────────


class TestCheckForRepositories:
    def _args(self, command: str = "install") -> MagicMock:
        args = MagicMock()
        args.command = command
        return args

    def _repos(self, repos_dict: dict) -> MagicMock:
        mock = MagicMock()
        mock.repositories = repos_dict
        mock.sbo_repo_name = "sbo"
        mock.ponce_repo_name = "ponce"
        return mock

    def test_valid_enabled_repo_passes(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({"slack": {"enable": True}})):
            check_for_repositories("slack", self._args(), parser, {})
        parser.error.assert_not_called()

    def test_nonexistent_repo_raises_error(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({"slack": {"enable": True}})):
            check_for_repositories("nonexistent", self._args(), parser, {})
        parser.error.assert_called_once_with("Repository 'nonexistent' does not exist.")

    def test_disabled_repo_raises_error(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({"slack": {"enable": False}})):
            check_for_repositories("slack", self._args(), parser, {})
        parser.error.assert_called_once_with("Repository 'slack' is not enabled.")

    def test_empty_repository_skips_validation(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({})):
            check_for_repositories("", self._args(), parser, {})
        parser.error.assert_not_called()

    def test_wildcard_with_search_is_allowed(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({})):
            check_for_repositories("*", self._args("search"), parser, {})
        parser.error.assert_not_called()

    def test_wildcard_with_upgrade_is_allowed(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({})):
            check_for_repositories("*", self._args("upgrade"), parser, {})
        parser.error.assert_not_called()

    def test_wildcard_with_disallowed_command_raises_error(self):
        parser = MagicMock()
        with patch("slpkg.main.Repositories", return_value=self._repos({})):
            check_for_repositories("*", self._args("list"), parser, {})
        parser.error.assert_called_once_with("Repository '*' is not allowed with this command.")

    def test_build_with_non_sbo_repo_raises_error(self):
        parser = MagicMock()
        repos = self._repos({"slack": {"enable": True}})
        with patch("slpkg.main.Repositories", return_value=repos):
            check_for_repositories("slack", self._args("build"), parser, {})
        parser.error.assert_called_with("Repository 'slack' is not allowed with this command.")

    def test_build_with_sbo_repo_passes(self):
        parser = MagicMock()
        repos = self._repos({"sbo": {"enable": True}})
        with patch("slpkg.main.Repositories", return_value=repos):
            check_for_repositories("sbo", self._args("build"), parser, {})
        parser.error.assert_not_called()


# ── main() entry point ────────────────────────────────────────────────────────


class TestMain:
    """Tests for the main() dispatch function.

    Run() is always patched at slpkg.main.Run to keep tests focused on
    argument parsing and dispatch logic only.
    """

    def test_no_args_exits_with_error(self):
        with (
            patch.object(sys, "argv", ["slpkg"]),
            patch("slpkg.main.config_load"),
            pytest.raises(SystemExit),
        ):
            main()

    def test_bare_help_flag_exits_with_error(self):
        with (
            patch.object(sys, "argv", ["slpkg", "--help"]),
            patch("slpkg.main.config_load"),
            pytest.raises(SystemExit),
        ):
            main()

    def test_version_command_dispatches_to_runner_version(self):
        with (
            patch.object(sys, "argv", ["slpkg", "version"]),
            patch("slpkg.main.config_load"),
            patch("slpkg.main.check_for_repositories"),
            patch("slpkg.main.Run") as mock_run_cls,
        ):
            mock_run_cls.return_value.version.side_effect = SystemExit(0)
            with pytest.raises(SystemExit) as exc:
                main()
        assert exc.value.code == 0
        mock_run_cls.return_value.version.assert_called_once()

    def test_help_command_prints_usage(self, capsys):
        with (
            patch.object(sys, "argv", ["slpkg", "help"]),
            patch("slpkg.main.config_load"),
            patch("slpkg.main.check_for_repositories"),
            patch("slpkg.main.Run"),
            patch("slpkg.cli_parser.config_load") as mock_cfg,
        ):
            mock_cfg.bold = ""
            mock_cfg.endc = ""
            try:
                main()
            except SystemExit:
                pass
        assert "Usage" in capsys.readouterr().out

    def test_all_alias_normalized_to_wildcard(self):
        """-o all must be converted to '*' before Run() is called."""
        with (
            patch.object(sys, "argv", ["slpkg", "-o", "all", "search", "pkg"]),
            patch("slpkg.main.config_load"),
            patch("slpkg.main.check_for_repositories"),
            patch("slpkg.main.Run") as mock_run_cls,
        ):
            mock_run_cls.return_value.search.side_effect = SystemExit(0)
            with pytest.raises(SystemExit):
                main()
        _, repo_arg = mock_run_cls.call_args[0]
        assert repo_arg == "*"

    def test_install_command_dispatches_packages_to_runner(self):
        with (
            patch.object(sys, "argv", ["slpkg", "-o", "slack", "install", "pkg1"]),
            patch("slpkg.main.config_load"),
            patch("slpkg.main.check_for_repositories"),
            patch("slpkg.main.Run") as mock_run_cls,
        ):
            mock_run_cls.return_value.install.side_effect = SystemExit(0)
            with pytest.raises(SystemExit):
                main()
        mock_run_cls.return_value.install.assert_called_once_with(["pkg1"])

    def test_unknown_command_exits_with_error(self, capsys):
        with (
            patch.object(sys, "argv", ["slpkg", "no-such-command"]),
            patch("slpkg.main.config_load"),
            pytest.raises(SystemExit),
        ):
            main()
