"""
Converts JSON objects into nix (hackishly).
https://gist.github.com/Scoder12/0538252ed4b82d65e59115075369d34d
"""

import sys
import json


INDENT = " " * 2


def strip_comments(t):
    # fixme: doesn't work if JSON strings contain //
    return "\n".join(l.partition("//")[0] for l in t.split("\n"))


def indent(s):
    return "\n".join(INDENT + i for i in s.split("\n"))


def nix_stringify(s):
    # fixme: this doesn't handle string interpolation and possibly has more bugs
    return json.dumps(s)


def sanitize_key(s):
    if s and s.isalnum() and not s[0].isdigit():
        return s
    return nix_stringify(s)


def flatten_obj_item(k, v):
    keys = [k]
    val = v
    while isinstance(val, dict) and len(val) == 1:
        k = next(iter(val.keys()))
        keys.append(k)
        val = val[k]
    return keys, val


def fmt_object(obj, flatten):
    fields = []
    for k, v in obj.items():
        if flatten:
            keys, val = flatten_obj_item(k, v)
            formatted_key = ".".join(sanitize_key(i) for i in keys)
        else:
            formatted_key = sanitize_key(k)
            val = v
        fields.append(f"{formatted_key} = {fmt_any(val, flatten)};")

    return "{\n" + indent("\n".join(fields)) + "\n}"


def fmt_array(o, flatten):
    body = indent("\n".join(fmt_any(i, flatten) for i in o))
    return f"[\n{body}\n]"


def fmt_any(o, flatten):
    if isinstance(o, str) or isinstance(o, bool) or isinstance(o, int):
        return json.dumps(o)
    if isinstance(o, list):
        return fmt_array(o, flatten)
    if isinstance(o, dict):
        return fmt_object(o, flatten)
    raise TypeError(f"Unknown type {type(o)!r}")


def main():
    flatten = "--flatten" in sys.argv
    args = [a for a in sys.argv[1:] if not a.startswith("--")]

    if len(args) < 1:
        print(f"Usage: {sys.argv[0]} [--flatten] <file.json>", file=sys.stderr)
        sys.exit(1)

    with open(args[0], "r") as f:
        data = json.loads(strip_comments(f.read()))

    print(fmt_any(data, flatten=flatten))


if __name__ == "__main__":
    main()