"""
Comprehensive tests for the GitHub search engine.
Tests initialization, search functionality, and API configuration.
"""

import base64
import pytest
from unittest.mock import Mock, patch


class TestGitHubSearchEngineInit:
    """Tests for GitHub search engine initialization."""

    def test_init_default_parameters(self):
        """Test initialization with default parameters."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        assert engine.max_results >= 10
        # Default search type is repositories
        assert engine.search_type == "repositories"
        assert engine.api_key is None

    def test_init_with_api_key(self):
        """Test initialization with GitHub API key."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(api_key="ghp_test_token_123")
        assert engine.api_key == "ghp_test_token_123"

    def test_init_with_search_type(self):
        """Test initialization with specific search type."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="code")
        assert engine.search_type == "code"

    def test_init_with_custom_max_results(self):
        """Test initialization with custom max results."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(max_results=50)
        assert engine.max_results >= 50


class TestGitHubSearchExecution:
    """Tests for GitHub search execution."""

    @pytest.fixture
    def github_engine(self):
        """Create a GitHub engine instance."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        return GitHubSearchEngine(max_results=10)

    def test_engine_initialization(self, github_engine):
        """Test that engine is properly initialized."""
        assert github_engine is not None
        assert github_engine.max_results >= 10

    def test_engine_has_api_base(self, github_engine):
        """Test that engine has API base URL configured."""
        assert hasattr(github_engine, "api_base")
        assert "api.github.com" in github_engine.api_base


class TestGitHubAPIConfiguration:
    """Tests for GitHub API configuration."""

    def test_api_key_in_headers(self):
        """Test that API key is included in headers when provided."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(api_key="ghp_my_token")
        assert engine.api_key == "ghp_my_token"
        # Check that Authorization header is set
        assert "Authorization" in engine.headers
        assert "ghp_my_token" in engine.headers["Authorization"]

    def test_no_api_key_anonymous_access(self):
        """Test that engine works without API key (anonymous access)."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        assert engine.api_key is None
        # Authorization header should not be present without API key
        assert "Authorization" not in engine.headers


class TestGitHubSearchTypes:
    """Tests for different GitHub search types."""

    def test_repository_search_type(self):
        """Test repository search type configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="repositories")
        assert engine.search_type == "repositories"

    def test_code_search_type(self):
        """Test code search type configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="code")
        assert engine.search_type == "code"

    def test_issues_search_type(self):
        """Test issues search type configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="issues")
        assert engine.search_type == "issues"

    def test_users_search_type(self):
        """Test users search type configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="users")
        assert engine.search_type == "users"


class TestGitHubEngineType:
    """Tests for GitHub engine type identification."""

    def test_engine_type_set(self):
        """Test that engine type is properly set."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        assert "github" in engine.engine_type.lower()


class TestGitHubHeaders:
    """Tests for GitHub request headers."""

    def test_accept_header_set(self):
        """Test that Accept header is set for API compatibility."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        assert "Accept" in engine.headers
        assert "application/vnd.github" in engine.headers["Accept"]

    def test_user_agent_header_set(self):
        """Test that User-Agent header is set."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        assert "User-Agent" in engine.headers


