from __future__ import annotations

import json
import sys
import types
from pathlib import Path

import pytest

_sdk_module = types.ModuleType("wordlift_sdk")
_sdk_structured = types.ModuleType("wordlift_sdk.structured_data")
_sdk_render = types.ModuleType("wordlift_sdk.render")
_sdk_validation = types.ModuleType("wordlift_sdk.validation")


class _StubStructuredDataEngine:
    def get_dataset_uri(self, api_key: str, base_url: str | None = None) -> str:
        return "urn:dataset"


class _StubDatasetResolver:
    def get_dataset_uri(self, api_key: str, base_url: str | None = None) -> str:
        return "urn:dataset"


class _StubStructuredDataOptions:
    def __init__(self, **_kwargs) -> None:
        for key, value in _kwargs.items():
            setattr(self, key, value)


class _StubStructuredDataResult:
    def __init__(self, **kwargs) -> None:
        self.__dict__.update(kwargs)


class _StubYarrrmlPipeline:
    def make_reusable_yarrrml(self, yarrrml: str, url: str) -> str:
        return f"# {url}\n{yarrrml}"


class _StubAgentGenerator:
    def generate_from_agent(self, *args, **_kwargs):
        return "mappings: []", {"@context": "https://schema.org", "@type": "Thing"}


class _StubSchemaGuide:
    def shape_specs_for_type(self, _type_name: str | None) -> list[str]:
        return []


class _StubRenderOptions:
    def __init__(self, **_kwargs) -> None:
        for key, value in _kwargs.items():
            setattr(self, key, value)


class _StubCleanupOptions:
    def __init__(self, **_kwargs) -> None:
        pass


def _stub_render_html(_options):
    return _FakeRendered()


def _stub_clean_xhtml(xhtml: str, _options):
    return xhtml


def _stub_validate_file(*_args, **_kwargs):
    class _Result:
        conforms = True
        warning_count = 0
        report_text = "OK"

    return _Result()


def _stub_validate_jsonld_from_url(*_args, **_kwargs):
    return _stub_validate_file()


_sdk_structured.StructuredDataEngine = _StubStructuredDataEngine
_sdk_structured.DatasetResolver = _StubDatasetResolver
_sdk_structured.StructuredDataOptions = _StubStructuredDataOptions
_sdk_structured.StructuredDataResult = _StubStructuredDataResult
_sdk_structured.YarrrmlPipeline = _StubYarrrmlPipeline
_sdk_structured.AgentGenerator = _StubAgentGenerator
_sdk_structured.SchemaGuide = _StubSchemaGuide

_sdk_render.RenderOptions = _StubRenderOptions
_sdk_render.CleanupOptions = _StubCleanupOptions
_sdk_render.render_html = _stub_render_html
_sdk_render.clean_xhtml = _stub_clean_xhtml

_sdk_validation.validate_file = _stub_validate_file
_sdk_validation.validate_jsonld_from_url = _stub_validate_jsonld_from_url

_sdk_module.structured_data = _sdk_structured
_sdk_module.render = _sdk_render
_sdk_module.validation = _sdk_validation

sys.modules.setdefault("wordlift_sdk", _sdk_module)
sys.modules.setdefault("wordlift_sdk.structured_data", _sdk_structured)
sys.modules.setdefault("wordlift_sdk.render", _sdk_render)
sys.modules.setdefault("wordlift_sdk.validation", _sdk_validation)

from worai.structured_data import CreateRequest, CreateWorkflow, GenerateRequest, GenerateWorkflow


class _FakeRendered:
    def __init__(self) -> None:
        self.html = "<html><body>Hi</body></html>"
        self.xhtml = "<html><body>Hi</body></html>"
        self.status_code = 200


class _FakeRenderer:
    def __init__(self) -> None:
        self.seen = []

    def render(self, url: str, log) -> tuple[object, str]:
        self.seen.append(url)
        return _FakeRendered(), "<html><body>Hi</body></html>"


