"""Tests for OCLAWMA GitHub Skill."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import httpx
import pytest
from oclawma.skills import SkillMetadata

from oclawma_skill_github import GitHubSkill


@pytest.fixture
def metadata():
    """Create test skill metadata."""
    return SkillMetadata(
        name="github",
        version="1.0.0",
        description="Test GitHub skill",
    )


@pytest.fixture
def skill(metadata):
    """Create test skill instance."""
    return GitHubSkill(metadata)


@pytest.fixture
def mock_client():
    """Create mock HTTP client."""
    with patch("httpx.AsyncClient") as mock:
        client = MagicMock()
        mock.return_value = client
        yield client


class TestGitHubSkill:
    """Test suite for GitHubSkill."""

    def test_init(self, skill, metadata):
        """Test skill initialization."""
        assert skill.metadata == metadata
        assert skill.name == "github"
        assert not skill.is_loaded

    def test_load(self, skill):
        """Test skill loading."""
        skill.load()
        assert skill.is_loaded
        assert "list_repos" in skill.tools
        assert "create_pr" in skill.tools
        assert "list_workflow_runs" in skill.tools
        assert "create_issue" in skill.tools

    def test_list_tools_before_load(self, skill):
        """Test listing tools before loading returns from manifest."""
        tools = skill.list_tools()
        assert isinstance(tools, list)

    def test_list_tools_after_load(self, skill):
        """Test listing tools after loading."""
        skill.load()
        tools = skill.list_tools()
        assert "list_repos" in tools
        assert "get_repo" in tools
        assert "create_pr" in tools
        assert "merge_pr" in tools
        assert "list_issues" in tools
        assert "list_workflow_runs" in tools


class TestRepositoryOperations:
    """Test repository operation tools."""

    @pytest.mark.asyncio
    async def test_list_repos_success(self, skill, mock_client):
        """Test successful list_repos call."""
        skill.load()

        # Mock response
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = [
            {
                "id": 1,
                "name": "test-repo",
                "full_name": "owner/test-repo",
                "description": "Test repository",
                "html_url": "https://github.com/owner/test-repo",
                "private": False,
                "stargazers_count": 100,
                "forks_count": 50,
                "language": "Python",
                "default_branch": "main",
                "updated_at": "2024-01-01T00:00:00Z",
            }
        ]
        mock_response.content = b"[]"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["list_repos"](owner="owner")

        assert result["success"] is True
        assert len(result["data"]) == 1
        assert result["data"][0]["name"] == "test-repo"

    @pytest.mark.asyncio
    async def test_get_repo_success(self, skill, mock_client):
        """Test successful get_repo call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "id": 1,
            "name": "test-repo",
            "full_name": "owner/test-repo",
            "description": "Test repository",
            "html_url": "https://github.com/owner/test-repo",
            "private": False,
            "stargazers_count": 100,
            "forks_count": 50,
            "language": "Python",
            "default_branch": "main",
            "updated_at": "2024-01-01T00:00:00Z",
            "topics": ["python", "testing"],
            "archived": False,
            "has_issues": True,
            "has_projects": True,
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["get_repo"](owner="owner", repo="test-repo")

        assert result["success"] is True
        assert result["data"]["name"] == "test-repo"
        assert "topics" in result["data"]

    @pytest.mark.asyncio
    async def test_create_repo_success(self, skill, mock_client):
        """Test successful create_repo call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 201
        mock_response.json.return_value = {
            "id": 1,
            "name": "new-repo",
            "full_name": "owner/new-repo",
            "html_url": "https://github.com/owner/new-repo",
            "private": True,
            "stargazers_count": 0,
            "forks_count": 0,
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["create_repo"](
            name="new-repo",
            description="A new repository",
            private=True,
        )

        assert result["success"] is True
        assert result["data"]["name"] == "new-repo"

    @pytest.mark.asyncio
    async def test_search_repos_success(self, skill, mock_client):
        """Test successful search_repos call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "total_count": 1,
            "items": [
                {
                    "id": 1,
                    "name": "python-project",
                    "full_name": "owner/python-project",
                    "description": "A Python project",
                    "html_url": "https://github.com/owner/python-project",
                    "private": False,
                    "stargazers_count": 1000,
                    "forks_count": 100,
                    "language": "Python",
                }
            ],
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["search_repos"](query="language:python stars:>100")

        assert result["success"] is True
        assert result["data"]["total_count"] == 1
        assert len(result["data"]["repositories"]) == 1