class TestGitHubAdvancedInit:
    """Tests for advanced initialization options."""

    def test_init_with_readme_option(self):
        """Test initialization with include_readme option."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(include_readme=False)
        assert engine.include_readme is False

    def test_init_with_issues_option(self):
        """Test initialization with include_issues option."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(include_issues=True)
        assert engine.include_issues is True

    def test_init_with_llm(self):
        """Test initialization with LLM."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_llm = Mock()
        engine = GitHubSearchEngine(llm=mock_llm)
        assert engine.llm is mock_llm


class TestGitHubRateLimits:
    """Tests for rate limit handling."""

    def test_handle_rate_limits_normal(self):
        """Test rate limit handling with normal remaining."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        mock_response = Mock()
        mock_response.headers = {
            "X-RateLimit-Remaining": "50",
            "X-RateLimit-Reset": "0",
        }

        # Should not raise or sleep
        engine._handle_rate_limits(mock_response)

    def test_handle_rate_limits_low_remaining(self):
        """Test rate limit handling with low remaining requests."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        mock_response = Mock()
        mock_response.headers = {
            "X-RateLimit-Remaining": "3",
            "X-RateLimit-Reset": "0",
        }

        # Should log warning but not sleep
        engine._handle_rate_limits(mock_response)


class TestGitHubOptimizeQuery:
    """Tests for query optimization."""

    def test_optimize_query_without_llm(self):
        """Test query optimization returns original without LLM."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(llm=None)

        # Patch the config getter to return None
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm",
            return_value=None,
        ):
            result = engine._optimize_github_query("python web framework")
            assert result == "python web framework"

    def test_optimize_query_with_llm(self):
        """Test query optimization with LLM."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(
            content="python web framework stars:>100"
        )

        engine = GitHubSearchEngine(llm=mock_llm)
        result = engine._optimize_github_query("python web framework")

        assert "python" in result.lower()
        mock_llm.invoke.assert_called_once()

    def test_optimize_query_handles_error(self):
        """Test query optimization handles LLM errors gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_llm = Mock()
        mock_llm.invoke.side_effect = Exception("LLM error")

        engine = GitHubSearchEngine(llm=mock_llm)
        result = engine._optimize_github_query("test query")

        # Should fall back to original query
        assert result == "test query"

    def test_optimize_query_handles_empty_response(self):
        """Test query optimization handles empty LLM response."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(content="")

        engine = GitHubSearchEngine(llm=mock_llm)
        result = engine._optimize_github_query("test query")

        # Should fall back to original query
        assert result == "test query"


class TestGitHubSearchGithub:
    """Tests for _search_github method."""

    def test_search_github_success(self):
        """Test successful GitHub search."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(llm=None)

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "total_count": 2,
            "items": [
                {"id": 1, "full_name": "owner/repo1"},
                {"id": 2, "full_name": "owner/repo2"},
            ],
        }
        mock_response.headers = {
            "X-RateLimit-Remaining": "50",
            "X-RateLimit-Reset": "0",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
                return_value=mock_response,
            ):
                result = engine._search_github("python")

                assert len(result) == 2
                assert result[0]["full_name"] == "owner/repo1"

    def test_search_github_handles_error(self):
        """Test search handles API errors."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(llm=None)

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
                side_effect=Exception("API error"),
            ):
                result = engine._search_github("test query")

                assert result == []

    def test_search_github_handles_non_200_response(self):
        """Test search handles non-200 responses."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(llm=None)

        mock_response = Mock()
        mock_response.status_code = 403
        mock_response.text = "Rate limit exceeded"
        mock_response.headers = {
            "X-RateLimit-Remaining": "0",
            "X-RateLimit-Reset": "0",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.llm_config.get_llm",
            return_value=None,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
                return_value=mock_response,
            ):
                result = engine._search_github("test")

                assert result == []


