"""
Tests for the GitHubSearchEngine class.

Tests cover:
- Initialization and configuration
- API key and authentication
- Search type handling
- Preview generation for each search type
- Full content retrieval (README, issues, code)
- Rate limit handling
- Query optimization
- Helper methods
"""

import base64
from unittest.mock import Mock, patch

import pytest


class TestGitHubSearchEngineInit:
    """Tests for GitHubSearchEngine initialization."""

    def test_init_with_defaults(self):
        """Initialize with default values."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        assert engine.max_results == 15
        assert engine.search_type == "repositories"
        assert engine.include_readme is True
        assert engine.include_issues is False
        assert engine.api_base == "https://api.github.com"
        assert engine.api_key is None

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

        engine = GitHubSearchEngine(api_key="test-token")

        assert engine.api_key == "test-token"
        assert "Authorization" in engine.headers
        assert engine.headers["Authorization"] == "token test-token"

    def test_init_with_custom_max_results(self):
        """Initialize 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

    def test_init_with_search_type_code(self):
        """Initialize with code 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"
        assert engine.search_endpoint == "https://api.github.com/search/code"

    def test_init_with_search_type_issues(self):
        """Initialize with issues search type."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="issues")

        assert engine.search_type == "issues"
        assert engine.search_endpoint == "https://api.github.com/search/issues"

    def test_init_with_search_type_users(self):
        """Initialize with users search type."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(search_type="users")

        assert engine.search_type == "users"
        assert engine.search_endpoint == "https://api.github.com/search/users"

    def test_init_with_include_readme_disabled(self):
        """Initialize with include_readme disabled."""
        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_include_issues_enabled(self):
        """Initialize with include_issues enabled."""
        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):
        """Initialize 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 TestHandleRateLimits:
    """Tests for _handle_rate_limits method."""

    def test_handle_rate_limits_logs_warning_when_low(self):
        """Handle rate limits logs warning when remaining is low."""
        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 not raise, just log warning
        engine._handle_rate_limits(mock_response)


class TestFormatRepositoryPreview:
    """Tests for _format_repository_preview method."""

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

        engine = GitHubSearchEngine()

        repo = {
            "id": 12345,
            "full_name": "owner/repo",
            "html_url": "https://github.com/owner/repo",
            "description": "A test repository",
            "stargazers_count": 1000,
            "forks_count": 100,
            "language": "Python",
            "updated_at": "2024-01-15T10:00:00Z",
            "created_at": "2023-01-01T00:00:00Z",
            "topics": ["python", "testing"],
            "owner": {"login": "owner"},
            "fork": False,
        }

        preview = engine._format_repository_preview(repo)

        assert preview["id"] == "12345"
        assert preview["title"] == "owner/repo"
        assert preview["link"] == "https://github.com/owner/repo"
        assert preview["snippet"] == "A test repository"
        assert preview["stars"] == 1000
        assert preview["forks"] == 100
        assert preview["language"] == "Python"
        assert preview["search_type"] == "repository"
        assert preview["is_fork"] is False


class TestFormatCodePreview:
    """Tests for _format_code_preview method."""

    def test_format_code_preview(self):
        """Format code preview correctly."""
        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/src/test.py",
            "url": "https://api.github.com/repos/owner/repo/contents/src/test.py",
            "repository": {
                "full_name": "owner/repo",
                "html_url": "https://github.com/owner/repo",
            },
        }

        preview = engine._format_code_preview(code)

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


