from __future__ import annotations

import datetime as dt
from pathlib import Path

import pytest
from rdflib import BNode, Literal, URIRef

import worai.core.gsc as gsc
import worai.core.patch as patch_core
import worai.structured_data.batch as batch_mod


def test_batch_generator_success_and_empty(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    class _Yarrrml:
        @staticmethod
        def build_output_basename(_url: str) -> str:
            return "page"

        @staticmethod
        def ensure_no_blank_nodes(_graph):
            return None

    class _Materializer:
        def __init__(self, _p):
            pass

        @staticmethod
        def normalize(*_a, **_k):
            return "norm", []

        @staticmethod
        def materialize(*_a, **_k):
            return {"@context": "https://schema.org", "@id": "https://example.com/#x", "@type": "Thing"}

        @staticmethod
        def postprocess(jsonld_raw, *_a, **_k):
            return jsonld_raw

    class _Rendered:
        xhtml = "<html/>"
        status_code = 200

    class _Settings:
        workers = 1
        is_auto = False

    monkeypatch.setattr(batch_mod, "YarrrmlPipeline", _Yarrrml)
    monkeypatch.setattr(batch_mod, "MaterializationPipeline", _Materializer)
    monkeypatch.setattr(batch_mod, "parse_concurrency", lambda *_a, **_k: _Settings())
    monkeypatch.setattr(batch_mod, "render_html", lambda _opts: _Rendered())
    monkeypatch.setattr(batch_mod, "clean_xhtml", lambda _x, _o: "<clean/>")

    gen = batch_mod.BatchGenerator(
        output_dir=tmp_path,
        output_format="jsonld",
        concurrency="1",
        headed=False,
        timeout_ms=1000,
        wait_until="load",
        max_xhtml_chars=1000,
        max_text_node_chars=100,
        dataset_uri="https://data.example.com",
        verbose=False,
    )
    out = gen.generate(["https://example.com/page"], "mappings", lambda _m: None)
    assert out["success"] == 1
    assert (tmp_path / "page.jsonld").exists()

    with pytest.raises(Exception):
        gen.generate([], "mappings", lambda _m: None)


def test_patch_core_helpers_and_run(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    assert patch_core.format_json_ld_node(URIRef("https://example.com/x")) == {"@id": "https://example.com/x"}
    assert patch_core.format_json_ld_node(BNode("b1"))["@id"].startswith("_:")
    assert patch_core.format_json_ld_node(Literal("x", lang="en"))["@language"] == "en"

    subject, ok, message = patch_core.process_subject(
        "https://example.com/s",
        {URIRef("http://schema.org/name"): [Literal("A")]},
        "key",
        dry_run=True,
        max_retries=1,
        add_types=False,
    )
    assert ok and "DRY RUN" in message and "example.com/s" in subject

    monkeypatch.setattr(
        patch_core.subprocess,
        "run",
        lambda *_a, **_k: type("R", (), {"stdout": "ok"})(),
    )
    subject, ok, _ = patch_core.process_subject(
        "https://example.com/s",
        {patch_core.RDF_TYPE: [URIRef("http://schema.org/Thing")]},
        "key",
        dry_run=False,
        max_retries=1,
        add_types=True,
    )
    assert ok and "example.com/s" in subject

    subjects_file = tmp_path / "subjects.txt"
    subjects_file.write_text("https://example.com/s\n", encoding="utf-8")
    assert patch_core._load_subjects_from_file(str(subjects_file)) == {"https://example.com/s"}

    ttl = tmp_path / "in.ttl"
    ttl.write_text('<https://example.com/s> <http://schema.org/name> "A" .', encoding="utf-8")
    seen: list[tuple[str, bool]] = []
    monkeypatch.setattr(
        patch_core,
        "process_subject",
        lambda subject_uri, predicates, api_key, dry_run, max_retries, add_types: seen.append((subject_uri, dry_run)) or (subject_uri, True, "ok"),
    )
    patch_core.run(
        patch_core.PatchOptions(
            input_file=str(ttl),
            api_key="k",
            dry_run=True,
            workers=1,
            retries=1,
        )
    )
    assert seen


def test_gsc_core_functions(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    class _Resp:
        def __init__(self, code: int, payload: dict):
            self.status_code = code
            self._payload = payload
            self.text = "bad"

        def json(self):
            return self._payload

    class _Session:
        @staticmethod
        def post(_url, json=None):
            if json and json.get("startRow") == 0:
                return _Resp(200, {"rows": [{"keys": ["https://example.com/a"], "clicks": 1, "impressions": 2, "ctr": 0.5, "position": 3}]})
            return _Resp(200, {"rows": []})

    data = gsc.gsc_post(_Session(), "sc-domain:example.com", {"x": 1})
    assert isinstance(data, dict)

    metrics = gsc.fetch_page_metrics(
        _Session(),
        "sc-domain:example.com",
        dt.date(2026, 1, 1),
        dt.date(2026, 1, 2),
        row_limit=1,
        search_type="web",
        data_state="all",
    )
    assert "https://example.com/a" in metrics

    merged = gsc.merge_metrics({"a": {"clicks": 1}}, {"a": {"impressions": 2}}, {})
    assert "clicks_7d" in merged["a"]

    monkeypatch.setattr(gsc, "load_credentials", lambda *_a, **_k: object())
    monkeypatch.setattr(gsc, "AuthorizedSession", lambda _c: object())
    monkeypatch.setattr(gsc, "find_last_available_date", lambda _s, _site: dt.date(2026, 1, 7))
    monkeypatch.setattr(gsc, "fetch_page_metrics", lambda *_a, **_k: {"a": {"clicks": 1, "impressions": 2, "ctr": 0.5, "position": 3}})
    rows = gsc.run(
        gsc.GscOptions(
            site="sc-domain:example.com",
            client_secrets="c.json",
            token=str(tmp_path / "token.json"),
            port=0,
            row_limit=100,
            search_type="web",
            data_state="all",
        )
    )
    assert isinstance(rows, list)
    assert rows[0]["page"] == "a"
