from __future__ import annotations

import csv
import json
from pathlib import Path

import pytest
from typer.testing import CliRunner

from worai.commands import structured_data as structured_data_cmd
from worai.core.structured_data_inventory import (
    IngestLoaderUnavailableError,
    InventoryOptions,
    InventoryRow,
    build_inventory,
    build_inventory_row,
    run_inventory,
)
from worai.core.url_sources import UrlRecord
from worai.errors import UsageError


def test_build_inventory_row_extracts_faq_and_types() -> None:
    html = """
    <html><head>
      <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@graph": [
          {"@id": "https://data.example.com/dataset/page#faq", "@type": "https://schema.org/FAQPage"},
          {"@id": "https://example.com/page", "@type": ["https://schema.org/WebPage", "Thing"]}
        ]
      }
      </script>
    </head></html>
    """

    row = build_inventory_row(
        "https://example.com/page",
        html,
        dataset_uri="https://data.example.com/dataset",
    )

    assert row.url == "https://example.com/page"
    assert row.faq_markup == "yes"
    assert row.faq_markup_from_graph == "yes"
    assert set(row.types.split(",")) == {"FAQPage", "WebPage", "Thing"}
    payload = json.loads(row.structured_data)
    assert payload["@context"] == "https://schema.org"
    assert len(payload["@graph"]) == 2


def test_build_inventory_row_ignores_invalid_jsonld_and_checks_faq_id() -> None:
    html = """
    <html><head>
      <script type="application/ld+json">{ invalid json }</script>
      <script type="application/ld+json">
      {"@type": "FAQPage", "@id": "https://elsewhere.example/faq"}
      </script>
    </head></html>
    """

    row = build_inventory_row(
        "https://example.com/page",
        html,
        dataset_uri="https://data.example.com/dataset/",
    )

    assert row.faq_markup == "yes"
    assert row.faq_markup_from_graph == "no"


def test_run_inventory_requires_exactly_one_destination() -> None:
    options = InventoryOptions(
        source="https://example.com/sitemap.xml",
        api_key="key",
    )
    with pytest.raises(UsageError):
        run_inventory(options)

    options = InventoryOptions(
        source="https://example.com/sitemap.xml",
        api_key="key",
        output="out.csv",
        destination_spreadsheet_id="spreadsheet-id",
    )
    with pytest.raises(UsageError):
        run_inventory(options)


def test_run_inventory_requires_destination_sheet_name() -> None:
    options = InventoryOptions(
        source="https://example.com/sitemap.xml",
        api_key="key",
        destination_spreadsheet_id="spreadsheet-id",
    )
    with pytest.raises(UsageError):
        run_inventory(options)


