"""Compare small UTF-8 config snapshots without modifying them. Python 3.9+."""
import argparse
import difflib
import io
from pathlib import Path
import sys

MAX_BYTES = 256 * 1024
MAX_LINES = 2000


def context_lines(value):
    number = int(value)
    if not 0 <= number <= 20:
        raise argparse.ArgumentTypeError("context must be between 0 and 20")
    return number


def read_lines(path):
    if not path.is_file():
        raise ValueError(f"not a regular file: {path}")
    with path.open("rb") as stream:
        raw = stream.read(MAX_BYTES + 1)
    if len(raw) > MAX_BYTES:
        raise ValueError(f"file exceeds {MAX_BYTES} bytes: {path}")
    text = raw.decode("utf-8", errors="strict")
    if text.startswith("\ufeff"):
        raise ValueError(f"UTF-8 BOM found: {path}")
    if "\x00" in text:
        raise ValueError(f"NUL byte found: {path}")
    text = text.replace("\r\n", "\n").replace("\r", "\n")
    lines = io.StringIO(text).readlines()
    if len(lines) > MAX_LINES:
        raise ValueError(f"file exceeds {MAX_LINES} lines: {path}")
    return lines


def render_diff(before, after, context):
    parts = []
    for line in difflib.unified_diff(
        before, after, fromfile="before", tofile="after", n=context,
    ):
        parts.append(line)
        if not line.endswith("\n"):
            parts.append("\n\\ No newline at end of file\n")
    return "".join(parts)


def main():
    parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
    parser.add_argument("before", type=Path)
    parser.add_argument("after", type=Path)
    parser.add_argument("--context", type=context_lines, default=3)
    args = parser.parse_args()
    try:
        before = read_lines(args.before)
        after = read_lines(args.after)
        if before == after:
            return 0
        report = render_diff(before, after, args.context)
    except (OSError, ValueError) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 2
    sys.stdout.write(report)
    return 1


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