"""Tests for open command helpers and CLI."""

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from typer.testing import CliRunner

from repo_sync_kitty.cli import app
from repo_sync_kitty.commands.open import _find_matching_projects
from repo_sync_kitty.util.git import git_url_to_web_url


class TestGitUrlToWebUrl:
    """Tests for git_url_to_web_url helper."""

    def test_ssh_shorthand(self) -> None:
        assert (
            git_url_to_web_url("git@github.com:owner/repo")
            == "https://github.com/owner/repo"
        )

    def test_ssh_shorthand_with_git_suffix(self) -> None:
        assert (
            git_url_to_web_url("git@github.com:owner/repo.git")
            == "https://github.com/owner/repo"
        )

    def test_ssh_url(self) -> None:
        assert (
            git_url_to_web_url("ssh://git@github.com/owner/repo")
            == "https://github.com/owner/repo"
        )

    def test_ssh_url_with_git_suffix(self) -> None:
        assert (
            git_url_to_web_url("ssh://git@github.com/owner/repo.git")
            == "https://github.com/owner/repo"
        )

    def test_https_url(self) -> None:
        assert (
            git_url_to_web_url("https://github.com/owner/repo")
            == "https://github.com/owner/repo"
        )

    def test_https_url_with_git_suffix(self) -> None:
        assert (
            git_url_to_web_url("https://github.com/owner/repo.git")
            == "https://github.com/owner/repo"
        )

    def test_http_url_upgrades_to_https(self) -> None:
        assert (
            git_url_to_web_url("http://github.com/owner/repo")
            == "https://github.com/owner/repo"
        )

    def test_unrecognized_url_returns_none(self) -> None:
        assert git_url_to_web_url("ftp://example.com/repo") is None

    def test_strips_whitespace(self) -> None:
        assert (
            git_url_to_web_url("  https://github.com/owner/repo  ")
            == "https://github.com/owner/repo"
        )

    def test_gitlab_ssh(self) -> None:
        assert (
            git_url_to_web_url("git@gitlab.com:group/project")
            == "https://gitlab.com/group/project"
        )


class TestFindMatchingProjects:
    """Tests for _find_matching_projects helper."""

    @pytest.fixture
    def projects(self) -> list[MagicMock]:
        p1 = MagicMock()
        p1.slug = "owner/hello-world"
        p1.path = Path("libs/hello-world")
        p2 = MagicMock()
        p2.slug = "owner/other-repo"
        p2.path = Path("tools/other-repo")
        return [p1, p2]

    def test_exact_slug_match(self, projects: list[MagicMock]) -> None:
        result = _find_matching_projects(projects, "owner/hello-world")
        assert len(result) == 1
        assert result[0].slug == "owner/hello-world"

    def test_exact_path_match(self, projects: list[MagicMock]) -> None:
        result = _find_matching_projects(projects, "libs/hello-world")
        assert len(result) == 1

    def test_partial_path_suffix(self, projects: list[MagicMock]) -> None:
        result = _find_matching_projects(projects, "hello-world")
        assert len(result) >= 1

    def test_slug_repo_name(self, projects: list[MagicMock]) -> None:
        result = _find_matching_projects(projects, "other-repo")
        assert len(result) >= 1

    def test_no_match(self, projects: list[MagicMock]) -> None:
        result = _find_matching_projects(projects, "nonexistent")
        assert len(result) == 0

    def test_case_insensitive(self, projects: list[MagicMock]) -> None:
        result = _find_matching_projects(projects, "OWNER/HELLO-WORLD")
        assert len(result) == 1