class TestPullRequestOperations:
    """Test pull request operation tools."""

    @pytest.mark.asyncio
    async def test_list_prs_success(self, skill, mock_client):
        """Test successful list_prs call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = [
            {
                "number": 1,
                "title": "Add feature",
                "state": "open",
                "html_url": "https://github.com/owner/repo/pull/1",
                "user": {"login": "contributor"},
                "head": {"ref": "feature-branch"},
                "base": {"ref": "main"},
                "created_at": "2024-01-01T00:00:00Z",
                "updated_at": "2024-01-02T00:00:00Z",
                "draft": False,
            }
        ]
        mock_response.content = b"[]"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["list_prs"](owner="owner", repo="repo")

        assert result["success"] is True
        assert len(result["data"]) == 1
        assert result["data"][0]["number"] == 1

    @pytest.mark.asyncio
    async def test_create_pr_success(self, skill, mock_client):
        """Test successful create_pr call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 201
        mock_response.json.return_value = {
            "number": 42,
            "title": "Fix bug",
            "state": "open",
            "html_url": "https://github.com/owner/repo/pull/42",
            "user": {"login": "developer"},
            "head": {"ref": "fix-branch"},
            "base": {"ref": "main"},
            "draft": False,
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["create_pr"](
            owner="owner",
            repo="repo",
            title="Fix bug",
            head="fix-branch",
            base="main",
        )

        assert result["success"] is True
        assert result["data"]["number"] == 42

    @pytest.mark.asyncio
    async def test_merge_pr_success(self, skill, mock_client):
        """Test successful merge_pr call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "sha": "abc123",
            "merged": True,
            "message": "Pull Request successfully merged",
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["merge_pr"](
            owner="owner",
            repo="repo",
            number=42,
            merge_method="squash",
        )

        assert result["success"] is True
        assert result["data"]["merged"] is True


class TestIssueOperations:
    """Test issue operation tools."""

    @pytest.mark.asyncio
    async def test_list_issues_success(self, skill, mock_client):
        """Test successful list_issues call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = [
            {
                "number": 1,
                "title": "Bug report",
                "state": "open",
                "html_url": "https://github.com/owner/repo/issues/1",
                "user": {"login": "reporter"},
                "labels": [{"name": "bug"}],
                "assignees": [],
                "created_at": "2024-01-01T00:00:00Z",
                "updated_at": "2024-01-02T00:00:00Z",
                "comments": 5,
            }
        ]
        mock_response.content = b"[]"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["list_issues"](owner="owner", repo="repo")

        assert result["success"] is True
        assert len(result["data"]) == 1
        assert result["data"][0]["labels"] == ["bug"]

    @pytest.mark.asyncio
    async def test_create_issue_success(self, skill, mock_client):
        """Test successful create_issue call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 201
        mock_response.json.return_value = {
            "number": 42,
            "title": "New issue",
            "state": "open",
            "html_url": "https://github.com/owner/repo/issues/42",
            "user": {"login": "creator"},
            "labels": [{"name": "enhancement"}],
            "assignees": [{"login": "assignee"}],
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["create_issue"](
            owner="owner",
            repo="repo",
            title="New issue",
            body="Issue description",
            labels=["enhancement"],
            assignees=["assignee"],
        )

        assert result["success"] is True
        assert result["data"]["number"] == 42

    @pytest.mark.asyncio
    async def test_search_issues_success(self, skill, mock_client):
        """Test successful search_issues call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "total_count": 1,
            "items": [
                {
                    "number": 1,
                    "title": "Bug found",
                    "state": "open",
                    "html_url": "https://github.com/owner/repo/issues/1",
                    "user": {"login": "user"},
                    "labels": [{"name": "bug"}],
                }
            ],
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["search_issues"](query="is:open is:issue label:bug")

        assert result["success"] is True
        assert result["data"]["total_count"] == 1


class TestGitHubActions:
    """Test GitHub Actions tools."""

    @pytest.mark.asyncio
    async def test_list_workflows_success(self, skill, mock_client):
        """Test successful list_workflows call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "total_count": 2,
            "workflows": [
                {
                    "id": 1,
                    "name": "CI",
                    "path": ".github/workflows/ci.yml",
                    "state": "active",
                    "created_at": "2024-01-01T00:00:00Z",
                    "updated_at": "2024-01-02T00:00:00Z",
                    "html_url": "https://github.com/owner/repo/blob/main/.github/workflows/ci.yml",
                }
            ],
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["list_workflows"](owner="owner", repo="repo")

        assert result["success"] is True
        assert len(result["data"]) == 1
        assert result["data"][0]["name"] == "CI"

    @pytest.mark.asyncio
    async def test_list_workflow_runs_success(self, skill, mock_client):
        """Test successful list_workflow_runs call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "total_count": 10,
            "workflow_runs": [
                {
                    "id": 12345,
                    "name": "CI",
                    "status": "completed",
                    "conclusion": "success",
                    "workflow_id": 1,
                    "head_branch": "main",
                    "head_sha": "abc123def456",
                    "event": "push",
                    "html_url": "https://github.com/owner/repo/actions/runs/12345",
                    "created_at": "2024-01-01T00:00:00Z",
                    "updated_at": "2024-01-01T00:10:00Z",
                    "run_number": 42,
                }
            ],
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["list_workflow_runs"](
            owner="owner", repo="repo", branch="main"
        )

        assert result["success"] is True
        assert len(result["data"]) == 1
        assert result["data"][0]["conclusion"] == "success"

    @pytest.mark.asyncio
    async def test_trigger_workflow_success(self, skill, mock_client):
        """Test successful trigger_workflow call."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 204
        mock_response.content = b""

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["trigger_workflow"](
            owner="owner",
            repo="repo",
            workflow_id="deploy.yml",
            ref="main",
            inputs={"environment": "staging"},
        )

        assert result["success"] is True
        assert result["data"]["triggered"] is True


class TestErrorHandling:
    """Test error handling."""

    @pytest.mark.asyncio
    async def test_api_error(self, skill, mock_client):
        """Test API error handling."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 404
        mock_response.json.return_value = {"message": "Not Found"}
        mock_response.content = b'{"message": "Not Found"}'

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["get_repo"](owner="owner", repo="nonexistent")

        assert result["success"] is False
        assert "Not Found" in result["error"]
        assert result["status_code"] == 404

    @pytest.mark.asyncio
    async def test_rate_limit_error(self, skill, mock_client):
        """Test rate limit error handling."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 403
        mock_response.json.return_value = {"message": "API rate limit exceeded"}
        mock_response.content = b'{"message": "API rate limit exceeded"}'
        mock_response.headers = {"X-RateLimit-Reset": "1704067200"}
        mock_response.text = "rate limit"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["list_repos"]()

        assert result["success"] is False
        assert "rate" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_timeout_error(self, skill, mock_client):
        """Test timeout error handling."""
        skill.load()

        mock_client.request = AsyncMock(side_effect=httpx.TimeoutException("Request timed out"))

        result = await skill.tools["list_repos"]()

        assert result["success"] is False
        assert "timeout" in result["error"].lower()


class TestUtilityTools:
    """Test utility tools."""

    @pytest.mark.asyncio
    async def test_rate_limit(self, skill, mock_client):
        """Test rate_limit tool."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "resources": {
                "core": {
                    "limit": 5000,
                    "remaining": 4999,
                    "used": 1,
                    "reset": 1704067200,
                }
            }
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["rate_limit"]()

        assert result["success"] is True
        assert result["data"]["limit"] == 5000
        assert result["data"]["remaining"] == 4999

    @pytest.mark.asyncio
    async def test_whoami(self, skill, mock_client):
        """Test whoami tool."""
        skill.load()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "login": "testuser",
            "id": 12345,
            "name": "Test User",
            "email": "test@example.com",
            "public_repos": 10,
            "followers": 50,
            "following": 25,
            "created_at": "2020-01-01T00:00:00Z",
            "html_url": "https://github.com/testuser",
        }
        mock_response.content = b"{}"

        mock_client.request = AsyncMock(return_value=mock_response)

        result = await skill.tools["whoami"]()

        assert result["success"] is True
        assert result["data"]["login"] == "testuser"
        assert result["data"]["public_repos"] == 10