class TestFormatIssuePreview:
    """Tests for _format_issue_preview method."""

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

        engine = GitHubSearchEngine()

        issue = {
            "number": 42,
            "title": "Bug: Something broken",
            "html_url": "https://github.com/owner/repo/issues/42",
            "body": "This is the issue description",
            "state": "open",
            "created_at": "2024-01-15T10:00:00Z",
            "updated_at": "2024-01-16T10:00:00Z",
            "user": {"login": "reporter"},
            "comments": 5,
            "repository": {"full_name": "owner/repo"},
        }

        preview = engine._format_issue_preview(issue)

        assert preview["id"] == "issue_42"
        assert preview["title"] == "Bug: Something broken"
        assert preview["state"] == "open"
        assert preview["comments"] == 5
        assert preview["search_type"] == "issue"

    def test_format_issue_preview_truncates_long_body(self):
        """Format issue preview truncates long body."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        issue = {
            "number": 42,
            "title": "Test",
            "html_url": "https://github.com/owner/repo/issues/42",
            "body": "x" * 300,
            "user": {},
        }

        preview = engine._format_issue_preview(issue)

        assert len(preview["snippet"]) <= 210  # 200 + "..."


class TestFormatUserPreview:
    """Tests for _format_user_preview method."""

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

        engine = GitHubSearchEngine()

        user = {
            "id": 12345,
            "login": "testuser",
            "html_url": "https://github.com/testuser",
            "bio": "A test user",
            "name": "Test User",
            "followers": 100,
            "public_repos": 50,
            "location": "San Francisco",
        }

        preview = engine._format_user_preview(user)

        assert preview["id"] == "user_12345"
        assert preview["title"] == "testuser"
        assert preview["snippet"] == "A test user"
        assert preview["followers"] == 100
        assert preview["public_repos"] == 50
        assert preview["search_type"] == "user"


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

    def test_search_github_returns_results(self):
        """Search GitHub returns results."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.headers = {"X-RateLimit-Remaining": "60"}
        mock_response.json.return_value = {
            "total_count": 1,
            "items": [
                {
                    "id": 1,
                    "full_name": "owner/repo",
                    "html_url": "https://github.com/owner/repo",
                    "description": "Test repo",
                }
            ],
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            engine = GitHubSearchEngine()
            # Mock LLM to skip query optimization
            engine.llm = None
            results = engine._search_github("test query")

            assert len(results) == 1
            assert results[0]["full_name"] == "owner/repo"

    def test_search_github_api_error(self):
        """Search GitHub handles API errors."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 403
        mock_response.headers = {"X-RateLimit-Remaining": "0"}
        mock_response.text = "Forbidden"

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            engine = GitHubSearchEngine()
            engine.llm = None
            results = engine._search_github("test query")

            assert results == []


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

    def test_get_readme_content_returns_decoded_content(self):
        """Get README content returns decoded content."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        readme_text = "# Test Repository\n\nThis is a test."
        encoded_content = base64.b64encode(readme_text.encode()).decode()

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

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

            assert content == readme_text

    def test_get_readme_content_not_found(self):
        """Get README content handles not found."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.headers = {"X-RateLimit-Remaining": "60"}

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

            assert content == ""


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

    def test_get_recent_issues_returns_issues(self):
        """Get recent issues returns issues."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.headers = {"X-RateLimit-Remaining": "60"}
        mock_response.json.return_value = [
            {"number": 1, "title": "Issue 1"},
            {"number": 2, "title": "Issue 2"},
        ]

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

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

    def test_get_recent_issues_not_found(self):
        """Get recent issues handles not found."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.headers = {"X-RateLimit-Remaining": "60"}

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

            assert issues == []


class TestGetFileContent:
    """Tests for _get_file_content method."""

    def test_get_file_content_returns_decoded_content(self):
        """Get file content returns decoded content."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        file_text = "def test():\n    pass"
        encoded_content = base64.b64encode(file_text.encode()).decode()

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            engine = GitHubSearchEngine()
            content = engine._get_file_content(
                "https://api.github.com/repos/owner/repo/contents/test.py"
            )

            assert content == file_text


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

    def test_get_previews_repositories(self):
        """Get previews for repository search."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

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

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

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

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

    def test_get_previews_empty_results(self):
        """Get previews handles empty results."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        engine.llm = None

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

            assert previews == []

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

        # Test with repositories search type so the contribution query works correctly
        engine = GitHubSearchEngine(search_type="repositories")
        engine.llm = None

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

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

            assert len(previews) == 1
            # Contribution query triggers specialized search for repositories
            assert previews[0]["search_type"] == "repository"


class TestGetFullContent:
    """Tests for _get_full_content method."""

    def test_get_full_content_repository_with_readme(self):
        """Get full content fetches README for repositories."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(include_readme=True, include_issues=False)

        items = [
            {
                "id": "1",
                "title": "owner/repo",
                "link": "https://github.com/owner/repo",
                "search_type": "repository",
                "repo_full_name": "owner/repo",
            }
        ]

        with patch.object(
            engine, "_get_readme_content", return_value="# Test README"
        ):
            results = engine._get_full_content(items)

            assert len(results) == 1
            assert results[0]["full_content"] == "# Test README"
            assert results[0]["content_type"] == "readme"

    def test_get_full_content_repository_with_issues(self):
        """Get full content fetches issues for repositories."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine(include_readme=True, include_issues=True)

        items = [
            {
                "id": "1",
                "title": "owner/repo",
                "link": "https://github.com/owner/repo",
                "search_type": "repository",
                "repo_full_name": "owner/repo",
            }
        ]

        mock_issues = [{"number": 1, "title": "Test issue"}]

        with patch.object(
            engine, "_get_readme_content", return_value="# README"
        ):
            with patch.object(
                engine, "_get_recent_issues", return_value=mock_issues
            ):
                results = engine._get_full_content(items)

                assert results[0]["recent_issues"] == mock_issues

    def test_get_full_content_code(self):
        """Get full content fetches file content for code."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        items = [
            {
                "id": "code_abc",
                "title": "test.py",
                "search_type": "code",
                "file_url": "https://api.github.com/repos/owner/repo/contents/test.py",
            }
        ]

        with patch.object(
            engine, "_get_file_content", return_value="def test(): pass"
        ):
            results = engine._get_full_content(items)

            assert results[0]["full_content"] == "def test(): pass"
            assert results[0]["content_type"] == "file"

    def test_get_full_content_user(self):
        """Get full content creates profile summary for users."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        items = [
            {
                "id": "user_123",
                "title": "testuser",
                "snippet": "A developer",
                "search_type": "user",
                "name": "Test User",
                "location": "NYC",
                "followers": 100,
                "public_repos": 50,
            }
        ]

        results = engine._get_full_content(items)

        assert "testuser" in results[0]["full_content"]
        assert "Followers: 100" in results[0]["full_content"]
        assert results[0]["content_type"] == "user_profile"