def test_run_inventory_writes_csv(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    rows = [
        InventoryRow(
            url="https://example.com/a",
            faq_markup="no",
            faq_markup_from_graph="no",
            types="WebPage",
            structured_data='{"@context":"https://schema.org","@graph":[]}',
        )
    ]

    monkeypatch.setattr("worai.core.structured_data_inventory.build_inventory", lambda _options: rows)

    output_path = tmp_path / "inventory.csv"
    run_inventory(
        InventoryOptions(
            source="https://example.com/sitemap.xml",
            api_key="key",
            output=str(output_path),
        )
    )

    with output_path.open("r", encoding="utf-8") as handle:
        reader = csv.DictReader(handle)
        records = list(reader)

    assert len(records) == 1
    assert records[0]["url"] == "https://example.com/a"
    assert records[0]["types"] == "WebPage"


def test_inventory_command_builds_options_and_emits_summary(monkeypatch: pytest.MonkeyPatch) -> None:
    captured: dict[str, object] = {}

    def _fake_run_inventory(options):
        captured["options"] = options
        return []

    monkeypatch.setattr(structured_data_cmd, "resolve_profile_api_key", lambda _ctx, explicit_profile=None: ("key", explicit_profile or "default", "worai.toml"))
    monkeypatch.setattr(structured_data_cmd, "run_inventory", _fake_run_inventory)

    runner = CliRunner()
    result = runner.invoke(
        structured_data_cmd.app,
        ["inventory", "https://example.com/sitemap.xml", "--output", "report.csv"],
    )

    assert result.exit_code == 0
    payload = json.loads(result.output)
    assert payload["total"] == 0
    options = captured["options"]
    assert isinstance(options, InventoryOptions)
    assert options.source == "https://example.com/sitemap.xml"
    assert options.output == "report.csv"
    assert options.concurrency == "auto"


def test_inventory_command_sets_destination_sheet_options(monkeypatch: pytest.MonkeyPatch) -> None:
    captured: dict[str, object] = {}

    def _fake_run_inventory(options):
        captured["options"] = options
        return []

    monkeypatch.setattr(structured_data_cmd, "resolve_profile_api_key", lambda _ctx, explicit_profile=None: ("key", explicit_profile or "default", "worai.toml"))
    monkeypatch.setattr(structured_data_cmd, "run_inventory", _fake_run_inventory)

    runner = CliRunner()
    result = runner.invoke(
        structured_data_cmd.app,
        [
            "inventory",
            "https://example.com/sitemap.xml",
            "--destination-sheet-id",
            "spreadsheet-id",
            "--destination-sheet-name",
            "Inventory",
        ],
    )

    assert result.exit_code == 0
    options = captured["options"]
    assert isinstance(options, InventoryOptions)
    assert options.destination_spreadsheet_id == "spreadsheet-id"
    assert options.destination_sheet_name == "Inventory"


def test_inventory_command_sets_concurrency(monkeypatch: pytest.MonkeyPatch) -> None:
    captured: dict[str, object] = {}

    def _fake_run_inventory(options):
        captured["options"] = options
        return []

    monkeypatch.setattr(structured_data_cmd, "resolve_profile_api_key", lambda _ctx, explicit_profile=None: ("key", explicit_profile or "default", "worai.toml"))
    monkeypatch.setattr(structured_data_cmd, "run_inventory", _fake_run_inventory)

    runner = CliRunner()
    result = runner.invoke(
        structured_data_cmd.app,
        [
            "inventory",
            "https://example.com/sitemap.xml",
            "--output",
            "report.csv",
            "--concurrency",
            "3",
        ],
    )

    assert result.exit_code == 0
    options = captured["options"]
    assert isinstance(options, InventoryOptions)
    assert options.concurrency == "3"


def test_inventory_command_sets_source_type(monkeypatch: pytest.MonkeyPatch) -> None:
    captured: dict[str, object] = {}

    def _fake_run_inventory(options):
        captured["options"] = options
        return []

    monkeypatch.setattr(structured_data_cmd, "resolve_profile_api_key", lambda _ctx, explicit_profile=None: ("key", explicit_profile or "default", "worai.toml"))
    monkeypatch.setattr(structured_data_cmd, "run_inventory", _fake_run_inventory)

    runner = CliRunner()
    result = runner.invoke(
        structured_data_cmd.app,
        [
            "inventory",
            "/tmp/debug_cloud",
            "--output",
            "report.csv",
            "--source-type",
            "debug-cloud",
        ],
    )

    assert result.exit_code == 0
    options = captured["options"]
    assert isinstance(options, InventoryOptions)
    assert options.source_type == "debug-cloud"


def test_inventory_command_sets_new_ingest_flags(monkeypatch: pytest.MonkeyPatch) -> None:
    captured: dict[str, object] = {}

    def _fake_run_inventory(options):
        captured["options"] = options
        return []

    monkeypatch.setattr(structured_data_cmd, "resolve_profile_api_key", lambda _ctx, explicit_profile=None: ("key", explicit_profile or "default", "worai.toml"))
    monkeypatch.setattr(structured_data_cmd, "run_inventory", _fake_run_inventory)

    runner = CliRunner()
    result = runner.invoke(
        structured_data_cmd.app,
        [
            "inventory",
            "https://example.com/sitemap.xml",
            "--output",
            "report.csv",
            "--ingest-source",
            "sitemap",
            "--ingest-loader",
            "web_scrape_api",
            "--no-ingest-passthrough-when-html",
        ],
    )

    assert result.exit_code == 0
    options = captured["options"]
    assert isinstance(options, InventoryOptions)
    assert options.ingest_source == "sitemap"
    assert options.ingest_loader == "web_scrape_api"
    assert options.ingest_passthrough_when_html is False


def test_build_inventory_uses_browser_with_shared_user_agent(monkeypatch: pytest.MonkeyPatch) -> None:
    captured: dict[str, object] = {}

    class _FakeResponse:
        status = 200

    class _FakePage:
        def content(self) -> str:
            return (
                "<script type='application/ld+json'>"
                "{\"@type\":\"FAQPage\",\"@id\":\"https://dataset.example/page#faq\"}"
                "</script>"
            )

        def close(self) -> None:
            return None

    class _FakeBrowser:
        def __init__(self, **kwargs):
            captured["browser_kwargs"] = kwargs

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc, tb):
            return None

        def open(self, _url: str):
            return _FakePage(), _FakeResponse(), 1.0, []

    monkeypatch.setattr(
        "worai.core.structured_data_inventory.get_dataset_uri",
        lambda _api_key, _base_url: "https://dataset.example",
    )
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.resolve_url_records",
        lambda _opts: [UrlRecord(url="https://example.com/page")],
    )
    monkeypatch.setattr("worai.core.structured_data_inventory.Browser", _FakeBrowser)

    options = InventoryOptions(source="https://example.com/sitemap.xml", api_key="key")
    rows = build_inventory(options)

    assert len(rows) == 1
    assert rows[0].faq_markup == "yes"
    browser_kwargs = captured["browser_kwargs"]
    assert isinstance(browser_kwargs, dict)
    assert browser_kwargs.get("headless") is True
    assert browser_kwargs.get("user_agent")


