#!/usr/bin/env python3
"""Check every App Store copy block in this directory against its field limit.

App Store Connect silently truncates or hard-rejects over-long fields, and the German copy is the
easy one to get wrong because umlauts read as one character but two bytes. Apple counts characters,
so `len()` on a `str` is the right measure — do not switch this to a byte count.

Each fenced code block in the .md files here is one field. Which limit applies is inferred from the
nearest heading above it. Exit status is non-zero if anything is over, so CI can gate on it.
"""

from __future__ import annotations

import pathlib
import re
import sys

LIMITS = {"PROMO": 170, "DESC": 4000, "KW": 100, "NOTES": 4000}


def blocks(text: str):
    """Yield (heading, body) for every fenced block, tagged with the heading above it."""
    heading = None
    buf: list[str] | None = None
    for line in text.split("\n"):
        if line.startswith("#") and buf is None:
            heading = line.lstrip("#").strip()
        if line.strip() == "```":
            if buf is None:
                buf = []
            else:
                yield heading or "", "\n".join(buf)
                buf = None
            continue
        if buf is not None:
            buf.append(line)


def kind_of(heading: str, body: str) -> str:
    low = heading.lower()
    if "keyword" in low or re.fullmatch(r"(de|en) \(\d+\)", low):
        return "KW"
    if "template" in low:
        return "NOTES"
    return "DESC" if len(body) > 400 else "PROMO"


def main() -> int:
    here = pathlib.Path(__file__).parent
    failures = 0
    stale = 0
    for path in sorted(here.glob("*.md")):
        found = list(blocks(path.read_text(encoding="utf-8")))
        if not found:
            continue
        print(f"\n=== {path.name} ===")
        for heading, body in found:
            kind = kind_of(heading, body)
            limit = LIMITS[kind]
            n = len(body)
            over = n > limit
            failures += over
            # Headings carry the count in parentheses; flag any that drifted from the real length.
            claimed = re.search(r"\((\d+)\)\s*$", heading)
            drift = ""
            if claimed and int(claimed.group(1)) != n:
                drift = f"   [heading claims {claimed.group(1)}]"
                stale += 1
            status = "OVER" if over else "ok"
            print(f"  [{kind:5}] {status:>4} {n:>4}/{limit}  {heading[:48]}{drift}")

    if failures:
        print(f"\n{failures} block(s) OVER the limit")
    elif stale:
        print(f"\nAll within limits, but {stale} heading count(s) are stale")
    else:
        print("\nAll blocks within limits, all heading counts accurate")
    return 1 if failures or stale else 0


if __name__ == "__main__":
    sys.exit(main())