class TestGitHubGetReadmeContent:
    """Tests for _get_readme_content method."""

    def test_get_readme_success(self):
        """Test successful README retrieval."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        readme_content = "# Test README\nThis is a test."
        encoded_content = base64.b64encode(readme_content.encode()).decode()

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "content": encoded_content,
            "encoding": "base64",
        }
        mock_response.headers = {
            "X-RateLimit-Remaining": "50",
            "X-RateLimit-Reset": "0",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            result = engine._get_readme_content("owner/repo")

            assert "Test README" in result

    def test_get_readme_not_found(self):
        """Test README not found."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.headers = {
            "X-RateLimit-Remaining": "50",
            "X-RateLimit-Reset": "0",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            result = engine._get_readme_content("owner/repo")

            assert result == ""

    def test_get_readme_handles_error(self):
        """Test README retrieval handles errors."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            side_effect=Exception("Network error"),
        ):
            result = engine._get_readme_content("owner/repo")

            assert result == ""


class TestGitHubGetRecentIssues:
    """Tests for _get_recent_issues method."""

    def test_get_issues_success(self):
        """Test successful issues retrieval."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        mock_issues = [
            {"number": 1, "title": "Issue 1"},
            {"number": 2, "title": "Issue 2"},
        ]

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = mock_issues
        mock_response.headers = {
            "X-RateLimit-Remaining": "50",
            "X-RateLimit-Reset": "0",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            result = engine._get_recent_issues("owner/repo", limit=5)

            assert len(result) == 2
            assert result[0]["title"] == "Issue 1"

    def test_get_issues_not_found(self):
        """Test issues not found."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.headers = {
            "X-RateLimit-Remaining": "50",
            "X-RateLimit-Reset": "0",
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            result = engine._get_recent_issues("owner/repo")

            assert result == []


class TestGitHubFormatPreviews:
    """Tests for preview formatting methods."""

    def test_format_repository_preview(self):
        """Test repository preview formatting."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        repo = {
            "id": 123,
            "full_name": "owner/repo",
            "html_url": "https://github.com/owner/repo",
            "description": "Test description",
            "stargazers_count": 100,
            "forks_count": 10,
            "language": "Python",
            "owner": {"login": "owner"},
        }

        preview = engine._format_repository_preview(repo)

        assert preview["title"] == "owner/repo"
        assert preview["stars"] == 100
        assert preview["language"] == "Python"
        assert preview["search_type"] == "repository"

    def test_format_code_preview(self):
        """Test code preview formatting."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        code = {
            "sha": "abc123",
            "name": "test.py",
            "path": "src/test.py",
            "html_url": "https://github.com/owner/repo/blob/main/test.py",
            "repository": {
                "full_name": "owner/repo",
                "html_url": "https://github.com/owner/repo",
            },
        }

        preview = engine._format_code_preview(code)

        assert "test.py" in preview["title"]
        assert preview["path"] == "src/test.py"
        assert preview["search_type"] == "code"

    def test_format_issue_preview(self):
        """Test issue preview formatting."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        issue = {
            "number": 42,
            "title": "Bug report",
            "html_url": "https://github.com/owner/repo/issues/42",
            "body": "This is a bug description",
            "state": "open",
            "user": {"login": "reporter"},
            "comments": 5,
        }

        preview = engine._format_issue_preview(issue)

        assert preview["title"] == "Bug report"
        assert preview["state"] == "open"
        assert preview["search_type"] == "issue"

    def test_format_user_preview(self):
        """Test user preview formatting."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        user = {
            "id": 1,
            "login": "testuser",
            "html_url": "https://github.com/testuser",
            "bio": "Test bio",
            "followers": 100,
            "public_repos": 50,
        }

        preview = engine._format_user_preview(user)

        assert preview["title"] == "testuser"
        assert preview["followers"] == 100
        assert preview["search_type"] == "user"


class TestGitHubGetPreviews:
    """Tests for _get_previews method."""

    def test_get_previews_repository(self):
        """Test getting repository previews."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="repositories", llm=None)

        mock_results = [
            {
                "id": 123,
                "full_name": "owner/repo",
                "html_url": "https://github.com/owner/repo",
                "description": "Test repo",
                "stargazers_count": 100,
                "forks_count": 10,
                "owner": {"login": "owner"},
            }
        ]

        with patch.object(engine, "_search_github", return_value=mock_results):
            previews = engine._get_previews("python")

            assert len(previews) == 1
            assert previews[0]["search_type"] == "repository"

    def test_get_previews_code(self):
        """Test getting code previews."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="code", llm=None)

        mock_results = [
            {
                "sha": "abc123",
                "name": "test.py",
                "path": "src/test.py",
                "html_url": "https://github.com/owner/repo/blob/main/test.py",
                "repository": {"full_name": "owner/repo"},
            }
        ]

        with patch.object(engine, "_search_github", return_value=mock_results):
            previews = engine._get_previews("function")

            assert len(previews) == 1
            assert previews[0]["search_type"] == "code"

    def test_get_previews_no_results(self):
        """Test getting previews with no results."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(llm=None)

        with patch.object(engine, "_search_github", return_value=[]):
            previews = engine._get_previews("nonexistent")

            assert previews == []

    def test_get_previews_contribution_query(self):
        """Test getting previews for contribution-focused queries."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="issues", llm=None)

        mock_results = [
            {
                "id": 123,
                "full_name": "owner/repo",
                "html_url": "https://github.com/owner/repo",
                "description": "Good for beginners",
                "stargazers_count": 500,
                "forks_count": 50,
                "owner": {"login": "owner"},
            }
        ]

        with patch.object(engine, "_search_github", return_value=mock_results):
            previews = engine._get_previews(
                "beginner python projects to contribute"
            )

            # Should have adjusted search type
            assert len(previews) == 1