def test_build_inventory_loader_simple_uses_requests(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.get_dataset_uri",
        lambda _api_key, _base_url: "https://dataset.example",
    )
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.resolve_url_records",
        lambda _opts: [UrlRecord(url="https://example.com/page")],
    )

    class _FakeResponse:
        status_code = 200
        text = (
            "<script type='application/ld+json'>"
            "{\"@type\":\"FAQPage\",\"@id\":\"https://dataset.example/page#faq\"}"
            "</script>"
        )

    monkeypatch.setattr("worai.core.structured_data_inventory.requests.get", lambda *args, **kwargs: _FakeResponse())

    class _FailBrowser:
        def __init__(self, **_kwargs):
            raise AssertionError("Browser should not be initialized for simple loader")

    monkeypatch.setattr("worai.core.structured_data_inventory.Browser", _FailBrowser)
    rows = build_inventory(
        InventoryOptions(
            source="https://example.com/sitemap.xml",
            api_key="key",
            ingest_loader="simple",
        )
    )
    assert len(rows) == 1
    assert rows[0].faq_markup == "yes"


def test_build_inventory_uses_embedded_html_without_browser(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.get_dataset_uri",
        lambda _api_key, _base_url: "https://dataset.example",
    )
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.resolve_url_records",
        lambda _opts: [
            UrlRecord(
                url="https://example.com/page",
                html=(
                    "<script type='application/ld+json'>"
                    "{\"@type\":\"FAQPage\",\"@id\":\"https://dataset.example/page#faq\"}"
                    "</script>"
                ),
                source_type="debug-cloud",
            )
        ],
    )

    class _FailBrowser:
        def __init__(self, **_kwargs):
            raise AssertionError("Browser should not be initialized for embedded HTML records")

    monkeypatch.setattr("worai.core.structured_data_inventory.Browser", _FailBrowser)

    options = InventoryOptions(
        source="/tmp/debug_cloud",
        source_type="debug-cloud",
        api_key="key",
    )
    rows = build_inventory(options)

    assert len(rows) == 1
    assert rows[0].faq_markup == "yes"


def test_build_inventory_playwright_unavailable_raises_typed_error(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.get_dataset_uri",
        lambda _api_key, _base_url: "https://dataset.example",
    )
    monkeypatch.setattr(
        "worai.core.structured_data_inventory.resolve_url_records",
        lambda _opts: [UrlRecord(url="https://example.com/page")],
    )

    class _FailBrowser:
        def __init__(self, **_kwargs):
            raise RuntimeError("Playwright is not installed")

    monkeypatch.setattr("worai.core.structured_data_inventory.Browser", _FailBrowser)

    with pytest.raises(IngestLoaderUnavailableError):
        build_inventory(
            InventoryOptions(
                source="https://example.com/sitemap.xml",
                api_key="key",
                ingest_loader="playwright",
            )
        )
