#!/usr/bin/env python3
# Copyright: 2026, Maximiliano Curia <maxy@debian.org>
#
# License: ISC
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#  .
#  THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
#  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
#  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
#  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
#  OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
#  PERFORMANCE OF THIS SOFTWARE.

"""Capture or check decopy's full output for a corpus (golden-output workflow).

The committed regression net for the in-repo fixtures lives in
tests/golden_output_test.py. This script is its counterpart for large external
trees (e.g. the ruff checkout) that are too big to commit: capture a golden
once, then `check` after each optimisation to prove the output is unchanged.

    # before optimising:
    python3 scripts/golden_output.py capture ~/debian/python/ruff /tmp/ruff.golden
    # after a change:
    python3 scripts/golden_output.py check   ~/debian/python/ruff /tmp/ruff.golden

Extra decopy options (after `--`) are passed through and recorded, so the same
flags are used for capture and check:

    python3 scripts/golden_output.py capture SRC OUT -- --group-by copyright
"""

from __future__ import annotations

import argparse
import difflib
import subprocess
import sys
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))

from decopy import cmdoptions  # noqa: E402
from decopy.decopy import run_pipeline  # noqa: E402

_BASE_OPTIONS = ["--no-progress"]


def _git_rev(path: Path) -> str:
    try:
        out = subprocess.run(  # noqa: S603
            ["git", "-C", str(path), "rev-parse", "--short", "HEAD"],  # noqa: S607
            check=True,
            capture_output=True,
            text=True,
        )
        return out.stdout.strip()
    except (subprocess.CalledProcessError, OSError):
        return "unknown"


def render(corpus: Path, extra: list[str], out_path: Path) -> str:
    args = [*_BASE_OPTIONS, *extra, "--root", str(corpus), "-o", str(out_path)]
    run_pipeline(cmdoptions.process_options(args))
    return out_path.read_text(encoding="utf-8")


def capture(corpus: Path, golden: Path, extra: list[str]) -> int:
    text = render(corpus, extra, golden)
    print(
        f"Captured {len(text)} bytes to {golden}\n"
        f"  corpus: {corpus} (rev {_git_rev(corpus)})\n"
        f"  decopy: rev {_git_rev(ROOT)}  options: {extra or '(defaults)'}",
        file=sys.stderr,
    )
    return 0


def check(corpus: Path, golden: Path, extra: list[str]) -> int:
    if not golden.exists():
        print(f"No golden at {golden}; run `capture` first.", file=sys.stderr)
        return 2
    with tempfile.TemporaryDirectory() as tmp:
        got = render(corpus, extra, Path(tmp) / "copyright")
    want = golden.read_text(encoding="utf-8")
    if got == want:
        print(f"OK: output matches {golden}", file=sys.stderr)
        return 0
    diff = difflib.unified_diff(
        want.splitlines(keepends=True),
        got.splitlines(keepends=True),
        fromfile=str(golden),
        tofile="current",
    )
    sys.stdout.writelines(diff)
    print(f"\nMISMATCH: output drifted from {golden}", file=sys.stderr)
    return 1


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("mode", choices=("capture", "check"))
    parser.add_argument("corpus", help="source tree to render")
    parser.add_argument("golden", help="golden file to write (capture) or compare")
    parser.add_argument(
        "extra",
        nargs="*",
        help="extra decopy options after `--`, e.g. -- --group-by copyright",
    )
    args = parser.parse_args()

    corpus = Path(args.corpus)
    golden = Path(args.golden)
    if args.mode == "capture":
        return capture(corpus, golden, args.extra)
    return check(corpus, golden, args.extra)


if __name__ == "__main__":
    sys.exit(main())
