#!/usr/bin/env python3
"""Bulk-resolve the redesign (new) translation values for a list of localization keys.

Reads the per-key authored source under
`<resources>/Resources/Localization/LocalizationStrings/Sources/Suggested/<Key>.json`
(SSOT in the resources repo) and prints `{key: {lang: value|null}}`.

Why a script: the model otherwise hand-copies 8 languages x N keys out of JSON
files  -  slow, token-heavy, and error-prone. This is pure file IO, so it is a
script. Read-only; touches nothing.

Key-form handling: code often references a key as `AddPassenger.continueButton`
(camelCase leaf) while the Suggested filename PascalCases every dotted segment
(`AddPassenger.ContinueButton.json`). We normalise the first character of
each segment to upper-case before resolving, so either form works as input.
Accessibility keys live under `AccessibilityStrings/Sources/Suggested/`  -  pass
`--domain accessibility` for those.

`--catalog <file>`: the SHIPPED string catalog, checked whenever a key has no Suggested
file. Pass it. A missing Suggested file means "not in this snapshot", NOT "this key is
unauthored"  -  the snapshot is a generated mirror and goes stale the moment a key is added
upstream. Concluding "unauthored" from the snapshot alone has already produced a batch of
pointless authoring requests for keys that were shipping in all eight languages. With
`--catalog`, each key reports where its values came from (`suggested` / `catalog`), and
only keys absent from BOTH are reported as genuinely unauthored.

Accepts an Apple `.xcstrings` (`{"strings": {key: {"localizations": {lang: {"stringUnit":
{"value": ...}}}}}}`) or a flat `{key: {lang: value}}` map.
"""
import argparse
import json
import os
import sys

DOMAIN_DIR = {
    "localization": "LocalizationStrings",
    "accessibility": "AccessibilityStrings",
}


def normalise(key: str) -> str:
    """PascalCase the first char of every dotted segment (file naming convention)."""
    return ".".join(s[:1].upper() + s[1:] if s else s for s in key.split("."))


SNAPSHOT_SUBDIR = {"localization": "Suggested", "accessibility": "AccessibilitySuggested"}


def suggested_dir(resources_root: str, domain: str) -> str:
    # In-repo snapshot layout first (specs/resources/Localization/Suggested)  -  single-repo, offline;
    # else the full the resources repo layout.
    snap = os.path.join(resources_root, "Localization", SNAPSHOT_SUBDIR[domain])
    if os.path.isdir(snap):
        return snap
    return os.path.join(
        resources_root, "Resources", "Localization",
        DOMAIN_DIR[domain], "Sources", "Suggested",
    )


def resolve(key: str, sug_dir: str, langs):
    path = os.path.join(sug_dir, normalise(key) + ".json")
    if not os.path.isfile(path):
        return None  # absent from THIS snapshot  -  see load_catalog before concluding
    with open(path, encoding="utf-8") as fh:
        data = json.load(fh)
    out = {}
    for lang in langs:
        entry = data.get(lang)
        out[lang] = entry.get("value") if isinstance(entry, dict) else None
    return out


def load_catalog(path):
    """{key: {lang: value}} from an .xcstrings or an already-flat catalog."""
    if not path:
        return {}
    with open(path, encoding="utf-8") as fh:
        raw = json.load(fh)
    strings = raw.get("strings") if isinstance(raw, dict) else None
    if not isinstance(strings, dict):
        return raw if isinstance(raw, dict) else {}
    out = {}
    for key, entry in strings.items():
        locs = (entry or {}).get("localizations") or {}
        out[key] = {lang: ((v or {}).get("stringUnit") or {}).get("value")
                    for lang, v in locs.items()}
    return out


def resolve_from_catalog(key, catalog, langs):
    for cand in (key, normalise(key)):
        vals = catalog.get(cand)
        if vals:
            return {lang: vals.get(lang) for lang in langs}
    return None


def main():
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--keys", help="comma-separated keys (code- or file-form)")
    ap.add_argument("--keys-file", help="file with one key per line (- for stdin)")
    ap.add_argument("--resources-root",
                    help="resources checkout OR the in-repo snapshot root (specs/resources)")
    ap.add_argument("--suggested-dir", help="explicit Suggested dir (overrides --resources-root detection)")
    ap.add_argument("--domain", choices=list(DOMAIN_DIR), default="localization")
    ap.add_argument("--catalog", help="shipped string catalog (.xcstrings or flat JSON) used "
                                     "whenever a key has no Suggested file  -  pass it, or a "
                                     "stale snapshot reads as 'unauthored'")
    ap.add_argument("--report-source", action="store_true",
                    help="emit {key: {source, values}} instead of {key: values}")
    ap.add_argument("--langs", default="en,tr",
                    help="comma-separated langs, or 'all' (ar,de,en,es,fr,it,ru,tr)")
    args = ap.parse_args()

    if args.langs.strip() == "all":
        langs = ["ar", "de", "en", "es", "fr", "it", "ru", "tr"]
    else:
        langs = [l.strip() for l in args.langs.split(",") if l.strip()]

    keys = []
    if args.keys:
        keys += [k.strip() for k in args.keys.split(",") if k.strip()]
    if args.keys_file:
        src = sys.stdin if args.keys_file == "-" else open(args.keys_file, encoding="utf-8")
        keys += [ln.strip() for ln in src if ln.strip()]
    if not keys:
        ap.error("supply --keys and/or --keys-file")

    if args.suggested_dir:
        sug_dir = args.suggested_dir
    elif args.resources_root:
        sug_dir = suggested_dir(args.resources_root, args.domain)
    else:
        ap.error("supply --resources-root or --suggested-dir")
    if not os.path.isdir(sug_dir):
        ap.error(f"Suggested dir not found: {sug_dir} (is --resources-root / --suggested-dir correct?)")

    catalog = load_catalog(args.catalog)
    result, source = {}, {}
    for k in keys:
        vals = resolve(k, sug_dir, langs)
        src = "suggested" if vals is not None else None
        if vals is None and catalog:
            vals = resolve_from_catalog(k, catalog, langs)
            src = "catalog" if vals is not None else None
        result[k] = vals
        source[k] = src

    if args.report_source:
        json.dump({k: {"source": source[k], "values": result[k]} for k in keys},
                  sys.stdout, ensure_ascii=False, indent=2)
    else:
        json.dump(result, sys.stdout, ensure_ascii=False, indent=2)
    sys.stdout.write("\n")

    from_catalog = [k for k in keys if source[k] == "catalog"]
    unauthored = [k for k in keys if source[k] is None]
    if from_catalog:
        sys.stderr.write(
            f"\n# {len(from_catalog)} key(s) resolved from the CATALOG, not the snapshot  -  "
            "the snapshot is stale, refresh it with snapshot-resources.sh: "
            + ", ".join(from_catalog) + "\n")
    if unauthored:
        label = ("with no authored source" if catalog else
                 "absent from the snapshot (no --catalog given, so 'unauthored' is UNVERIFIED)")
        sys.stderr.write(f"\n# {len(unauthored)} key(s) {label}: " + ", ".join(unauthored) + "\n")


if __name__ == "__main__":
    main()