class _FakeAgent:
    def __init__(self) -> None:
        self.calls = []

    def generate(self, *args, **kwargs):
        self.calls.append((args, kwargs))
        return "mappings: []", {"@context": "https://schema.org", "@type": "Thing"}


class _FakeValidator:
    def __init__(self) -> None:
        self.calls = []

    def validate(self, jsonld_path: Path, target_type: str, workdir: Path) -> str:
        self.calls.append((jsonld_path, target_type, workdir))
        return "OK"


def test_create_workflow_writes_outputs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    def _fake_get_dataset_uri(self, api_key: str, base_url: str | None = None) -> str:
        return "urn:dataset"

    def _fake_make_reusable_yarrrml(self, yarrrml: str, url: str) -> str:
        return f"# {url}\n{yarrrml}"

    monkeypatch.setattr(
        "wordlift_sdk.structured_data.StructuredDataEngine.get_dataset_uri",
        _fake_get_dataset_uri,
    )
    monkeypatch.setattr(
        "wordlift_sdk.structured_data.YarrrmlPipeline.make_reusable_yarrrml",
        _fake_make_reusable_yarrrml,
    )

    agent = _FakeAgent()
    renderer = _FakeRenderer()
    validator = _FakeValidator()

    request = CreateRequest(
        url="https://example.com",
        target_type="Thing",
        output_dir=tmp_path,
        base_name="structured-data",
        jsonld_path=None,
        yarrml_path=None,
        api_key="test-key",
        base_url="https://api.wordlift.io",
        debug=False,
        headed=False,
        timeout_ms=1000,
        max_retries=0,
        quality_check=False,
        max_xhtml_chars=1000,
        max_text_node_chars=200,
        max_nesting_depth=1,
        verbose=False,
        validate=True,
        wait_until="load",
    )

    workflow = CreateWorkflow(agent=agent, renderer=renderer, validator=validator)
    result = workflow.run(request, log=lambda *_: None)

    jsonld_path = tmp_path / "structured-data.jsonld"
    yarrml_path = tmp_path / "structured-data.yarrml"
    assert jsonld_path.exists()
    assert yarrml_path.exists()
    assert json.loads(jsonld_path.read_text())["@type"] == "Thing"
    assert result.jsonld_filename == str(jsonld_path)
    assert result.yarrml_filename == str(yarrml_path)
    assert agent.calls
    assert renderer.seen == ["https://example.com"]
    assert validator.calls


def test_generate_workflow_runs_batch(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    yarrrml_path = tmp_path / "mapping.yarrrml"
    yarrrml_path.write_text("mappings: []")

    monkeypatch.setattr(
        "worai.structured_data.orchestrator.resolve_input_urls",
        lambda value: ["https://example.com"],
    )
    monkeypatch.setattr(
        "worai.structured_data.orchestrator.filter_urls",
        lambda urls, regex, max_pages: urls,
    )
    monkeypatch.setattr(
        "wordlift_sdk.structured_data.StructuredDataEngine.get_dataset_uri",
        lambda self, api_key, base_url=None: "urn:dataset",
    )

    class _FakeBatch:
        def __init__(self, **_kwargs) -> None:
            pass

        def generate(self, urls, yarrrml, log):
            return {
                "format": "ttl",
                "output_dir": str(tmp_path),
                "total": len(urls),
                "success": 1,
                "failed": 0,
                "errors": [],
            }

    monkeypatch.setattr("worai.structured_data.orchestrator.BatchGenerator", _FakeBatch)

    request = GenerateRequest(
        input_value="https://example.com/sitemap.xml",
        yarrrml_path=yarrrml_path,
        regex=".*",
        output_dir=tmp_path,
        output_format="ttl",
        concurrency="1",
        api_key="test-key",
        base_url="https://api.wordlift.io",
        headed=False,
        timeout_ms=1000,
        wait_until="load",
        max_xhtml_chars=1000,
        max_text_node_chars=200,
        max_pages=None,
        verbose=False,
    )

    workflow = GenerateWorkflow()
    summary = workflow.run(request, log=lambda *_: None)

    assert summary["input"] == "https://example.com/sitemap.xml"
    assert summary["success"] == 1
