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

import os
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.enroll import resolve_github_token, resolve_repo_path


class TestResolveRepoPath:
    """Tests for resolve_repo_path helper."""

    def test_relative_to_root(self, tmp_path: Path) -> None:
        root = tmp_path / "root"
        root.mkdir()
        result = resolve_repo_path("myrepo", root)
        assert result == (root / "myrepo").resolve()

    def test_dot_slash_relative_to_cwd(self, tmp_path: Path) -> None:
        original = os.getcwd()
        os.chdir(tmp_path)
        try:
            result = resolve_repo_path("./myrepo", Path("/unused"))
            assert result == (tmp_path / "myrepo").resolve()
        finally:
            os.chdir(original)

    def test_absolute_path(self, tmp_path: Path) -> None:
        result = resolve_repo_path(str(tmp_path / "myrepo"), Path("/unused"))
        assert result == (tmp_path / "myrepo").resolve()

    def test_tilde_expansion(self) -> None:
        result = resolve_repo_path("~/myrepo", Path("/unused"))
        assert result == Path("~/myrepo").expanduser().resolve()

    def test_nested_relative(self, tmp_path: Path) -> None:
        root = tmp_path / "root"
        root.mkdir()
        result = resolve_repo_path("libs/myrepo", root)
        assert result == (root / "libs" / "myrepo").resolve()


class TestResolveGithubToken:
    """Tests for resolve_github_token helper."""

    def test_explicit_token_returned(self) -> None:
        assert resolve_github_token("my-token") == "my-token"

    def test_github_token_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "env-token")
        monkeypatch.delenv("GH_TOKEN", raising=False)
        assert resolve_github_token(None) == "env-token"

    def test_gh_token_env_fallback(self, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.delenv("GITHUB_TOKEN", raising=False)
        monkeypatch.setenv("GH_TOKEN", "gh-token")
        assert resolve_github_token(None) == "gh-token"

    def test_returns_none_when_no_token(self, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.delenv("GITHUB_TOKEN", raising=False)
        monkeypatch.delenv("GH_TOKEN", raising=False)
        assert resolve_github_token(None) is None

    def test_explicit_takes_precedence(self, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "env-token")
        assert resolve_github_token("explicit") == "explicit"


class TestEnrollCommand:
    """Tests for enroll CLI command."""

    def test_no_manifest_shows_error(
        self, cli_runner: CliRunner, tmp_path: Path
    ) -> None:
        original = os.getcwd()
        os.chdir(tmp_path)
        try:
            result = cli_runner.invoke(app, ["enroll", "myrepo"])
            assert result.exit_code == 2
        finally:
            os.chdir(original)

    def test_no_token_shows_error(
        self, cli_runner: CliRunner, tmp_manifest: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        monkeypatch.delenv("GITHUB_TOKEN", raising=False)
        monkeypatch.delenv("GH_TOKEN", raising=False)
        result = cli_runner.invoke(app, ["enroll", "myrepo", "-m", str(tmp_manifest)])
        assert result.exit_code == 2
        assert "token" in result.stdout.lower()

    @patch("repo_sync_kitty.commands.enroll.RepoManager")
    def test_path_not_found(
        self,
        _mock_mgr_cls: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "test-token")
        result = cli_runner.invoke(
            app, ["enroll", "nonexistent-repo", "-m", str(tmp_manifest)]
        )
        assert result.exit_code == 2

    @patch("repo_sync_kitty.commands.enroll.RepoManager")
    def test_not_a_git_repo(
        self,
        mock_mgr_cls: MagicMock,
        cli_runner: CliRunner,
        tmp_manifest: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "test-token")
        repo_dir = Path("/tmp/test-repos/somerepo")
        repo_dir.mkdir(parents=True, exist_ok=True)
        mock_mgr_cls.return_value.exists.return_value = False
        result = cli_runner.invoke(app, ["enroll", "somerepo", "-m", str(tmp_manifest)])
        assert result.exit_code == 2

    @patch("repo_sync_kitty.commands.enroll.GitHubClient")
    @patch("repo_sync_kitty.commands.enroll.RepoManager")
    def test_dry_run_does_not_create(
        self,
        mock_mgr_cls: MagicMock,
        mock_gh_cls: MagicMock,
        cli_runner: CliRunner,
        tmp_path: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "test-token")

        # Create manifest with owner in base_url
        manifest = tmp_path / "manifest.toml"
        manifest.write_text("""
[common]
root_path = "/tmp/test-repos"
branch = "main"
remote = "origin"

[[remotes]]
name = "origin"
base_url = "https://github.com/myorg/"
""")
        repo_dir = Path("/tmp/test-repos/myrepo")
        repo_dir.mkdir(parents=True, exist_ok=True)

        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_current_branch.return_value = "main"

        result = cli_runner.invoke(
            app, ["enroll", "myrepo", "-m", str(manifest), "--dry-run"]
        )
        assert result.exit_code == 0
        assert "dry run" in result.stdout.lower()
        mock_gh_cls.assert_not_called()

    @patch("repo_sync_kitty.commands.enroll.RepoManager")
    def test_detached_head_error(
        self,
        mock_mgr_cls: MagicMock,
        cli_runner: CliRunner,
        tmp_path: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "test-token")

        manifest = tmp_path / "manifest.toml"
        manifest.write_text("""
[common]
root_path = "/tmp/test-repos"
branch = "main"
remote = "origin"

[[remotes]]
name = "origin"
base_url = "https://github.com/myorg/"
""")
        repo_dir = Path("/tmp/test-repos/myrepo")
        repo_dir.mkdir(parents=True, exist_ok=True)

        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_current_branch.return_value = None

        result = cli_runner.invoke(app, ["enroll", "myrepo", "-m", str(manifest)])
        assert result.exit_code == 2
        assert "detached" in result.stdout.lower()

    @patch("repo_sync_kitty.commands.enroll.webbrowser")
    @patch("repo_sync_kitty.commands.enroll.GitHubClient")
    @patch("repo_sync_kitty.commands.enroll.RepoManager")
    def test_repo_already_exists(
        self,
        mock_mgr_cls: MagicMock,
        mock_gh_cls: MagicMock,
        _mock_wb: MagicMock,
        cli_runner: CliRunner,
        tmp_path: Path,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        monkeypatch.setenv("GITHUB_TOKEN", "test-token")

        manifest = tmp_path / "manifest.toml"
        manifest.write_text("""
[common]
root_path = "/tmp/test-repos"
branch = "main"
remote = "origin"

[[remotes]]
name = "origin"
base_url = "https://github.com/myorg/"
""")
        repo_dir = Path("/tmp/test-repos/myrepo")
        repo_dir.mkdir(parents=True, exist_ok=True)

        mgr = mock_mgr_cls.return_value
        mgr.exists.return_value = True
        mgr.get_current_branch.return_value = "main"

        gh = mock_gh_cls.return_value
        gh.repo_exists.return_value = True

        result = cli_runner.invoke(app, ["enroll", "myrepo", "-m", str(manifest)])
        assert result.exit_code == 1
        assert "exists" in result.stdout.lower()
