from __future__ import annotations

import json
from pathlib import Path
from types import SimpleNamespace

import pytest

from worai.seocheck import cli as mod
from worai.seocheck.checks.types import CheckResult


def _site_result(status: str = "ok"):
    return SimpleNamespace(url="u", status=status, details="d", status_code=200, disallow_all=False)


def _llms_result(status: str = "ok"):
    return SimpleNamespace(url="l", status=status, details="d", status_code=200)


def test_json_print_helpers_and_assets(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
    mod._print_site_results([{"name": "robots", "status": "ok", "details": "d", "url": "u"}], as_json=True)
    j1 = json.loads(capsys.readouterr().out.strip())
    assert j1["name"] == "robots"

    mod._print_page_results("u", [CheckResult(name="c", status="ok")], as_json=True)
    j2 = json.loads(capsys.readouterr().out.strip())
    assert j2["type"] == "page"

    mod._copy_report_assets(tmp_path)
    assert (tmp_path / "index.html").exists()


def test_serve_report_handles_keyboard_interrupt(monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
    class _Server:
        server_address = ("127.0.0.1", 1234)

        def serve_forever(self):
            raise KeyboardInterrupt()

        def shutdown(self):
            print("shutdown")

    monkeypatch.setattr(mod.http.server, "ThreadingHTTPServer", lambda *_a, **_k: _Server())
    opened = {}
    monkeypatch.setattr(mod.webbrowser, "open", lambda url: opened.setdefault("url", url))
    mod._serve_report(tmp_path, open_browser=True)
    out = capsys.readouterr().out
    assert "Report server running" in out
    assert "shutdown" in out
    assert opened["url"].startswith("http://127.0.0.1:1234/")


def test_run_invalid_concurrency_and_unknown_checks(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(mod, "parse_sitemap_urls", lambda *_a, **_k: ["https://e.com/p"])
    monkeypatch.setattr(mod, "check_robots_txt", lambda *_a, **_k: _site_result())
    monkeypatch.setattr(mod, "check_llms_txt", lambda *_a, **_k: _llms_result())
    monkeypatch.setattr(mod, "get_page_checks", lambda **_k: [SimpleNamespace(name="status")])

    assert mod.run(["https://e.com/sitemap.xml", "--concurrency", "x", "--no-report-ui"]) == 1
    assert mod.run(["https://e.com/sitemap.xml", "--checks", "missing", "--no-report-ui"]) == 1
    assert mod.run(["https://e.com/sitemap.xml", "--disable-checks", "missing", "--no-report-ui"]) == 1


def test_run_recheck_and_merge_and_outputs(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    report = tmp_path / "report.json"
    report.write_text(json.dumps({"pages": [{"url": "https://e.com/a", "status": "fail", "checks": []}, {"url": "https://e.com/old", "status": "ok", "checks": []}]}), encoding="utf-8")

    monkeypatch.setattr(mod, "parse_sitemap_urls", lambda *_a, **_k: ["https://e.com/a", "https://e.com/b"])
    monkeypatch.setattr(mod, "check_robots_txt", lambda *_a, **_k: _site_result())
    monkeypatch.setattr(mod, "check_llms_txt", lambda *_a, **_k: _llms_result())
    monkeypatch.setattr(mod, "get_page_checks", lambda **_k: [SimpleNamespace(name="status")])

    def _proc(url: str, **_k):
        return (
            {"type": "page", "url": url, "checks": [{"name": "status", "status": "ok"}]},
            {"url": url, "status": "ok", "file": None, "html": None, "checks": [{"name": "status", "status": "ok"}]},
            False,
        )

    monkeypatch.setattr(mod, "_process_url", _proc)
    served = {}
    monkeypatch.setattr(mod, "_serve_report", lambda output_dir, open_browser: served.setdefault("called", (output_dir, open_browser)))

    rc = mod.run([
        "https://e.com/sitemap.xml",
        "--recheck-failed",
        "--recheck-from",
        str(report),
        "--concurrency",
        "2",
        "--output",
        str(tmp_path / "o.json"),
        "--output-summary",
        str(tmp_path / "s.txt"),
    ])
    assert rc == 0
    payload = json.loads((tmp_path / "o.json").read_text(encoding="utf-8"))
    urls = [p["url"] for p in payload["pages"]]
    assert "https://e.com/old" in urls
    assert served.get("called")


def test_run_auto_concurrency_and_recheck_shortcuts(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr(mod, "check_robots_txt", lambda *_a, **_k: _site_result())
    monkeypatch.setattr(mod, "check_llms_txt", lambda *_a, **_k: _llms_result())
    monkeypatch.setattr(mod, "get_page_checks", lambda **_k: [SimpleNamespace(name="status")])

    assert mod.run(["https://e.com/sitemap.xml", "--recheck-failed", "--no-report-ui"]) == 1

    rp = tmp_path / "r.json"
    rp.write_text(json.dumps({"pages": [{"url": "https://e.com/x", "status": "ok", "checks": []}]}), encoding="utf-8")
    monkeypatch.setattr(mod, "parse_sitemap_urls", lambda *_a, **_k: ["https://e.com/x"])
    assert mod.run(["https://e.com/sitemap.xml", "--recheck-failed", "--recheck-from", str(rp), "--no-report-ui"]) == 0

    rp.write_text(json.dumps({"pages": [{"url": "https://e.com/x", "status": "fail", "checks": []}]}), encoding="utf-8")
    monkeypatch.setattr(mod, "parse_sitemap_urls", lambda *_a, **_k: ["https://e.com/a", "https://e.com/b", "https://e.com/c"])

    calls = {"n": 0}

    def _proc(url: str, **_k):
        calls["n"] += 1
        had_error = calls["n"] >= 2
        return (
            {"type": "page", "url": url, "checks": [{"name": "status", "status": "warn" if had_error else "ok"}]},
            {"url": url, "status": "warn" if had_error else "ok", "file": None, "html": None, "checks": [{"name": "status", "status": "warn" if had_error else "ok"}]},
            had_error,
        )

    monkeypatch.setattr(mod, "_process_url", _proc)
    rc = mod.run(["https://e.com/sitemap.xml", "--concurrency", "auto", "--recheck-failed", "--recheck-from", str(rp), "--no-report-ui"])
    assert rc == 0


def test_run_runtime_error_path(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(mod, "parse_sitemap_urls", lambda *_a, **_k: ["https://e.com/a"])
    monkeypatch.setattr(mod, "check_robots_txt", lambda *_a, **_k: _site_result())
    monkeypatch.setattr(mod, "check_llms_txt", lambda *_a, **_k: _llms_result())
    monkeypatch.setattr(mod, "get_page_checks", lambda **_k: [SimpleNamespace(name="status", run=lambda **_k: CheckResult(name="status", status="ok"))])

    class _BadBrowser:
        def __init__(self, **_k):
            raise RuntimeError("boom")

    monkeypatch.setattr(mod, "Browser", _BadBrowser)
    assert mod.run(["https://e.com/sitemap.xml", "--concurrency", "1", "--no-report-ui"]) == 1


def test_run_non_auto_concurrency_future_failure(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr(mod, "parse_sitemap_urls", lambda *_a, **_k: ["https://e.com/a", "https://e.com/b"])
    monkeypatch.setattr(mod, "check_robots_txt", lambda *_a, **_k: _site_result())
    monkeypatch.setattr(mod, "check_llms_txt", lambda *_a, **_k: _llms_result())
    monkeypatch.setattr(mod, "get_page_checks", lambda **_k: [SimpleNamespace(name="status")])

    def _proc(url: str, **_k):
        if url.endswith("/b"):
            raise RuntimeError("boom")
        return (
            {"type": "page", "url": url, "checks": [{"name": "status", "status": "ok"}]},
            {"url": url, "status": "ok", "file": None, "html": None, "checks": [{"name": "status", "status": "ok"}]},
            False,
        )

    monkeypatch.setattr(mod, "_process_url", _proc)
    assert mod.run(["https://e.com/sitemap.xml", "--concurrency", "2", "--output-dir", str(tmp_path), "--no-report-ui"]) == 0


def test_run_auto_concurrency_full_loop(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr(
        mod,
        "parse_sitemap_urls",
        lambda *_a, **_k: ["https://e.com/1", "https://e.com/2", "https://e.com/3", "https://e.com/4"],
    )
    monkeypatch.setattr(mod, "check_robots_txt", lambda *_a, **_k: _site_result())
    monkeypatch.setattr(mod, "check_llms_txt", lambda *_a, **_k: _llms_result())
    monkeypatch.setattr(mod, "get_page_checks", lambda **_k: [SimpleNamespace(name="status")])

    seen = {"n": 0}

    def _proc(url: str, **_k):
        seen["n"] += 1
        # First call no error (current grows), later calls with error (current shrinks path).
        had_error = seen["n"] >= 2
        return (
            {"type": "page", "url": url, "checks": [{"name": "status", "status": "warn" if had_error else "ok"}]},
            {"url": url, "status": "warn" if had_error else "ok", "file": None, "html": None, "checks": [{"name": "status", "status": "warn" if had_error else "ok"}]},
            had_error,
        )

    monkeypatch.setattr(mod, "_process_url", _proc)
    assert mod.run(["https://e.com/sitemap.xml", "--concurrency", "auto", "--output-dir", str(tmp_path), "--no-report-ui"]) == 0
