"""No em dash in anything a shopper or the client reads; American spelling in every identifier.

The brand style (agents.md, OVERNIGHT-RULES.md) had reached the tests and the client pages
but not the storefront: on 10 Sep fifteen remained in shopper-facing strings (the shared
title, the savings page, the airport picker's maximum notice, the search placeholder) and
one in a served product title the night before. This walks the SPA sources and the served
strings so the next one fails here. Comments may still use one; only string content is read.

Stream L adds the Python files it created: no em dash in their strings, and `colour` and
`flavour` in no identifier, column name, key token or alias row there or in `ATTRIBUTE_KINDS`
(rian, 14 Sep: "shade" is a shop word for the kind `color`, American spelling in code).
"""

import pathlib
import re

from app.services.product_lines import ATTRIBUTE_KINDS
from app.services.seo import DEFAULT_TITLE

MAIN = pathlib.Path(__file__).resolve().parents[1]
WEB = MAIN / "web"
COMMENT = re.compile(r"/\*.*?\*/|^\s*//.*$|^\s*\*.*$", re.S | re.M)
PY_COMMENT = re.compile(r'^\s*#.*$|"""(?:.|\n)*?"""', re.M)
# The Python files this stream created; a new one is added here when it lands.
STREAM_L_FILES = ("quantity.py", "overrides.py", "collected.py", "listings_table.py", "merge_desk.py",
                  "product_lines.py", "keying.py", "taxonomy.py", "merges.py")
BRITISH = re.compile(r"colour|flavour|behaviour", re.I)


def sources():
    for path in sorted((WEB / "src").rglob("*")):
        # The vendored packs (src/vendor) are byte-identical to their sources and never edited;
        # a pack string that could reach the client is overridden through a prop where it
        # carries an em dash (the thread starter's hint), so DFP's own files are the test.
        if "vendor" in path.relative_to(WEB / "src").parts:
            continue
        if path.suffix in (".ts", ".tsx") and path.name != "schema.ts":
            yield path
    yield WEB / "index.html"


def python_sources():
    for name in STREAM_L_FILES:
        path = MAIN / "app" / "services" / name
        if path.exists():
            yield path


def test_no_em_dash_outside_comments():
    offenders = []
    for path in sources():
        text = COMMENT.sub("", path.read_text())
        for number, line in enumerate(text.splitlines(), 1):
            if "—" in line:
                offenders.append(f"{path.relative_to(WEB)}:{number}: {line.strip()[:80]}")
    for path in python_sources():
        text = PY_COMMENT.sub("", path.read_text())
        for number, line in enumerate(text.splitlines(), 1):
            if "—" in line:
                offenders.append(f"{path.relative_to(MAIN)}:{number}: {line.strip()[:80]}")
    assert not offenders, "\n".join(offenders)


def test_served_titles_carry_none():
    assert "—" not in DEFAULT_TITLE


def test_american_spelling_in_every_identifier_the_stream_created():
    """`colour` and `flavour` may appear only inside a quoted string or a comment (a shop's own
    wording, "Colour Riche", is data); never in code."""
    offenders = []
    for path in python_sources():
        text = PY_COMMENT.sub("", path.read_text())
        text = re.sub(r"'[^'\n]*'|\"[^\"\n]*\"", "''", text)
        for number, line in enumerate(text.splitlines(), 1):
            if BRITISH.search(line):
                offenders.append(f"{path.relative_to(MAIN)}:{number}: {line.strip()[:80]}")
    assert not offenders, "\n".join(offenders)
    assert not any(BRITISH.search(kind) for kind in ATTRIBUTE_KINDS)
    assert "color" in ATTRIBUTE_KINDS and "flavor" in ATTRIBUTE_KINDS


# --- the Professor's own copy (Stream AW5.2) -----------------------------------------------

SAMPLES = MAIN.parent / "import" / "articles" / "samples"
FENCE = re.compile(r"^```.*?^```", re.S | re.M)
CODE_SPAN = re.compile(r"`[^`]*`")
#: "duty free" and "tax free" are the trade's own words and are not the word being banned.
COMPOUND_FREE = re.compile(r"(?i)\b(?:duty|tax)[ -]free\b")
#: The vocabulary's compounds; only a bare "product" is wrong (agents.md, VOCABULARY.md).
COMPOUND_PRODUCT = re.compile(r"(?i)\bproduct (?:lines?|variants?)\b")
BANNED = (
    ("—", re.compile("—"), "an em dash"),
    ("cheap", re.compile(r"(?i)\bcheap(?:er|est)?\b"), '"cheap"'),
    ("free", re.compile(r"(?i)\bfree\b"), 'a bare "free"'),
    ("product", re.compile(r"(?i)\bproducts?\b"), 'a bare "product"'),
)


def test_the_sample_articles_are_written_in_the_house_words():
    """The ten pieces and the README that imports them are client-facing copy: no em dash,
    never "cheap" or a bare "free", and never a bare "product" where the vocabulary says
    product line or product variant. Code spans and fenced blocks are commands, not prose."""
    assert SAMPLES.is_dir(), f"the samples folder is missing: {SAMPLES}"
    offenders = []
    for path in sorted(SAMPLES.glob("*.md")):
        # A fence is blanked line for line, so the number in the message is the real one.
        text = FENCE.sub(lambda m: "\n" * m.group().count("\n"), path.read_text())
        text = CODE_SPAN.sub(" ", text)
        text = COMPOUND_PRODUCT.sub(" ", COMPOUND_FREE.sub(" ", 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)
