#!/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.

"""Regenerate tests/testdata/license-coverage.tsv from the current matchers.

For every licence in tests/testdata/licenses/, record what find_licenses()
currently produces (its DEP-5 name, or empty if undetected). The table is the
regression baseline and audit log: rows with an empty result are the coverage
gaps still to close. The `status` column are annotations (ok / undetected /
wrong / ...); it is preserved across regenerations so manual review is not
lost.

Run from the repository root:

    python3 scripts/audit-license-coverage.py
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

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

from decopy.licensing import licenses  # noqa: E402

LICENSES_DIR = ROOT / "tests" / "testdata" / "licenses"
TSV = ROOT / "tests" / "testdata" / "license-coverage.tsv"

_STATUS_COL = 2  # third column (spdx_id, dep5_result, status)

HEADER = (
    "# SPDX licence coverage baseline (regenerate with "
    "scripts/audit-license-coverage.py).\n"
    "# Columns: spdx_id <TAB> dep5_result <TAB> status <TAB> fields\n"
    "# dep5_result is find_licenses() joined with ' or '; empty = undetected.\n"
    "# status: ok = detected (correctness reviewed); undetected = no match yet;\n"
    "#         wrong:<note> = detected as the wrong licence.\n"
    "# fields: comma-separated SPDX field(s) to validate against (default\n"
    "#         licenseText). e.g. standardLicenseHeader when the licenseText is\n"
    "#         shared across an id's -only/-or-later variants.\n"
)

DEFAULT_FIELDS = ("licenseText",)
_FIELDS_COL = 3


def detect(license_text: str) -> str:
    found = licenses.find_licenses(licenses.clean_comments(license_text))
    return " or ".join(sorted(found))


def load_curated() -> dict[str, tuple[str, str]]:
    """Preserve the status and fields columns from the table."""
    curated: dict[str, tuple[str, str]] = {}
    if not TSV.exists():
        return curated
    for line in TSV.read_text().splitlines():
        if line.startswith("#") or not line.strip():
            continue
        cols = line.split("\t")
        status = cols[_STATUS_COL] if len(cols) > _STATUS_COL else ""
        fields = cols[_FIELDS_COL] if len(cols) > _FIELDS_COL else ""
        curated[cols[0]] = (status, fields)
    return curated


def main() -> None:
    prev = load_curated()
    rows: list[tuple[str, str, str, str]] = []
    for path in sorted(LICENSES_DIR.glob("*.json")):
        spdx_id = path.stem
        data = json.loads(path.read_text())
        prev_status, fields = prev.get(spdx_id, ("", ""))
        field_names = [f for f in fields.split(",") if f] or list(DEFAULT_FIELDS)
        # Result comes from the first listed field that is present.
        result = next(
            (detect(data[f]) for f in field_names if data.get(f)),
            "",
        )
        status = prev_status or ("ok" if result else "undetected")
        rows.append((spdx_id, result, status, fields))

    lines = [HEADER]
    lines.extend(f"{s}\t{r}\t{st}\t{f}\n" for s, r, st, f in rows)
    TSV.write_text("".join(lines))

    detected = sum(1 for _, result, _, _ in rows if result)
    undetected = len(rows) - detected
    print(f"{len(rows)} licences: {detected} detected, {undetected} undetected")


if __name__ == "__main__":
    main()
