#!/usr/bin/env python3
"""Build an inventory that maps _ExtensionsInfo rows to DBNames-Ext files.

The script keeps the byte conversion explicit. SQL _ExtensionsInfo._IDRRef is
stored as 16 bytes. Observed DBNames-Ext suffixes match this rearrangement:

    b[12:16] b[10:12] b[8:10] b[0:2] b[2:8]

This is recorded as an observed conversion and validated against exported
DBNames-Ext file names.
"""

from __future__ import annotations

import argparse
import json
import uuid
from pathlib import Path
from typing import Any


def load_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8-sig"))


def dbnames_ext_guid_from_idrref(data: bytes) -> str:
    if len(data) != 16:
        raise ValueError(f"_IDRRef must contain 16 bytes, got {len(data)}")
    reordered = data[12:16] + data[10:12] + data[8:10] + data[0:2] + data[2:8]
    return str(uuid.UUID(bytes=reordered))


def binary_info(value: Any) -> dict[str, Any] | None:
    if isinstance(value, dict) and value.get("type") == "binary":
        return value
    return None


def main() -> int:
    parser = argparse.ArgumentParser(description="Map _ExtensionsInfo rows to DBNames-Ext files.")
    parser.add_argument("--extensions-info", type=Path, required=True)
    parser.add_argument("--params-dir", type=Path, required=True)
    parser.add_argument("--output", type=Path, required=True)
    args = parser.parse_args()

    report = load_json(args.extensions_info)
    dbnames_ext_files = {
        path.name.removeprefix("DBNames-Ext-").lower(): path
        for path in args.params_dir.iterdir()
        if path.is_file() and path.name.startswith("DBNames-Ext-")
    }

    rows = []
    matched = 0
    for row in report.get("rows") or []:
        columns = row.get("columns") or {}
        idrref = binary_info(columns.get("_IDRRef"))
        if not idrref:
            continue
        idrref_bytes = Path(idrref["path"]).read_bytes()
        dbnames_guid = dbnames_ext_guid_from_idrref(idrref_bytes)
        dbnames_file = dbnames_ext_files.get(dbnames_guid)
        if dbnames_file:
            matched += 1
        rows.append(
            {
                "row_index": row.get("row_index"),
                "extension_name": columns.get("_ExtName"),
                "extension_order": columns.get("_ExtensionOrder"),
                "update_time": columns.get("_UpdateTime"),
                "use_purpose": columns.get("_ExtensionUsePurpose"),
                "scope": columns.get("_ExtensionScope"),
                "idrref_hex": idrref_bytes.hex(),
                "dbnames_ext_guid": dbnames_guid,
                "dbnames_ext_file": str(dbnames_file) if dbnames_file else None,
                "dbnames_ext_file_name": dbnames_file.name if dbnames_file else None,
                "dbnames_ext_file_bytes": dbnames_file.stat().st_size if dbnames_file else None,
                "extension_zipped_info": binary_info(columns.get("_ExtensionZippedInfo")),
            }
        )

    known_from_rows = {row["dbnames_ext_guid"] for row in rows}
    orphan_dbnames_files = [
        {
            "file_name": path.name,
            "guid_or_marker": guid,
            "bytes": path.stat().st_size,
        }
        for guid, path in sorted(dbnames_ext_files.items())
        if guid not in known_from_rows
    ]

    result = {
        "schema": "onec_extension_inventory.v1",
        "extensions_info": str(args.extensions_info),
        "params_dir": str(args.params_dir),
        "observed_idrref_to_dbnames_ext_guid": "b[12:16] + b[10:12] + b[8:10] + b[0:2] + b[2:8]",
        "extension_row_count": len(rows),
        "matched_dbnames_ext_count": matched,
        "orphan_dbnames_ext_count": len(orphan_dbnames_files),
        "extensions": rows,
        "orphan_dbnames_ext_files": orphan_dbnames_files,
    }
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(
        json.dumps(
            {
                "output": str(args.output),
                "extensions": len(rows),
                "matched_dbnames_ext": matched,
                "orphan_dbnames_ext": len(orphan_dbnames_files),
            },
            ensure_ascii=False,
        )
    )
    return 0


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