"""Read-only symlink audit for a quiet local directory. Python 3.9+."""
import argparse
import json
import os
from pathlib import Path
import stat


def audit(root):
    broken = []
    errors = []
    symlinks = 0

    def record_error(path, exc):
        errors.append({"path": str(path), "errno": exc.errno,
                       "message": str(exc)})

    def walk_error(exc):
        record_error(exc.filename or root, exc)

    for folder, dirs, names in os.walk(root, onerror=walk_error,
                                       followlinks=False):
        dirs.sort()
        for name in sorted(dirs + names):
            path = Path(folder) / name
            try:
                mode = path.lstat().st_mode
                if not stat.S_ISLNK(mode):
                    continue
                symlinks += 1
                target = os.readlink(path)
            except OSError as exc:
                record_error(path, exc)
                continue
            try:
                path.stat()
            except (FileNotFoundError, NotADirectoryError):
                broken.append({"path": str(path.relative_to(root)),
                               "target": target})
            except OSError as exc:
                record_error(path, exc)

    return {"root": str(root), "symlinks": symlinks,
            "broken": sorted(broken, key=lambda item: item["path"]),
            "errors": sorted(errors, key=lambda item: item["path"]),
            "complete": not errors}


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
    parser.add_argument("directory", type=Path, help="directory to inspect")
    args = parser.parse_args(argv)
    try:
        root = args.directory.resolve(strict=True)
        if not stat.S_ISDIR(root.stat().st_mode):
            parser.error("directory must refer to a directory")
    except (OSError, RuntimeError) as exc:
        parser.error(str(exc))

    report = audit(root)
    print(json.dumps(report, indent=2, sort_keys=True))
    if report["errors"]:
        return 2
    return 1 if report["broken"] else 0


if __name__ == "__main__":
    raise SystemExit(main())
