from __future__ import annotations

from typer.testing import CliRunner

from worai.commands import graph as graph_cmd
from worai.core.graph import property_delete as property_delete_core
from worai.errors import UsageError


class _FakeResponse:
    def __init__(self, status_code: int = 200, text: str = "ok") -> None:
        self.status_code = status_code
        self.text = text


def test_normalize_predicate_accepts_curie() -> None:
    assert (
        property_delete_core.normalize_predicate("seovoc:html")
        == "https://w3id.org/seovoc/html"
    )


def test_normalize_predicate_rejects_unknown_prefix() -> None:
    try:
        property_delete_core.normalize_predicate("foo:bar")
        assert False, "Expected ValueError for unknown prefix."
    except ValueError as exc:
        assert "Unknown CURIE prefix" in str(exc)


def test_run_property_delete_dry_run(monkeypatch) -> None:
    called: dict[str, object] = {}

    def _stub_graphql_request(_endpoint, _api_key, _query, timeout=60, include_private=False):
        called["include_private"] = include_private
        return {
            "data": {
                "entities": [
                    {"iri": "https://example.com/e1", "value": "<html>...</html>"},
                    {"iri": "https://example.com/e2", "value": "x"},
                    {"iri": "https://example.com/e3", "value": None},
                ]
            }
        }

    monkeypatch.setattr(property_delete_core, "graphql_request", _stub_graphql_request)
    options = property_delete_core.PropertyDeleteOptions(
        api_key="wl_123",
        predicate="seovoc:html",
        dry_run=True,
    )
    summary = property_delete_core.run_property_delete(options)

    assert summary.predicate == "https://w3id.org/seovoc/html"
    assert summary.candidates == 2
    assert summary.attempted == 0
    assert summary.removed == 0
    assert summary.failed == 0
    assert called["include_private"] is True


def test_run_property_delete_applies_patch(monkeypatch) -> None:
    calls: list[tuple[str, str]] = []
    headers_seen: list[dict[str, str] | None] = []

    def _stub_graphql_request(_endpoint, _api_key, _query, timeout=60, include_private=False):
        return {
            "data": {
                "entities": [
                    {"iri": "https://example.com/e1", "value": "x"},
                    {"iri": "https://example.com/e2", "value": "y"},
                ]
            }
        }

    def _stub_patch(url, headers=None, data=None, timeout=60):
        calls.append((url, data or ""))
        headers_seen.append(headers)
        return _FakeResponse(status_code=200, text="ok")

    monkeypatch.setattr(property_delete_core, "graphql_request", _stub_graphql_request)
    monkeypatch.setattr(property_delete_core.requests, "patch", _stub_patch)

    options = property_delete_core.PropertyDeleteOptions(
        api_key="wl_123",
        predicate="seovoc:html",
        dry_run=False,
        workers=1,
    )
    summary = property_delete_core.run_property_delete(options)

    assert summary.candidates == 2
    assert summary.attempted == 2
    assert summary.removed == 2
    assert summary.failed == 0
    assert len(calls) == 2
    assert '"path": "/https://w3id.org/seovoc/html"' in calls[0][1]
    assert headers_seen[0] is not None
    assert headers_seen[0]["X-include-Private"] == "true"


def test_graph_property_delete_cli_calls_core(monkeypatch) -> None:
    seen: list[property_delete_core.PropertyDeleteOptions] = []

    def _stub_run(options):
        seen.append(options)
        if options.dry_run:
            return property_delete_core.PropertyDeleteSummary(
                predicate="https://w3id.org/seovoc/html",
                candidates=2,
                attempted=0,
                removed=0,
                failed=0,
            )
        return property_delete_core.PropertyDeleteSummary(
            predicate="https://w3id.org/seovoc/html",
            candidates=2,
            attempted=2,
            removed=2,
            failed=0,
        )

    monkeypatch.setattr(graph_cmd, "run_property_delete", _stub_run)
    monkeypatch.setattr(graph_cmd, "resolve_api_key", lambda _cfg=None: "wl_123")

    runner = CliRunner()
    result = runner.invoke(
        graph_cmd.app,
        ["property", "delete", "seovoc:html", "--yes"],
    )

    assert result.exit_code == 0
    assert len(seen) == 2
    assert seen[0].dry_run is True
    assert seen[1].dry_run is False


def test_graph_property_delete_cli_requires_api_key(monkeypatch) -> None:
    monkeypatch.setattr(graph_cmd, "resolve_api_key", lambda _cfg=None: None)
    runner = CliRunner()
    result = runner.invoke(graph_cmd.app, ["property", "delete", "seovoc:html", "--yes"])

    assert result.exit_code == 1
    assert isinstance(result.exception, UsageError)
    assert "WORDLIFT_KEY is required" in str(result.exception)
