#!python

"""
JSON check utility. Run 'jsoncheck --help' to display help.

Author: Vlado Potisk
License: MIT
Project homepage: https://github.com/xitop/jsonstrip
"""

import argparse
import collections
import json
import sys

import jsonstrip

exit_code = 0

def object_pairs(pairs):
    duplicates = [
        key for key, cnt in collections.Counter(p[0] for p in pairs).items()
        if cnt > 1]
    if duplicates:
        raise ValueError(f"Duplicate key(s): {', '.join(duplicates)}")
    return dict(pairs)


def json_check(file):
    global exit_code

    stripped = jsonstrip.strip(file.read())
    try:
        json.loads(stripped, object_pairs_hook=object_pairs)
    except ValueError as err:
        print(f"{file.name}: JSON error: {err}")
        exit_code |= 1   # JSON error
    else:
        print(f"{file.name}: OK")


DESC = """\
Read a commented JSON document, strip the comments, check the resulting JSON.
All JSON objects are checked for duplicate keys. The result of the check
is printed on the standard output, one line for each input file.
"""
EPILOG = """\
exit code:
    0 = all input files are OK
    1 = invalid JSON in some input file(s)
    2 = I/O error reading some input file(s)
    3 = both errors 1 and 2 occurred
    5 = incorrect usage (unrecognized option)
"""

def main():
    global exit_code

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=DESC,
        epilog=EPILOG,
    )
    parser.add_argument(
        'filenames', metavar='file', nargs='*',
        help="Input JSON file to check. "
            + "When no filenames are given, input is taken from stdin.")
    try:
        args = parser.parse_args()
    except SystemExit as exit_exc:
        exit_exc.code = 5   # exit code 2 -> 5 for backward compatibility
        raise

    if not args.filenames:
        json_check(sys.stdin)
        return
    for name in args.filenames:
        try:
            with open(name, encoding='utf-8') as file:
                json_check(file)
        except OSError as err:
            exit_code |= 2   # I/O error
            print(err)


if __name__ == '__main__':
    main()
    sys.exit(exit_code)
