"""Tests for GitHub API helpers."""

from __future__ import annotations

import httpx
import respx

from github_app.github import (
    add_labels,
    build_file_summary,
    create_check_run,
    fetch_commit_diff,
    fetch_pr_details,
    fetch_pr_diff,
    fetch_pr_files,
    fetch_repo_labels,
    post_comment,
    post_review,
    truncate_diff,
)

API = "https://api.github.com"


@respx.mock
async def test_fetch_pr_diff():
    respx.get(f"{API}/repos/o/r/pulls/1").respond(text="diff content")
    async with httpx.AsyncClient() as client:
        result = await fetch_pr_diff(client, "o", "r", 1, "tok")
    assert result == "diff content"


@respx.mock
async def test_fetch_pr_files():
    def side_effect(request):
        page = int(request.url.params.get("page", "1"))
        if page == 1:
            return httpx.Response(200, json=[{"filename": "a.py"}])
        return httpx.Response(200, json=[])

    respx.get(f"{API}/repos/o/r/pulls/1/files").mock(
        side_effect=side_effect,
    )
    async with httpx.AsyncClient() as client:
        result = await fetch_pr_files(client, "o", "r", 1, "tok")
    assert result == [{"filename": "a.py"}]


@respx.mock
async def test_fetch_pr_files_paginated():
    page1 = [{"filename": f"f{i}.py"} for i in range(100)]
    page2 = [{"filename": "last.py"}]

    def side_effect(request):
        page = int(request.url.params.get("page", "1"))
        if page == 1:
            return httpx.Response(200, json=page1)
        if page == 2:
            return httpx.Response(200, json=page2)
        return httpx.Response(200, json=[])

    respx.get(f"{API}/repos/o/r/pulls/1/files").mock(
        side_effect=side_effect,
    )
    async with httpx.AsyncClient() as client:
        result = await fetch_pr_files(client, "o", "r", 1, "tok")
    assert len(result) == 101


@respx.mock
async def test_fetch_pr_details():
    pr = {"number": 1, "title": "Test"}
    respx.get(f"{API}/repos/o/r/pulls/1").respond(json=pr)
    async with httpx.AsyncClient() as client:
        result = await fetch_pr_details(client, "o", "r", 1, "tok")
    assert result["title"] == "Test"


@respx.mock
async def test_fetch_commit_diff():
    respx.get(f"{API}/repos/o/r/commits/abc").respond(text="commit diff")
    async with httpx.AsyncClient() as client:
        result = await fetch_commit_diff(client, "o", "r", "abc", "tok")
    assert result == "commit diff"


@respx.mock
async def test_fetch_repo_labels():
    def side_effect(request):
        page = int(request.url.params.get("page", "1"))
        if page == 1:
            return httpx.Response(
                200,
                json=[{"name": "bug"}, {"name": "enhancement"}],
            )
        return httpx.Response(200, json=[])

    respx.get(f"{API}/repos/o/r/labels").mock(side_effect=side_effect)
    async with httpx.AsyncClient() as client:
        result = await fetch_repo_labels(client, "o", "r", "tok")
    assert result == ["bug", "enhancement"]


@respx.mock
async def test_post_review():
    route = respx.post(f"{API}/repos/o/r/pulls/1/reviews").respond(200)
    async with httpx.AsyncClient() as client:
        await post_review(client, "o", "r", 1, "body", "COMMENT", "tok")
    assert route.called


@respx.mock
async def test_post_comment():
    route = respx.post(f"{API}/repos/o/r/issues/1/comments").respond(200)
    async with httpx.AsyncClient() as client:
        await post_comment(client, "o", "r", 1, "body", "tok")
    assert route.called


@respx.mock
async def test_add_labels():
    route = respx.post(f"{API}/repos/o/r/issues/1/labels").respond(200)
    async with httpx.AsyncClient() as client:
        await add_labels(client, "o", "r", 1, ["bug"], "tok")
    assert route.called


@respx.mock
async def test_create_check_run():
    route = respx.post(f"{API}/repos/o/r/check-runs").respond(200)
    async with httpx.AsyncClient() as client:
        await create_check_run(
            client,
            "o",
            "r",
            "sha",
            "name",
            "neutral",
            {"title": "t", "summary": "s"},
            "tok",
        )
    assert route.called


def test_build_file_summary():
    files = [
        {"filename": "a.py", "status": "modified", "additions": 5, "deletions": 2},
        {"filename": "b.py", "status": "added", "additions": 10, "deletions": 0},
    ]
    result = build_file_summary(files)
    assert "a.py" in result
    assert "+5/-2" in result
    assert "added" in result


def test_truncate_diff_short():
    diff = "short diff"
    assert truncate_diff(diff) == diff


def test_truncate_diff_long():
    diff = "x" * 70_000
    result = truncate_diff(diff)
    assert len(result) < 70_000
    assert "truncated" in result


@respx.mock
async def test_fetch_pr_files_pagination_cap():
    """Pagination stops at MAX_PAGES to prevent infinite loops."""
    from github_app.github import MAX_PAGES

    def side_effect(request):
        # Always return data, simulating a repo with unlimited pages.
        page = int(request.url.params.get("page", "1"))
        return httpx.Response(200, json=[{"filename": f"f{page}.py"}])

    respx.get(f"{API}/repos/o/r/pulls/1/files").mock(
        side_effect=side_effect,
    )
    async with httpx.AsyncClient() as client:
        result = await fetch_pr_files(client, "o", "r", 1, "tok")
    # Should stop after MAX_PAGES pages, not loop forever.
    assert len(result) == MAX_PAGES


@respx.mock
async def test_fetch_repo_labels_pagination_cap():
    """Label fetching stops at MAX_PAGES to prevent infinite loops."""
    from github_app.github import MAX_PAGES

    def side_effect(request):
        page = int(request.url.params.get("page", "1"))
        return httpx.Response(200, json=[{"name": f"label-{page}"}])

    respx.get(f"{API}/repos/o/r/labels").mock(side_effect=side_effect)
    async with httpx.AsyncClient() as client:
        result = await fetch_repo_labels(client, "o", "r", "tok")
    assert len(result) == MAX_PAGES