class TestSetSearchType:
    """Tests for set_search_type method."""

    def test_set_search_type_valid(self):
        """Set search type to valid value."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        engine.set_search_type("code")

        assert engine.search_type == "code"
        assert "code" in engine.search_endpoint

    def test_set_search_type_invalid(self):
        """Set search type with invalid value raises ValueError."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()

        with pytest.raises(ValueError, match="Invalid GitHub search_type"):
            engine.set_search_type("invalid")


class TestOptimizeGitHubQuery:
    """Tests for _optimize_github_query method."""

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

        engine = GitHubSearchEngine()
        engine.llm = 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("test query")

            assert result == "test query"

    def test_optimize_query_with_llm(self):
        """Optimize query uses LLM when available."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

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

        engine = GitHubSearchEngine(llm=mock_llm)

        result = engine._optimize_github_query("python repositories")

        assert result == "python stars:>100"


class TestSearchRepository:
    """Tests for search_repository method."""

    def test_search_repository_returns_details(self):
        """Search repository returns repository details."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.headers = {"X-RateLimit-Remaining": "60"}
        mock_response.json.return_value = {
            "id": 1,
            "full_name": "owner/repo",
            "html_url": "https://github.com/owner/repo",
            "description": "Test repo",
            "stargazers_count": 100,
            "owner": {"login": "owner"},
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            engine = GitHubSearchEngine(include_readme=False)
            result = engine.search_repository("owner", "repo")

            assert result["title"] == "owner/repo"
            assert result["stars"] == 100

    def test_search_repository_not_found(self):
        """Search repository handles not found."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.headers = {"X-RateLimit-Remaining": "60"}
        mock_response.text = "Not Found"

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            engine = GitHubSearchEngine()
            result = engine.search_repository("owner", "nonexistent")

            assert result == {}


class TestSearchCode:
    """Tests for search_code method."""

    def test_search_code_with_language(self):
        """Search code with language filter."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        engine = GitHubSearchEngine()
        engine.llm = None

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

        with patch.object(engine, "_search_github", return_value=mock_results):
            results = engine.search_code("def test", language="python")

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


class TestSearchIssues:
    """Tests for search_issues method."""

    def test_search_issues_open(self):
        """Search issues with open state."""
        from local_deep_research.web_search_engines.engines.search_engine_github import (
            GitHubSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.headers = {"X-RateLimit-Remaining": "60"}
        mock_response.json.return_value = {
            "total_count": 1,
            "items": [
                {
                    "number": 1,
                    "title": "Bug report",
                    "html_url": "https://github.com/owner/repo/issues/1",
                    "body": "Description",
                    "state": "open",
                    "user": {"login": "reporter"},
                }
            ],
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_github.safe_get",
            return_value=mock_response,
        ):
            engine = GitHubSearchEngine()
            results = engine.search_issues("bug", state="open")

            assert len(results) == 1
            assert results[0]["state"] == "open"