class TestOpenCommand:
    """Tests for open_repo CLI command."""

    def test_open_no_manifest_shows_error(
        self, cli_runner: CliRunner, tmp_path: Path
    ) -> None:
        import os

        original = os.getcwd()
        os.chdir(tmp_path)
        try:
            result = cli_runner.invoke(app, ["open", "some-repo"])
            assert result.exit_code == 2
        finally:
            os.chdir(original)

    def test_open_no_match_shows_error(
        self, cli_runner: CliRunner, tmp_manifest: Path
    ) -> None:
        result = cli_runner.invoke(
            app, ["open", "nonexistent-repo", "-m", str(tmp_manifest)]
        )
        assert result.exit_code == 2
        assert "not found" in result.stdout.lower() or "error" in result.stdout.lower()

    @patch("repo_sync_kitty.commands.open.webbrowser")
    @patch("repo_sync_kitty.commands.open.RepoManager")
    def test_open_single_match_opens_browser(
        self,
        mock_mgr_cls: MagicMock,
        mock_wb: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
    ) -> None:
        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_remote_urls.return_value = {
            "origin": "https://github.com/octocat/Hello-World.git"
        }
        result = cli_runner.invoke(
            app, ["open", "Hello-World", "-m", str(tmp_manifest)]
        )
        assert result.exit_code == 0
        mock_wb.open.assert_called_once()

    @patch("repo_sync_kitty.commands.open.webbrowser")
    @patch("repo_sync_kitty.commands.open.RepoManager")
    def test_open_repo_not_cloned_uses_manifest_url(
        self,
        mock_mgr_cls: MagicMock,
        mock_wb: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
    ) -> None:
        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = False
        result = cli_runner.invoke(
            app, ["open", "Hello-World", "-m", str(tmp_manifest)]
        )
        assert result.exit_code == 0
        mock_wb.open.assert_called_once()

    @patch("repo_sync_kitty.commands.open.webbrowser")
    @patch("repo_sync_kitty.commands.open.RepoManager")
    def test_open_no_remotes_error(
        self,
        mock_mgr_cls: MagicMock,
        _mock_wb: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
    ) -> None:
        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_remote_urls.return_value = {}
        result = cli_runner.invoke(
            app, ["open", "Hello-World", "-m", str(tmp_manifest)]
        )
        assert result.exit_code == 1

    @patch("repo_sync_kitty.commands.open.webbrowser")
    @patch("repo_sync_kitty.commands.open.RepoManager")
    def test_open_with_remote_option(
        self,
        mock_mgr_cls: MagicMock,
        mock_wb: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
    ) -> None:
        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_remote_urls.return_value = {
            "origin": "https://github.com/octocat/Hello-World.git",
            "upstream": "https://github.com/other/Hello-World.git",
        }
        result = cli_runner.invoke(
            app, ["open", "Hello-World", "-m", str(tmp_manifest), "-r", "origin"]
        )
        assert result.exit_code == 0
        mock_wb.open.assert_called_once()

    def test_open_with_remote_flag_no_match_in_manifest(
        self, cli_runner: CliRunner, tmp_manifest: Path
    ) -> None:
        result = cli_runner.invoke(
            app, ["open", "nonexistent", "-m", str(tmp_manifest), "-r", "ghpub"]
        )
        # Remote "ghpub" exists in manifest but construct URL fails or opens
        assert result.exit_code in (0, 1, 2)

    def test_open_with_bad_remote_flag(
        self, cli_runner: CliRunner, tmp_manifest: Path
    ) -> None:
        result = cli_runner.invoke(
            app, ["open", "nonexistent", "-m", str(tmp_manifest), "-r", "badremote"]
        )
        assert result.exit_code == 2

    @patch("repo_sync_kitty.commands.open.webbrowser")
    @patch("repo_sync_kitty.commands.open.RepoManager")
    def test_open_with_org_filter(
        self,
        mock_mgr_cls: MagicMock,
        _mock_wb: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
    ) -> None:
        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_remote_urls.return_value = {
            "origin": "https://github.com/octocat/Hello-World.git",
        }
        result = cli_runner.invoke(
            app, ["open", "Hello-World", "-m", str(tmp_manifest), "-o", "octocat"]
        )
        assert result.exit_code == 0
