"""americanize.py

Converts British spellings to American across the exercise sources.

Directive 3.9, amended 2026-09-16, requires American spelling in prose, code
comments, map labels and popup text. Matching runs whole-word and preserves
case, so identifiers get renamed consistently within each file and words such
as "parameter", "analysis" and "analyses" are left alone.

    python americanize.py            dry run, report only
    python americanize.py --apply    rewrite the files
"""
from __future__ import annotations

import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent

PAIRS = {
    "colour": "color", "colours": "colors", "coloured": "colored",
    "colouring": "coloring",
    "neighbour": "neighbor", "neighbours": "neighbors",
    "neighbourhood": "neighborhood", "neighbourhoods": "neighborhoods",
    "neighbouring": "neighboring",
    "metre": "meter", "metres": "meters",
    "kilometre": "kilometer", "kilometres": "kilometers",
    "programme": "program", "programmes": "programs",
    "behaviour": "behavior", "behaviours": "behaviors",
    "centre": "center", "centres": "centers", "centred": "centered",
    "modelling": "modeling", "modelled": "modeled",
    "labelled": "labeled", "labelling": "labeling",
    "travelling": "traveling", "travelled": "traveled",
    "favour": "favor", "favours": "favors", "favourable": "favorable",
    "favoured": "favored",
    "recognise": "recognize", "recognised": "recognized",
    "recognisable": "recognizable",
    "summarise": "summarize", "summarised": "summarized",
    "summarises": "summarizes",
    "standardise": "standardize", "standardised": "standardized",
    "standardises": "standardizes", "standardisation": "standardization",
    "standardising": "standardizing",
    "initialise": "initialize", "initialised": "initialized",
    "optimise": "optimize", "optimised": "optimized",
    "optimising": "optimizing",
    "visualise": "visualize", "visualised": "visualized",
    "organise": "organize", "organised": "organized",
    "organisation": "organization", "organisations": "organizations",
    "characterise": "characterize", "characterised": "characterized",
    "generalise": "generalize", "generalised": "generalized",
    "generalising": "generalizing",
    "emphasise": "emphasize", "emphasised": "emphasized",
    "minimise": "minimize", "maximise": "maximize",
    "realise": "realize", "realised": "realized",
    "authorised": "authorized", "authorise": "authorize",
    "analyse": "analyze", "analysed": "analyzed", "analysing": "analyzing",
    "catalogue": "catalog", "catalogues": "catalogs", "catalogued": "cataloged",
    "sceptical": "skeptical", "sceptic": "skeptic", "licence": "license",
    "defence": "defense", "labour": "labor",
    "grey": "gray", "whilst": "while", "vapour": "vapor",
    "prioritise": "prioritize", "utilise": "utilize",
}

TARGETS = (sorted((ROOT / "toolkit").glob("*.py"))
           + sorted((ROOT / "site").glob("*.py"))
           + sorted(ROOT.glob("GeoAIRisks_Exercises_*.md"))
           + sorted(ROOT.glob("README_Exercise_Package_*.md"))
           + sorted((ROOT / "site").glob("Deployment_Guide_*.md")))

# Letter boundaries, because \b treats the underscore as a word character and
# would miss snake_case names such as colour_ramp in the handoff files.
PAT = re.compile(r"(?<![A-Za-z])("
                 + "|".join(sorted(PAIRS, key=len, reverse=True))
                 + r")(?![A-Za-z])", re.IGNORECASE)


def swap(m):
    w = m.group(0)
    a = PAIRS[w.lower()]
    if w.isupper():
        return a.upper()
    if w[0].isupper():
        return a[0].upper() + a[1:]
    return a


def main(apply):
    total = 0
    for f in TARGETS:
        if f.name == "americanize.py":
            continue
        t = f.read_text(encoding="utf-8")
        hits = PAT.findall(t)
        if not hits:
            continue
        total += len(hits)
        words = sorted({h.lower() for h in hits})
        print(f"{len(hits):4d}  {f.relative_to(ROOT).as_posix():<52} "
              f"{', '.join(words)[:70]}")
        if apply:
            f.write_text(PAT.sub(swap, t), encoding="utf-8")
    print(f"\n{total} British forms {'converted' if apply else 'found'}")


if __name__ == "__main__":
    main("--apply" in sys.argv)
