"""Tests for PullRequestService — PR automation.

All HTTP calls are mocked via ``httpx.MockTransport``.
"""

from __future__ import annotations

import json

import httpx

from kgn.github.client import GitHubClient, GitHubConfig
from kgn.github.pr_service import PRContext, PRResult, PullRequestService

# ── Helpers ────────────────────────────────────────────────────────────


def _make_config() -> GitHubConfig:
    return GitHubConfig(token="ghp_test", owner="test-owner", repo="test-repo")


def _mock_client(handler) -> GitHubClient:
    config = _make_config()
    client = GitHubClient(config=config)
    client._client = httpx.Client(
        base_url="https://api.github.com",
        headers={
            "Authorization": f"Bearer {config.token}",
            "Accept": "application/vnd.github+json",
        },
        transport=httpx.MockTransport(handler),
        timeout=30.0,
    )
    return client


# ══════════════════════════════════════════════════════════════════════
#  PR Body generation
# ══════════════════════════════════════════════════════════════════════


class TestPRBody:
    def test_body_includes_task_info(self) -> None:
        """PR body contains task title, ID, agent, branch."""
        ctx = PRContext(
            task_title="JWT implementation",
            task_id="550e8400",
            branch_name="agent/claude-task-550e8400",
            agent_key="claude",
            changed_files=["nodes/SPEC/jwt.kgn", "edges/x--IMPLEMENTS--y.kge"],
            node_summary="JWT token-based authentication implementation",
        )
        body = PullRequestService._build_pr_body(ctx)

        assert "JWT implementation" in body
        assert "550e8400" in body
        assert "claude" in body
        assert "agent/claude-task-550e8400" in body
        assert "nodes/SPEC/jwt.kgn" in body
        assert "JWT token-based authentication implementation" in body
        assert "Auto-generated by KGN" in body

    def test_body_minimal(self) -> None:
        """PR body works with minimal context (no files, no summary)."""
        ctx = PRContext(
            task_title="Simple task",
            task_id="aabbccdd",
            branch_name="agent/bot-task-aabbccdd",
        )
        body = PullRequestService._build_pr_body(ctx)

        assert "Simple task" in body
        assert "Changed Files" not in body
        assert "Summary" not in body


# ══════════════════════════════════════════════════════════════════════
#  PR creation
# ══════════════════════════════════════════════════════════════════════


class TestPRCreation:
    def test_create_task_pr_success(self) -> None:
        """create_task_pr returns PRResult on 201."""

        def handler(request: httpx.Request) -> httpx.Response:
            if request.method == "POST" and "/pulls" in str(request.url):
                body = json.loads(request.content)
                assert "kgn: complete TASK" in body["title"]
                return httpx.Response(
                    201,
                    json={
                        "number": 99,
                        "html_url": "https://github.com/test/pr/99",
                    },
                )
            return httpx.Response(404)

        client = _mock_client(handler)
        pr_svc = PullRequestService(client)

        ctx = PRContext(
            task_title="auth implementation",
            task_id="abc123",
            branch_name="agent/claude-task-abc123",
            agent_key="claude",
        )
        result = pr_svc.create_task_pr(ctx)

        assert result.success is True
        assert result.pr_number == 99
        assert "github.com" in (result.html_url or "")

    def test_create_task_pr_failure(self) -> None:
        """create_task_pr returns failure on API error."""

        def handler(request: httpx.Request) -> httpx.Response:
            return httpx.Response(422, json={"message": "Validation Failed"})

        client = _mock_client(handler)
        pr_svc = PullRequestService(client)

        ctx = PRContext(
            task_title="Failing task",
            task_id="fail123",
            branch_name="agent/bot-task-fail123",
        )
        result = pr_svc.create_task_pr(ctx)

        assert result.success is False
        assert "failed" in result.message.lower()


# ══════════════════════════════════════════════════════════════════════
#  PR listing
# ══════════════════════════════════════════════════════════════════════


class TestPRList:
    def test_list_prs(self) -> None:
        """list_prs returns PR list from API."""

        def handler(request: httpx.Request) -> httpx.Response:
            if "/pulls" in str(request.url):
                return httpx.Response(
                    200,
                    json=[
                        {"number": 1, "title": "PR 1", "state": "open"},
                        {"number": 2, "title": "PR 2", "state": "open"},
                    ],
                )
            return httpx.Response(404)

        client = _mock_client(handler)
        pr_svc = PullRequestService(client)
        prs = pr_svc.list_prs(state="open")

        assert len(prs) == 2
        assert prs[0]["number"] == 1


# ══════════════════════════════════════════════════════════════════════
#  PRResult dataclass
# ══════════════════════════════════════════════════════════════════════


class TestPRResult:
    def test_defaults(self) -> None:
        result = PRResult(success=False)
        assert result.pr_number is None
        assert result.html_url is None
        assert result.message == ""

    def test_with_values(self) -> None:
        result = PRResult(
            success=True,
            pr_number=42,
            html_url="https://example.com/pr/42",
            message="Created",
        )
        assert result.pr_number == 42
