"""Find namespace references whose case does not match the real path on disk.

Yii's autoloader maps a namespace straight onto a filesystem path, so on Linux any case
difference between a referenced namespace segment and the actual directory or file is a
fatal class-not-found. On Windows/macOS it silently works, which is why these survive
review and only surface in the container.

Run from the repo root:  python ns_case_check.py
Exits 1 if any mismatch is found, so it can gate CI.
"""
import re
import subprocess
import sys

BS = chr(92)  # backslash, kept out of literals to avoid escaping surprises

tracked = subprocess.run(["git", "ls-files"], capture_output=True, text=True).stdout.splitlines()
php = [f for f in tracked if f.endswith(".php") and "/vendor/" not in f and "node_modules" not in f]

# Map lowercased path -> actual cased path, for BOTH directories and files-minus-.php.
# Including the file stem is essential: a namespace reference ends in the class name, which
# is a file, not a directory. Mapping directories only makes every real hit a false negative.
real = {}


def note(path):
    real.setdefault(path.lower(), set()).add(path)


for f in tracked:
    parts = f.split("/")
    for i in range(1, len(parts)):
        note("/".join(parts[:i]))
    if f.endswith(".php"):
        note(f[: -len(".php")])

roots = "api|common|frontend|backend|storage|modules|components|models|controllers"
# No leading "optional backslash" term here on purpose: writing it as BS + "?" yields the
# regex \? which matches a LITERAL question mark, so the pattern silently never fires.
# \b already anchors correctly at the "a" in "\api\Common", leading backslash or not.
pat = re.compile(
    r"\b((?:" + roots + r")(?:" + BS + BS + r"+[A-Za-z_][A-Za-z0-9_]*)+)"
)

hits = {}
for f in php:
    pack = f.split("/")[0]
    try:
        src = open(f, encoding="utf-8", errors="replace").read()
    except OSError:
        continue
    for ns in set(pat.findall(src)):
        # Plain split/join rather than re.sub: BS + "+" builds the regex \+ , a literal plus
        # sign, so a substitution written that way silently leaves the backslashes in place.
        # Dropping empty segments also collapses the doubled backslashes of PHP string
        # literals ("api\\common\\X") without a second pass.
        rel = "/".join(p for p in ns.split(BS) if p)
        cand = pack + "/" + rel
        actual = real.get(cand.lower())
        if actual and cand not in actual:
            hits[(f, ns)] = sorted(actual)[0]

for (f, ns), act in sorted(hits.items()):
    print("  %s" % f)
    print("      referenced: %s" % ns)
    print("      on disk:    %s" % act.split("/", 1)[1].replace("/", BS))
print("  total mismatches: %d" % len(hits))
sys.exit(1 if hits else 0)
