from __future__ import annotations

import os

import pytest

from recoil.pipeline.tools.autonomy import linear_client


def _team_response(team_id: str = "team-1") -> dict:
    return {"data": {"teams": {"nodes": [{"id": team_id, "name": "Recoil"}]}}}


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

    def transport(_url, _headers, payload):
        calls.append(payload)
        if "LinearTeamByName" in payload["query"]:
            assert payload["variables"] == {"team": "Recoil"}
            return _team_response("team-create")
        if "LinearCreateIssue" in payload["query"]:
            assert "issueCreate" in payload["query"]
            assert payload["variables"] == {
                "teamId": "team-create",
                "title": "Drain this",
                "description": "Body text",
                "labelIds": ["label-auto"],
            }
            return {
                "data": {
                    "issueCreate": {
                        "success": True,
                        "issue": {
                            "id": "issue-1",
                            "identifier": "REC-116",
                            "url": "https://linear.app/recoil/issue/REC-116",
                        },
                    }
                }
            }
        raise AssertionError(payload["query"])

    result = linear_client.create_issue(
        "Drain this",
        "Body text",
        label_ids=["label-auto"],
        transport=transport,
    )

    assert result == {
        "success": True,
        "id": "issue-1",
        "identifier": "REC-116",
        "url": "https://linear.app/recoil/issue/REC-116",
    }
    assert [call["query"] for call in calls if "LinearCreateIssue" in call["query"]]


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

    def transport(_url, _headers, payload):
        if "LinearTeamByName" in payload["query"]:
            return _team_response()
        if "LinearCreateIssue" in payload["query"]:
            return {"data": {"issueCreate": {"success": False, "issue": None}}}
        raise AssertionError(payload["query"])

    with pytest.raises(RuntimeError, match="issueCreate failed"):
        linear_client.create_issue("title", "description", transport=transport)


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

    def transport(_url, _headers, payload):
        assert "LinearTeamByName" in payload["query"]
        return {"data": {"teams": {"nodes": []}}}

    with pytest.raises(RuntimeError, match="Linear team not found"):
        linear_client.create_issue("title", "description", transport=transport)


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

    def transport(_url, _headers, payload):
        calls.append(payload)
        if "LinearTeamByName" in payload["query"]:
            return _team_response("team-label")
        if "LinearIssueLabels" in payload["query"]:
            assert payload["variables"] == {"teamId": "team-label"}
            return {
                "data": {
                    "issueLabels": {
                        "nodes": [
                            {"id": "label-existing", "name": "auto-filed"},
                            {"id": "label-other", "name": "other"},
                        ]
                    }
                }
            }
        raise AssertionError(payload["query"])

    assert linear_client.ensure_label(transport=transport) == "label-existing"
    assert not any("issueLabelCreate" in call["query"] for call in calls)


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

    def transport(_url, _headers, payload):
        calls.append(payload)
        if "LinearTeamByName" in payload["query"]:
            return _team_response("team-label")
        if "LinearIssueLabels" in payload["query"]:
            return {"data": {"issueLabels": {"nodes": []}}}
        if "LinearCreateIssueLabel" in payload["query"]:
            assert payload["variables"] == {
                "name": "auto-filed",
                "teamId": "team-label",
            }
            return {
                "data": {
                    "issueLabelCreate": {
                        "success": True,
                        "issueLabel": {"id": "label-created", "name": "auto-filed"},
                    }
                }
            }
        raise AssertionError(payload["query"])

    assert linear_client.ensure_label(transport=transport) == "label-created"
    assert any("issueLabelCreate" in call["query"] for call in calls)


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

    def transport(_url, _headers, payload):
        assert "LinearFindingKey" in payload["query"]
        assert "searchableContent: { contains: $q }" in payload["query"]
        assert "searchIssues" not in payload["query"]
        assert payload["variables"] == {
            "q": "finding_key:sha256:none",
            "team": "Recoil",
        }
        return {"data": {"issues": {"nodes": []}}}

    assert (
        linear_client.find_open_issue_by_finding_key(
            "sha256:none",
            transport=transport,
        )
        is None
    )


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

    def transport(_url, _headers, payload):
        assert payload["variables"] == {
            "q": "finding_key:sha256:abc",
            "team": "Recoil",
        }
        return {
            "data": {
                "issues": {
                    "nodes": [
                        {
                            "id": "done-1",
                            "identifier": "REC-1",
                            "url": "https://linear.app/recoil/issue/REC-1",
                            "state": {"type": "completed"},
                        },
                        {
                            "id": "cancel-1",
                            "identifier": "REC-2",
                            "url": "https://linear.app/recoil/issue/REC-2",
                            "state": {"type": "canceled"},
                        },
                        {
                            "id": "open-1",
                            "identifier": "REC-3",
                            "url": "https://linear.app/recoil/issue/REC-3",
                            "state": {"type": "started"},
                        },
                    ]
                }
            }
        }

    assert linear_client.find_open_issue_by_finding_key(
        "sha256:abc",
        transport=transport,
    ) == {
        "identifier": "REC-3",
        "id": "open-1",
        "url": "https://linear.app/recoil/issue/REC-3",
    }


def test_untrusted_strings_are_variables_not_query_text(monkeypatch):
    monkeypatch.setenv("LINEAR_API_KEY", "lin_test")
    title = 'x") { issueDelete(id: "bad") { success } }'
    description = "body with finding_key:sha256:danger"
    team = 'Recoil") } } query Bad { viewer { id } }'
    label_id = 'label") } } mutation Bad { issueDelete(id: "bad") { success } }'
    calls: list[dict] = []

    def transport(_url, _headers, payload):
        calls.append(payload)
        query = payload["query"]
        for unsafe in (title, description, team, label_id):
            assert unsafe not in query
        if "LinearTeamByName" in query:
            assert payload["variables"] == {"team": team}
            return _team_response("team-safe")
        if "LinearCreateIssue" in query:
            assert payload["variables"] == {
                "teamId": "team-safe",
                "title": title,
                "description": description,
                "labelIds": [label_id],
            }
            return {
                "data": {
                    "issueCreate": {
                        "success": True,
                        "issue": {
                            "id": "issue-safe",
                            "identifier": "REC-117",
                            "url": "https://linear.app/recoil/issue/REC-117",
                        },
                    }
                }
            }
        raise AssertionError(query)

    linear_client.create_issue(
        title,
        description,
        team=team,
        label_ids=[label_id],
        transport=transport,
    )
    assert len(calls) == 2


@pytest.mark.skipif(
    not os.environ.get("LINEAR_API_KEY"),
    reason="LINEAR_API_KEY is required for the opt-in Linear schema probe",
)
def test_live_find_open_issue_by_finding_key_probe_returns_none():
    assert (
        linear_client.find_open_issue_by_finding_key(
            "sha256:0000000000000000000000000000000000000000000000000000000000000000"
        )
        is None
    )
