"""The privacy policy and the terms of use: `docs/legal/<name>.md` read per request, served at
`/privacy` and `/terms` and handed over by `GET /api/legal/{name}`.

The pages were owed since the go-live readiness plan (its W1) and never built; the explainer rule
that plan named (`/{page}.html`) became a development route in the environment line, so a page
dropped there would have answered 404 on live. Pinned here: the reader's shape and its 404s, the
API, the three keys in the storefront class and nowhere in the development sets, the pages'
status in the owner's badge, and the copy written in the house words. No database, no network.
"""

from __future__ import annotations

import pathlib

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient

from app.routers import legal as legal_router
from app.services import access, process_doc, publish, seo

LEGAL_DIR = pathlib.Path(__file__).resolve().parents[1] / "docs" / "legal"


def api() -> TestClient:
    app = FastAPI()
    app.include_router(legal_router.router)
    return TestClient(app)


class TestTheReader:
    @pytest.mark.parametrize("name,title", [("privacy", "Privacy policy"), ("terms", "Terms of use")])
    def test_each_page_arrives_section_by_section_with_its_date(self, name, title):
        page = process_doc.legal_page(name)
        assert page is not None and page["name"] == name and page["title"] == title
        assert page["version_line"] == f"**Version {page['version']}, {page['dated']}**"
        assert page["sections"][0]["anchor"] == "about"
        # The version line is the page's date, never repeated inside the opening paragraph.
        assert "Version" not in page["sections"][0]["html"]
        assert len(page["sections"]) > 5 and all(s["html"] for s in page["sections"])

    def test_an_unknown_name_is_none(self):
        assert process_doc.legal_page("cookies") is None

    def test_an_absent_or_unversioned_doc_is_none(self, tmp_path, monkeypatch):
        monkeypatch.setitem(process_doc._DOCS["PRIVACY"], "candidates", (tmp_path / "nowhere.md",))
        assert process_doc.legal_page("privacy") is None
        bare = tmp_path / "privacy.md"
        bare.write_text("# Privacy policy\n\nNo date.\n\n## Who we are\n\nUs.\n")
        monkeypatch.setitem(process_doc._DOCS["PRIVACY"], "candidates", (bare,))
        assert process_doc.legal_page("privacy") is None


class TestTheApi:
    def test_the_shape(self):
        r = api().get("/api/legal/terms")
        assert r.status_code == 200
        body = r.json()
        assert set(body) == {"name", "title", "version", "dated", "version_line", "sections"}
        assert set(body["sections"][0]) == {"title", "anchor", "html"}
        assert body["name"] == "terms" and body["title"] == "Terms of use"

    def test_an_unknown_name_and_a_missing_doc_are_404_with_a_code(self, tmp_path, monkeypatch):
        r = api().get("/api/legal/cookies")
        assert r.status_code == 404 and r.json()["detail"]["error_code"] == "LEGAL_UNKNOWN"
        monkeypatch.setitem(process_doc._DOCS["TERMS"], "candidates", (tmp_path / "nowhere.md",))
        r = api().get("/api/legal/terms")
        assert r.status_code == 404 and r.json()["detail"]["error_code"] == "DOC_MISSING"


class TestTheRouteClasses:
    def test_the_pages_and_the_read_are_storefront_routes(self):
        for key in ("GET /privacy", "GET /terms", "GET /api/legal/{name}"):
            assert key in access.PUBLIC_WHEN_OPEN
        assert {"GET /privacy", "GET /terms"} <= access.HTML_KEYS
        assert {"/privacy", "/terms"} <= access.SPA_PUBLIC_WHEN_OPEN

    def test_they_stay_on_the_live_site(self):
        """A legal page is the live site's; the environment line must never hide it."""
        for key in ("GET /privacy", "GET /terms", "GET /api/legal/{name}"):
            assert key not in access.DEVELOPMENT_ROUTES and not access.is_development(key, None)
        for path in ("/privacy", "/terms"):
            assert path not in access.DEVELOPMENT_SPA
            assert not access.is_development(access.CATCH_ALL, path)

    def test_the_addresses_match_the_reader(self):
        assert set(seo.LEGAL_PATHS) == set(process_doc.LEGAL)
        assert all(seo.is_known_route(path) for path in seo.LEGAL_PATHS.values())


class TestTheBadge:
    @pytest.mark.parametrize("path", ["/privacy", "/terms"])
    def test_noindex_until_a_person_names_the_address(self, path):
        status = publish.status_of_path(None, path)
        assert status.status == "noindex" and status.in_sitemap is False and status.canonical == path


# --- the copy is the client's, written in the house words ------------------------------------

COMPOUND_FREE = r"(?i)\b(?:duty|tax)[ -]free\b"
COMPOUND_PRODUCT = r"(?i)\bproduct (?:lines?|variants?)\b"


def test_the_legal_copy_is_written_in_the_house_words():
    """Client-facing copy: no em dash, never "cheap" or a bare "free", never a bare "product"
    where the vocabulary says product line or product variant (agents.md, VOCABULARY.md)."""
    import re

    banned = (
        (re.compile("—"), "an em dash"),
        (re.compile(r"(?i)\bcheap(?:er|est)?\b"), '"cheap"'),
        (re.compile(r"(?i)\bfree\b"), 'a bare "free"'),
        (re.compile(r"(?i)\bproducts?\b"), 'a bare "product"'),
    )
    files = sorted(LEGAL_DIR.glob("*.md"))
    assert [p.name for p in files] == ["privacy.md", "terms.md"]
    offenders = []
    for path in files:
        text = re.sub(COMPOUND_PRODUCT, " ", re.sub(COMPOUND_FREE, " ", path.read_text()))
        for number, line in enumerate(text.splitlines(), 1):
            for pattern, said in banned:
                if pattern.search(line):
                    offenders.append(f"{path.name}:{number}: {said}: {line.strip()[:70]}")
    assert not offenders, "\n".join(offenders)
