from __future__ import annotations

import pytest

from recoil.pipeline.tools.autonomy import linear_client


def _label(label_id: str, name: str) -> dict[str, str]:
    return {"id": label_id, "name": name}


def _issue(
    issue_id: str,
    identifier: str,
    labels: list[str],
    *,
    state: str = "Backlog",
    attachments: list[dict] | None = None,
) -> dict:
    return {
        "id": issue_id,
        "identifier": identifier,
        "title": f"Title {identifier}",
        "description": "Goal\n\nAcceptance Criteria\n\nVerification\n\nScope\n\nOut of Scope",
        "url": f"https://linear.app/recoil/issue/{identifier}",
        "state": {"name": state},
        "labels": {
            "nodes": [
                _label(f"label-{issue_id}-{index}", name)
                for index, name in enumerate(labels)
            ]
        },
        "attachments": {"nodes": attachments or []},
    }


def test_list_candidates_filters_and_normalizes(monkeypatch):
    monkeypatch.setenv("LINEAR_API_KEY", "lin_test")
    issues = [
        _issue("issue-1", "REC-1", ["autonomy-ok", "feature"]),
        _issue("issue-2", "REC-2", ["autonomy-ok", "autonomy-claimed"]),
        _issue("issue-3", "REC-3", ["feature"]),
        _issue("issue-4", "REC-4", ["autonomy-ok"], state="Done"),
        _issue(
            "issue-5",
            "REC-5",
            ["autonomy-ok"],
            attachments=[{"url": "https://github.com/org/repo/pull/12"}],
        ),
    ]

    def transport(url, headers, payload):
        assert url == linear_client.LINEAR_API_URL
        assert headers["Authorization"] == "lin_test"
        assert payload["variables"] == {"team": "Recoil"}
        return {"data": {"issues": {"nodes": issues}}}

    candidates = linear_client.list_candidates("Recoil", transport=transport)

    assert candidates == [
        {
            "issue_id": "issue-1",
            "identifier": "REC-1",
            "title": "Title REC-1",
            "body": "Goal\n\nAcceptance Criteria\n\nVerification\n\nScope\n\nOut of Scope",
            "labels": ["autonomy-ok", "feature"],
            "url": "https://linear.app/recoil/issue/REC-1",
            "has_open_pr": False,
        }
    ]


def test_project_claim_returns_false_on_transport_error(monkeypatch):
    monkeypatch.setenv("LINEAR_API_KEY", "lin_test")

    def transport(_url, _headers, _payload):
        raise OSError("network down")

    assert (
        linear_client.project_claim(
            "issue-1",
            "REC-1",
            "run-1",
            transport=transport,
        )
        is False
    )


def test_project_claim_adds_claim_label_and_comment(monkeypatch):
    monkeypatch.setenv("LINEAR_API_KEY", "lin_test")
    calls: list[dict] = []

    def transport(_url, _headers, payload):
        calls.append(payload)
        query = payload["query"]
        if "AutonomyIssueLabels" in query:
            return {
                "data": {
                    "issue": {
                        "id": "issue-1",
                        "labels": {"nodes": [_label("ok-id", "autonomy-ok")]},
                    },
                    "issueLabels": {
                        "nodes": [
                            _label("ok-id", "autonomy-ok"),
                            _label("claim-id", "autonomy-claimed"),
                        ]
                    },
                }
            }
        if "AutonomyComment" in query:
            assert payload["variables"] == {
                "issueId": "issue-1",
                "body": "claimed by autonomy run run-1",
            }
            return {"data": {"commentCreate": {"success": True}}}
        if "AutonomyUpdateLabels" in query:
            assert payload["variables"] == {
                "issueId": "issue-1",
                "labelIds": ["ok-id", "claim-id"],
            }
            return {"data": {"issueUpdate": {"success": True}}}
        raise AssertionError(query)

    assert linear_client.project_claim("issue-1", "REC-1", "run-1", transport=transport)
    assert [call["variables"]["issueId"] for call in calls] == [
        "issue-1",
        "issue-1",
        "issue-1",
    ]


def test_project_status_only_comments(monkeypatch):
    monkeypatch.setenv("LINEAR_API_KEY", "lin_test")
    calls: list[dict] = []

    def transport(_url, _headers, payload):
        calls.append(payload)
        return {"data": {"commentCreate": {"success": True}}}

    assert linear_client.project_status(
        "issue-1",
        "spec-review failed",
        transport=transport,
    )
    assert len(calls) == 1
    assert "commentCreate" in calls[0]["query"]
    assert calls[0]["variables"] == {
        "issueId": "issue-1",
        "body": "spec-review failed",
    }


def test_mark_blocked_swaps_ok_for_blocked(monkeypatch):
    monkeypatch.setenv("LINEAR_API_KEY", "lin_test")

    def transport(_url, _headers, payload):
        query = payload["query"]
        if "AutonomyIssueLabels" in query:
            return {
                "data": {
                    "issue": {
                        "id": "issue-1",
                        "labels": {
                            "nodes": [
                                _label("ok-id", "autonomy-ok"),
                                _label("keep-id", "feature"),
                            ]
                        },
                    },
                    "issueLabels": {
                        "nodes": [
                            _label("ok-id", "autonomy-ok"),
                            _label("blocked-id", "autonomy-blocked"),
                        ]
                    },
                }
            }
        if "AutonomyUpdateLabels" in query:
            assert payload["variables"] == {
                "issueId": "issue-1",
                "labelIds": ["keep-id", "blocked-id"],
            }
            return {"data": {"issueUpdate": {"success": True}}}
        raise AssertionError(query)

    assert linear_client.mark_blocked("issue-1", transport=transport)
