#!/usr/bin/env python3
"""Refresh the legacy label snapshot from the label endpoint.

Writes one flat <out>/<lang>.json per language with keys exactly as the
service returns them (backend prefix kept). This is the only step of the
skill that needs the network.
"""
import argparse
import json
import os
import sys
import urllib.request

from _shared import parse_langs


def snapshot_langs(snapshot_dir):
    if not os.path.isdir(snapshot_dir):
        return []
    return [name[:-5] for name in os.listdir(snapshot_dir) if name.endswith(".json")]


def parse_pairs(pairs, flag):
    result = {}
    for pair in pairs or []:
        if "=" not in pair:
            raise SystemExit(f"{flag} expects K=V, got {pair!r}")
        key, value = pair.split("=", 1)
        result[key.strip()] = value.strip()
    return result


def build_headers(args):
    headers = {"Accept": "application/json"}
    if args.headers_file:
        with open(args.headers_file, encoding="utf-8") as stream:
            extra = json.load(stream)
        if not isinstance(extra, dict):
            raise SystemExit(f"--headers-file must hold a JSON object: {args.headers_file}")
        headers.update({str(k): str(v) for k, v in extra.items()})
    headers.update(parse_pairs(args.header, "--header"))
    return headers


def largest_string_map(data):
    best = {}
    stack = [data]
    while stack:
        node = stack.pop()
        if isinstance(node, dict):
            if node and all(isinstance(k, str) and isinstance(v, str) for k, v in node.items()):
                if len(node) > len(best):
                    best = node
            stack.extend(node.values())
        elif isinstance(node, list):
            stack.extend(node)
    return best


def fetch_labels(endpoint, lang, env, headers, status_field, fail_status, timeout):
    url = endpoint.format(lang=lang, env=env)
    request_headers = dict(headers)
    request_headers.setdefault("Accept-Language", lang)
    request = urllib.request.Request(url, headers=request_headers)
    with urllib.request.urlopen(request, timeout=timeout) as response:
        data = json.loads(response.read().decode("utf-8"))
    if isinstance(data, dict) and status_field in data:
        code = str(data[status_field])
        if code in fail_status:
            raise RuntimeError(f"{url}: {fail_status[code]} (status {code})")
    labels = largest_string_map(data)
    if not labels:
        raise RuntimeError(f"the response for '{lang}' ({url}) contains no string-to-string label map")
    return labels


def main():
    cli = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    cli.add_argument("--out", dest="snapshot_dir", required=True, help="where the snapshot lives, for example resources/Localization/Legacy")
    cli.add_argument("--endpoint", required=True, help="URL template containing {lang} and, if needed, {env}")
    cli.add_argument("--header", action="append", help="additional request header as K=V; may be repeated")
    cli.add_argument("--headers-file", help="request headers as a JSON object, so secrets stay out of argv")
    cli.add_argument("--langs", default="all", help="languages separated by commas, or 'all' for every language already in the snapshot (en when it is empty)")
    cli.add_argument("--env", default="", help="text put in place of {env}")
    cli.add_argument("--status-field", default="statusCode", help="name of the response field that holds a status code")
    cli.add_argument("--fail-status", action="append", help="CODE=message pair that marks a failed response; may be repeated")
    cli.add_argument("--timeout", default=40, type=int)
    opts = cli.parse_args()

    if "{lang}" not in opts.endpoint:
        print("# warning: --endpoint lacks a {lang} placeholder, so all languages request the same URL", file=sys.stderr)

    headers = build_headers(opts)
    fail_status = parse_pairs(opts.fail_status, "--fail-status")
    langs = parse_langs(opts.langs, snapshot_langs(opts.snapshot_dir))
    os.makedirs(opts.snapshot_dir, exist_ok=True)

    total = 0
    for lang in langs:
        labels = fetch_labels(opts.endpoint, lang, opts.env, headers, opts.status_field, fail_status, opts.timeout)
        target = os.path.join(opts.snapshot_dir, lang + ".json")
        with open(target, "w", encoding="utf-8") as sink:
            json.dump(labels, sink, ensure_ascii=False, sort_keys=True, indent=0)
            sink.write("\n")
        total += len(labels)
        print(f"  {lang}: {len(labels)} labels -> {target}")

    average = total // len(langs) if langs else 0
    print(f"snapshot refreshed: {len(langs)} languages, about {average} keys each")
    print(f"commit {opts.snapshot_dir} so later lookups stay offline")


if __name__ == "__main__":
    main()
