"""The server-rendered SEO layer.

ProductVariant names come from retailer feeds, which makes them untrusted input into
our own HTML head -- the escaping cases here are load-bearing, not pedantry.
"""

import json
import re

from app.services.seo import (
    DEFAULT_TITLE,
    Head,
    parse_product_slug,
    product_path,
    public_base,
    slugify,
)

SHELL = (
    "<!doctype html><html><head>"
    f"<title>{DEFAULT_TITLE}</title>"
    '<meta name="description" content="old description" />'
    "</head><body></body></html>"
)


class TestSlugs:
    def test_accents_flatten(self):
        assert slugify("Rémy Martin XO 1L") == "remy-martin-xo-1l"

    def test_roundtrip(self):
        path = product_path(60, "Johnnie Walker Blue Label 1L")
        assert path == "/products/johnnie-walker-blue-label-1l-60"
        assert parse_product_slug(path.rsplit("/", 1)[-1]) == 60

    def test_bare_numeric_still_resolves(self):
        """Old /products/60 links keep working (and then 301 to the slug)."""
        assert parse_product_slug("60") == 60

    def test_no_id_means_no_product(self):
        assert parse_product_slug("just-words") is None

    def test_empty_name_still_produces_a_slug(self):
        assert product_path(7, "???") == "/products/product-7"


class TestHeadInjection:
    def test_title_and_description_are_replaced_not_duplicated(self):
        page = Head("My Title", "My description", canonical_path="/x").apply(
            SHELL, "https://example.com"
        )
        assert "<title>My Title</title>" in page
        assert page.count("<title>") == 1
        assert 'content="My description"' in page
        assert "old description" not in page

    def test_canonical_and_og_are_absolute(self):
        page = Head("T", "D", canonical_path="/products/x-1").apply(SHELL, "https://example.com")
        assert '<link rel="canonical" href="https://example.com/products/x-1" />' in page
        assert '<meta property="og:url" content="https://example.com/products/x-1" />' in page

    def test_noindex_pages_say_so(self):
        page = Head("T", "D", noindex=True).apply(SHELL, "https://example.com")
        assert '<meta name="robots" content="noindex, nofollow" />' in page

    def test_html_in_a_product_name_is_escaped(self):
        """A feed name like 'Gin <b>"Special"</b>' must not become markup."""
        page = Head('Gin <b>"Special"</b>', 'A & B').apply(SHELL, "https://example.com")
        assert "<b>" not in page.split("<title>")[1].split("</title>")[0]
        assert "&lt;b&gt;" in page
        assert "A &amp; B" in page

    def test_jsonld_cannot_break_out_of_its_script_tag(self):
        """The classic: a value containing </script> must stay inert."""
        page = Head("T", "D", jsonld=[{"name": 'x</script><script>alert(1)'}]).apply(
            SHELL, "https://example.com"
        )
        assert "</script><script>alert(1)" not in page
        assert "\\u003c/script" in page

    def test_jsonld_is_valid_json(self):
        payload = {"@type": "Product", "name": "Rémy Martin XO"}
        page = Head("T", "D", jsonld=[payload]).apply(SHELL, "https://example.com")
        blob = re.search(r'<script type="application/ld\+json">(.*?)</script>', page).group(1)
        assert json.loads(blob)["name"] == "Rémy Martin XO"


class TestPublicBase:
    def test_public_hosts_are_https(self):
        assert public_base("dutyfreeprofessor.demoing.info") == "https://dutyfreeprofessor.demoing.info"

    def test_internal_hosts_are_http(self):
        assert public_base("172.17.0.1:3149") == "http://172.17.0.1:3149"

    def test_garbage_hosts_yield_nothing(self):
        """A hostile Host header must not be reflected into canonicals."""
        assert public_base('evil"><script>') == ""
        assert public_base(None) == ""


class TestSiteBase:
    """PUBLIC_BASE_URL decides the origin; the Host header only fills in outside production."""

    def test_configured_wins_everywhere(self):
        from app.services.seo import site_base
        assert site_base("evil.example", "https://dfp.example/", True) == "https://dfp.example"
        assert site_base("evil.example", "https://dfp.example", False) == "https://dfp.example"

    def test_production_without_config_emits_relative(self):
        from app.services.seo import site_base
        assert site_base("dutyfreeprofessor.demoing.info", "", True) == ""

    def test_dev_falls_back_to_host(self):
        from app.services.seo import site_base
        assert site_base("127.0.0.1:8766", "", False) == "http://127.0.0.1:8766"


