from __future__ import annotations

from pathlib import Path
from types import SimpleNamespace

import pytest

from worai.core import structured_data_inventory as mod
from worai.errors import UsageError


def test_helper_extract_collect_and_loader_resolution() -> None:
    assert mod._strip_schema_prefix("https://schema.org/Thing") == "Thing"
    assert mod._as_list(None) == []
    assert mod._as_list("x") == ["x"]

    nodes = mod._extract_graph_nodes({"@context": "x", "@graph": [{"@type": "A"}], "k": "v"})
    assert any(node.get("k") == "v" for node in nodes)

    assert mod._collect_types([{"@type": ["http://schema.org/WebPage", "WebPage", 1]}]) == ["WebPage"]
    assert mod._is_faq_page_node({"@type": ["FAQPage"]}) is True
    assert mod._has_faq_from_dataset([{"@type": "FAQPage", "@id": "https://d/x"}], "https://d/") == (True, True)
    assert mod._resolve_loader("auto") == mod.DEFAULT_INGEST_LOADER


def test_fetch_html_and_load_record_paths(monkeypatch: pytest.MonkeyPatch) -> None:
    class _Page:
        def __init__(self, html: str = "<html/>"):
            self._html = html
            self.closed = False

        def content(self):
            return self._html

        def close(self):
            self.closed = True

    class _Browser:
        def __init__(self, payload):
            self.payload = payload

        def open(self, _url):
            return self.payload

    with pytest.raises(RuntimeError, match="Failed to open page"):
        mod._fetch_html(_Browser((None, None, 0.0, [])), "https://e.com")

    with pytest.raises(RuntimeError, match="failed with status 500"):
        mod._fetch_html(_Browser((_Page(), SimpleNamespace(status=500), 0.0, [])), "https://e.com")

    html, status = mod._fetch_html(_Browser((_Page("ok"), SimpleNamespace(status=200), 0.0, [])), "https://e.com")
    assert html == "ok" and status == 200

    monkeypatch.setattr(mod, "_fetch_html_simple", lambda *_a, **_k: ("s", 200))
    monkeypatch.setattr(mod, "_fetch_html_browser", lambda *_a, **_k: ("b", 200))

    ingest = SimpleNamespace(loader="passthrough", passthrough_when_html=False)
    with pytest.raises(UsageError):
        mod._load_html_for_record(SimpleNamespace(url="u", html=None), ingest=ingest, timeout=1, timeout_ms=1)

    ingest.loader = "simple"
    assert mod._load_html_for_record(SimpleNamespace(url="u", html=None), ingest=ingest, timeout=1, timeout_ms=1)[0] == "s"
    ingest.loader = "playwright"
    assert mod._load_html_for_record(SimpleNamespace(url="u", html=None), ingest=ingest, timeout=1, timeout_ms=1)[0] == "b"

    with pytest.raises(UsageError, match="Unsupported ingest loader"):
        mod._load_html_for_record(SimpleNamespace(url="u", html=None), ingest=SimpleNamespace(loader="x", passthrough_when_html=False), timeout=1, timeout_ms=1)


def test_sheet_helpers_and_google_writer(monkeypatch: pytest.MonkeyPatch) -> None:
    class _Session:
        def __init__(self):
            self.posts = []
            self.puts = []

        def get(self, url):
            if "fields=sheets.properties.title" in url:
                return SimpleNamespace(status_code=200, json=lambda: {"sheets": [{"properties": {"title": "Existing"}}]})
            return SimpleNamespace(status_code=500, text="boom", json=lambda: {})

        def post(self, url, json):
            self.posts.append((url, json))
            if url.endswith(":batchUpdate"):
                return SimpleNamespace(status_code=200, text="ok")
            return SimpleNamespace(status_code=200, text="ok")

        def put(self, url, json):
            self.puts.append((url, json))
            return SimpleNamespace(status_code=200, text="ok")

    s = _Session()
    assert mod._get_sheet_titles(s, "sheet") == ["Existing"]

    mod._ensure_sheet_exists(s, "sheet", "Existing")
    assert not s.posts
    mod._ensure_sheet_exists(s, "sheet", "NewSheet")
    assert s.posts

    monkeypatch.setattr(mod, "load_google_credentials", lambda **_k: object())
    monkeypatch.setattr(mod, "AuthorizedSession", lambda _creds: s)

    mod.write_google_sheet(
        "sheet",
        "NewSheet",
        [mod.InventoryRow(url="u", faq_markup="no", faq_markup_from_graph="no", types="", structured_data="{}")],
        client_secrets=None,
        token="t.json",
        port=8080,
    )
    assert s.puts


def test_sheet_error_paths_and_run_inventory_destination(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    class _BadSession:
        def get(self, _url):
            return SimpleNamespace(status_code=500, text="bad", json=lambda: {})

        def post(self, _url, json):
            return SimpleNamespace(status_code=500, text="bad")

        def put(self, _url, json):
            return SimpleNamespace(status_code=500, text="bad")

    with pytest.raises(RuntimeError, match="Failed to read spreadsheet metadata"):
        mod._get_sheet_titles(_BadSession(), "sheet")

    monkeypatch.setattr(mod, "load_google_credentials", lambda **_k: object())
    monkeypatch.setattr(mod, "AuthorizedSession", lambda _creds: _BadSession())
    with pytest.raises(RuntimeError):
        mod.write_google_sheet(
            "sheet",
            "S",
            [mod.InventoryRow(url="u", faq_markup="no", faq_markup_from_graph="no", types="", structured_data="{}")],
            client_secrets=None,
            token="t",
            port=8080,
        )

    monkeypatch.setattr(mod, "build_inventory", lambda _o: [mod.InventoryRow(url="u", faq_markup="no", faq_markup_from_graph="no", types="", structured_data="{}")])
    seen = {}
    monkeypatch.setattr(mod, "write_google_sheet", lambda *args, **kwargs: seen.setdefault("called", True))
    out = tmp_path / "i.csv"
    rows = mod.run_inventory(mod.InventoryOptions(source="s", api_key="k", output=str(out)))
    assert rows and out.exists()
    mod.run_inventory(mod.InventoryOptions(source="s", api_key="k", destination_spreadsheet_id="id", destination_sheet_name="name"))
    assert seen.get("called") is True
