"""Tests for GitHub API client."""

from unittest.mock import MagicMock, patch

import pytest

from repo_sync_kitty.forge.github import (
    GitHubAPIError,
    GitHubAuthError,
    GitHubClient,
    GitHubRateLimitError,
    GitHubRepoExistsError,
)


class TestGitHubClientInit:
    """Tests for GitHubClient initialization."""

    def test_stores_token(self) -> None:
        client = GitHubClient("test-token")
        assert client.token == "test-token"

    def test_headers_include_auth(self) -> None:
        client = GitHubClient("test-token")
        headers = client._headers()
        assert headers["Authorization"] == "Bearer test-token"
        assert "Accept" in headers


class TestHandleError:
    """Tests for _handle_error method."""

    def test_401_raises_auth_error(self) -> None:
        client = GitHubClient("token")
        response = MagicMock()
        response.status_code = 401
        with pytest.raises(GitHubAuthError):
            client._handle_error(response)

    def test_403_rate_limit(self) -> None:
        client = GitHubClient("token")
        response = MagicMock()
        response.status_code = 403
        response.text = "API rate limit exceeded"
        with pytest.raises(GitHubRateLimitError):
            client._handle_error(response)

    def test_403_other(self) -> None:
        client = GitHubClient("token")
        response = MagicMock()
        response.status_code = 403
        response.text = "Resource not accessible"
        with pytest.raises(GitHubAPIError, match="forbidden"):
            client._handle_error(response)

    def test_422_repo_exists(self) -> None:
        client = GitHubClient("token")
        response = MagicMock()
        response.status_code = 422
        response.json.return_value = {
            "errors": [{"message": "name already exists on this account"}]
        }
        with pytest.raises(GitHubRepoExistsError):
            client._handle_error(response)

    def test_422_other_validation(self) -> None:
        client = GitHubClient("token")
        response = MagicMock()
        response.status_code = 422
        response.json.return_value = {"errors": [{"message": "other error"}]}
        with pytest.raises(GitHubAPIError, match="Validation"):
            client._handle_error(response)

    def test_500_server_error(self) -> None:
        client = GitHubClient("token")
        response = MagicMock()
        response.status_code = 500
        response.text = "Internal Server Error"
        with pytest.raises(GitHubAPIError, match="500"):
            client._handle_error(response)


class TestRepoExists:
    """Tests for repo_exists method."""

    @patch("repo_sync_kitty.forge.github.httpx.get")
    def test_returns_true_when_200(self, mock_get: MagicMock) -> None:
        mock_get.return_value.status_code = 200
        client = GitHubClient("token")
        assert client.repo_exists("owner", "repo") is True

    @patch("repo_sync_kitty.forge.github.httpx.get")
    def test_returns_false_when_404(self, mock_get: MagicMock) -> None:
        mock_get.return_value.status_code = 404
        client = GitHubClient("token")
        assert client.repo_exists("owner", "repo") is False


class TestCreateRepo:
    """Tests for create_repo method."""

    @patch("repo_sync_kitty.forge.github.httpx.post")
    @patch("repo_sync_kitty.forge.github.httpx.get")
    def test_creates_for_org(self, mock_get: MagicMock, mock_post: MagicMock) -> None:
        # is_organization returns True
        mock_get.return_value.status_code = 200
        mock_post.return_value.status_code = 201
        mock_post.return_value.json.return_value = {"name": "repo"}
        client = GitHubClient("token")
        result = client.create_repo("org", "repo")
        assert result["name"] == "repo"
        call_url = mock_post.call_args[0][0]
        assert "/orgs/org/repos" in call_url

    @patch("repo_sync_kitty.forge.github.httpx.post")
    @patch("repo_sync_kitty.forge.github.httpx.get")
    def test_creates_for_user(self, mock_get: MagicMock, mock_post: MagicMock) -> None:
        # is_organization returns False (404)
        mock_get.return_value.status_code = 404
        mock_post.return_value.status_code = 201
        mock_post.return_value.json.return_value = {"name": "repo"}
        client = GitHubClient("token")
        result = client.create_repo("user", "repo")
        assert result["name"] == "repo"
        call_url = mock_post.call_args[0][0]
        assert "/user/repos" in call_url


class TestUpdateRepoSettings:
    """Tests for update_repo_settings method."""

    @patch("repo_sync_kitty.forge.github.httpx.patch")
    def test_success(self, mock_patch: MagicMock) -> None:
        mock_patch.return_value.status_code = 200
        client = GitHubClient("token")
        client.update_repo_settings("owner", "repo")

    @patch("repo_sync_kitty.forge.github.httpx.patch")
    def test_failure_raises(self, mock_patch: MagicMock) -> None:
        mock_patch.return_value.status_code = 500
        mock_patch.return_value.text = "Server Error"
        client = GitHubClient("token")
        with pytest.raises(GitHubAPIError):
            client.update_repo_settings("owner", "repo")


class TestGetRepoWebUrl:
    """Tests for get_repo_web_url method."""

    def test_returns_correct_url(self) -> None:
        client = GitHubClient("token")
        assert (
            client.get_repo_web_url("owner", "repo") == "https://github.com/owner/repo"
        )
