"""Regenerate tests/fixtures/crdt/merge_fixtures.json from the canonical
reference (vault/crdt.py).  Run:  uv run python tests/fixtures/crdt/_generate.py

The emitted JSON is the *golden* G1 fixture corpus (ADR-0180 / ADR-0196 gate
G1).  Changing the canonical form in vault/crdt.py changes these hashes; the
contract tests pin them, so a golden diff is a reviewed, deliberate act.

Versor components use only exactly-f32-representable, exactly-JSON-representable
values (integers, signed zeros, halves) so the corpus round-trips with zero
float ambiguity.  NaN / signed-zero *bit*-distinctness is exercised in-code in
test_crdt_content_ordering.py, not here.
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

_REPO_ROOT = Path(__file__).resolve().parents[3]
sys.path.insert(0, str(_REPO_ROOT))

from vault.crdt import (  # noqa: E402  # pyright: ignore[reportMissingImports]
    VERSOR_COMPONENTS,
    ArenaEntry,
    Delta,
    canonical_bytes,
    delta_hash,
    merge_kernel,
)

_OUT = Path(__file__).resolve().parent / "merge_fixtures.json"
# Rust parity fixture lives under core-rs/tests/fixtures/ (a *subdir* of tests/,
# so cargo does not compile it as a standalone integration-test target).
_RUST_OUT = _REPO_ROOT / "core-rs" / "tests" / "fixtures" / "crdt_parity_expected.rs"


def versor(**components: float) -> list[float]:
    """Build a 32-float versor; unset indices default to 0.0."""
    v = [0.0] * VERSOR_COMPONENTS
    for idx, value in components.items():
        v[int(idx.lstrip("c"))] = value
    return v


def entry(versor_vals: list[float], provenance: bytes) -> ArenaEntry:
    return ArenaEntry.of(versor_vals, provenance)


def encode_entry(e: ArenaEntry) -> dict:
    return {"versor": list(e.versor), "provenance_hex": e.provenance.hex()}


def encode_delta_input(entries: list[ArenaEntry]) -> list[dict]:
    return [encode_entry(e) for e in entries]


def build_cases() -> list[dict]:
    cases: list[dict] = []

    def add(name: str, description: str, delta_entry_lists: list[list[ArenaEntry]]):
        deltas = [Delta.from_entries(es) for es in delta_entry_lists]
        merged = merge_kernel(deltas)
        cases.append(
            {
                "name": name,
                "description": description,
                "input_deltas": [encode_delta_input(es) for es in delta_entry_lists],
                "expected_merged_entries": [encode_entry(e) for e in merged.entries],
                "expected_canonical_bytes_hex": canonical_bytes(merged).hex(),
                "expected_delta_hash": delta_hash(merged),
            }
        )

    # 1 — single entry, single delta.
    add(
        "single_entry",
        "One delta, one entry: identity of the canonical form.",
        [[entry(versor(c0=1.0), b"a")]],
    )

    # 2 — duplicate byte-identical entries collapse (idempotence at entry level).
    add(
        "dedup_within_delta",
        "Byte-identical entries collapse to one (join idempotence).",
        [[entry(versor(c0=1.0), b"a"), entry(versor(c0=1.0), b"a")]],
    )

    # 3 — same versor, distinct provenance: BOTH retained (no over-dedup).
    add(
        "distinct_provenance_retained",
        "Same versor under different provenance are distinct content.",
        [[entry(versor(c5=2.0), b"left"), entry(versor(c5=2.0), b"right")]],
    )

    # 4 — three deltas merged; corpus pins the canonical (content-sorted) result.
    add(
        "three_delta_merge",
        "Union of three deltas, content-sorted and deduped.",
        [
            [entry(versor(c1=3.0), b"z"), entry(versor(c0=1.0), b"a")],
            [entry(versor(c2=2.0), b"m"), entry(versor(c0=1.0), b"a")],
            [entry(versor(c10=5.0), b"q")],
        ],
    )

    # 5 — signed-zero is distinct content (+0.0 bits != -0.0 bits).
    add(
        "signed_zero_distinct",
        "+0.0 and -0.0 have distinct bit patterns -> distinct entries.",
        [[entry(versor(c0=0.0), b"p"), entry(versor(c0=-0.0), b"p")]],
    )

    return cases


def write_rust_fixtures(cases: list[dict]) -> None:
    """Emit the Rust-side expected canonical_bytes hex so core-rs/tests can
    assert byte-parity without a JSON parser dependency. Python stays the single
    source of truth."""
    lines = [
        "// @generated by tests/fixtures/crdt/_generate.py — DO NOT EDIT.",
        "// Expected canonical_bytes hex per case (mirrors merge_fixtures.json):",
        "// the Python-canonical Delta-CRDT G1 contract (ADR-0180 / ADR-0196)",
        "// the Rust substrate must reproduce byte-for-byte.",
        "// (include!d mid-module, so plain // comments — not //! — are required.)",
        "",
        "pub const EXPECTED_CANONICAL_BYTES_HEX: &[(&str, &str)] = &[",
    ]
    for case in cases:
        lines.append(
            f'    ("{case["name"]}", "{case["expected_canonical_bytes_hex"]}"),'
        )
    lines.append("];")
    _RUST_OUT.parent.mkdir(parents=True, exist_ok=True)
    _RUST_OUT.write_text("\n".join(lines) + "\n")


def main() -> None:
    corpus = {
        "_comment": (
            "Golden Delta-CRDT fixture corpus (ADR-0180 / ADR-0196 gate G1). "
            "Regenerate with: uv run python tests/fixtures/crdt/_generate.py"
        ),
        "versor_components": VERSOR_COMPONENTS,
        "cases": build_cases(),
    }
    _OUT.write_text(json.dumps(corpus, indent=2, sort_keys=False) + "\n")
    write_rust_fixtures(corpus["cases"])
    print(f"wrote {_OUT} ({len(corpus['cases'])} cases)")
    print(f"wrote {_RUST_OUT}")
    for case in corpus["cases"]:
        print(f"  {case['name']:30s} hash={case['expected_delta_hash'][:16]}…")


if __name__ == "__main__":
    main()