class TestJsonLdCorrections:
    def test_absolutise_touches_only_site_relative_url_keys(self):
        from app.services.seo import absolutise
        payload = {
            "@id": "/#org", "url": "/", "logo": "/logo.png", "name": "/not-a-url-key",
            "image": "https://img.example/x.jpg",
            "itemListElement": [{"item": "/products"}, {"item": None}],
            "publisher": {"@id": "/#org"},
        }
        out = absolutise(payload, "https://s.example")
        assert out["@id"] == "https://s.example/#org"
        assert out["url"] == "https://s.example/"
        assert out["logo"] == "https://s.example/logo.png"
        assert out["name"] == "/not-a-url-key"
        assert out["image"] == "https://img.example/x.jpg"
        assert out["itemListElement"][0]["item"] == "https://s.example/products"
        assert out["publisher"]["@id"] == "https://s.example/#org"

    def test_empty_base_leaves_paths_relative(self):
        from app.services.seo import absolutise
        assert absolutise({"url": "/x"}, "")["url"] == "/x"

    def test_gtin_property_by_width(self):
        """A 12-digit UPC published as gtin13 fails Google's validator; the
        generic `gtin` is ignored by it. Real barcodes from the feeds come in
        all four widths."""
        from app.services.seo import gtin_property
        assert gtin_property("7591156404949") == "gtin13"
        assert gtin_property("012345678905") == "gtin12"
        assert gtin_property("96385074") == "gtin8"
        assert gtin_property("00012345678905") == "gtin14"
        assert gtin_property("1234567") is None
        assert gtin_property("ABC1234567890") is None
        assert gtin_property(None) is None

    def test_availability_is_omitted_when_nothing_is_known(self):
        """29% of observations carry no stock flag. Defaulting them to InStock
        asserted a fact nobody observed."""
        from app.services.seo import availability_of
        assert availability_of([None, None]) is None
        assert availability_of([]) is None
        assert availability_of([None, True]) == "https://schema.org/InStock"
        assert availability_of([False, None]) == "https://schema.org/OutOfStock"
        assert availability_of([False, True]) == "https://schema.org/InStock"

    def test_home_head_has_organization_and_website_without_searchaction(self):
        from app.services.seo import STATIC_HEADS
        types = [p["@type"] for p in STATIC_HEADS["/"].jsonld]
        assert types == ["Organization", "WebSite"]
        assert "potentialAction" not in STATIC_HEADS["/"].jsonld[1]
        assert STATIC_HEADS["/"].jsonld[1]["publisher"] == {"@id": "/#organization"}


class TestRouteInventory:
    def test_python_route_list_matches_app_tsx(self):
        """A route added to App.tsx but not here would 404 on the server while
        the SPA happily renders it; a route removed there but kept here would
        200 with the shell. Read the SPA's own list and compare."""
        import pathlib
        import re

        from app.services.seo import STATIC_HEADS, is_known_route

        src = (pathlib.Path(__file__).resolve().parents[1] / "web" / "src" / "App.tsx").read_text()
        spa_paths = set(re.findall(r'<Route\s+path="([^"]+)"', src)) | set(
            re.findall(r'path="([^"]+)"\s*\n', src)
        )
        spa_paths = {p for p in spa_paths if p != "*"}
        assert spa_paths, "App.tsx route list not found"
        for path in spa_paths:
            probe = re.sub(r":\w+", "example", path)
            # ProductVariant, airport, brand and article pages have routes of their own, before the shell.
            assert is_known_route(probe) or probe.startswith(("/products/", "/airports/", "/brands/", "/articles/")), path
        for path in STATIC_HEADS:
            assert path in spa_paths, f"{path} has a head but App.tsx has no route"
        assert not is_known_route("/nope")
        assert not is_known_route("/feature/")
        assert not is_known_route("/products/x")  # served by the product route, not the shell
