// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package thresholdvm

// TestGPUBridgeCgoNocgoParity is the node-side mirror of the parity
// test in chains/thresholdvm — proves that the re-exported GPUBackend
// method set produces byte-identical MPC ceremony state transitions
// regardless of build flavor (cgo / nocgo) and plugin presence.
//
// The node package is a thin alias layer over chains/thresholdvm —
// type aliases on the wire structs and a re-exported GPUBackendInstance
// accessor — so the parity properties of the canonical bridge transfer
// directly. We re-run the four-op pipeline through the node-side
// surface to pin that the alias layer doesn't drop any state on the
// floor.

import (
	"testing"

	chainsthreshold "github.com/luxfi/chains/thresholdvm"
)

// TestGPUBridgeCgoNocgoParity runs a 3-of-5 FROST keygen ceremony
// through GPUBackendInstance() (the node-side accessor) and compares
// the resulting arena byte-for-byte to a fresh run via the canonical
// bridge in chains/thresholdvm. Under cgo, the comparison is the
// upstream cgo bridge vs itself (both runs hit the same dispatcher).
// Under !cgo, the comparison is the upstream nocgo bridge vs itself.
// Either path proves the alias layer is transparent and the substrate
// transitions deterministically.
func TestGPUBridgeCgoNocgoParity(t *testing.T) {
	const cid uint64 = 0xCEFA01CA001
	const N = 16 // power of 2 for the open-addressing locator

	var subject, seed [32]byte
	for i := 0; i < 32; i++ {
		subject[i] = byte(i + 1)
		seed[i] = byte(0xA0 ^ i)
	}

	beginOps := []chainsthreshold.GPUCeremonyOp{
		{
			CeremonyID:        cid,
			DeadlineNs:        10_000_000_000,
			Kind:              0, // kCeremonyOpBegin
			CeremonyKind:      0, // kKindFrostKeygen → 3 rounds
			Threshold:         3,
			TotalParticipants: 5,
			Subject:           subject,
			CeremonySeed:      seed,
		},
	}

	buildRound := func(round uint32) []chainsthreshold.GPUContributionOp {
		ops := make([]chainsthreshold.GPUContributionOp, 5)
		for h := uint32(0); h < 5; h++ {
			var payload [384]byte
			for k := 0; k < 16; k++ {
				payload[k] = byte((round * 16) + h*4 + uint32(k))
			}
			ops[h] = chainsthreshold.GPUContributionOp{
				CeremonyID:  cid,
				HolderAddr:  uint64(0xDEAD0000 | h),
				Round:       round,
				HolderIndex: h,
				PayloadLen:  16,
				Payload:     payload,
			}
		}
		return ops
	}
	r1 := buildRound(0)
	r2 := buildRound(1)
	r3 := buildRound(2)

	desc1 := &GPUMPCVMRoundDescriptor{
		ChainID:             0xABBA,
		Round:               1,
		TimestampNs:         1_000_000_000,
		Epoch:               1,
		CeremonyOpCount:     1,
		ContributionOpCount: 5,
	}
	desc2 := &GPUMPCVMRoundDescriptor{
		ChainID:             0xABBA,
		Round:               2,
		TimestampNs:         2_000_000_000,
		Epoch:               1,
		ContributionOpCount: 5,
	}
	desc3 := &GPUMPCVMRoundDescriptor{
		ChainID:             0xABBA,
		Round:               3,
		TimestampNs:         3_000_000_000,
		Epoch:               1,
		ContributionOpCount: 5,
	}
	descClose := &GPUMPCVMRoundDescriptor{
		ChainID:     0xABBA,
		Round:       4,
		TimestampNs: 4_000_000_000,
		Epoch:       1,
		ClosingFlag: 1,
	}

	// Run via node-side re-export.
	runVia := func(t *testing.T) (
		[]GPUCeremony,
		[]GPUKeyShare,
		[]GPUContribution,
		GPUMPCVMState,
		GPUMPCVMTransitionResult,
	) {
		t.Helper()
		cer := make([]GPUCeremony, N)
		keys := make([]GPUKeyShare, N)
		con := make([]GPUContribution, N)

		b := GPUBackendInstance() // nil-receiver-safe via fallback to CPU reference

		if _, err := b.CeremonyApply(desc1, beginOps, cer); err != nil {
			t.Fatalf("CeremonyApply r0: %v", err)
		}
		if _, err := b.ContributionApply(desc1, r1, cer, con, 1); err != nil {
			t.Fatalf("ContributionApply r0: %v", err)
		}
		if _, _, _, err := b.KeyShareApply(desc1, cer, keys, con, 1); err != nil {
			t.Fatalf("KeyShareApply r0: %v", err)
		}

		if _, err := b.ContributionApply(desc2, r2, cer, con, 6); err != nil {
			t.Fatalf("ContributionApply r1: %v", err)
		}
		if _, _, _, err := b.KeyShareApply(desc2, cer, keys, con, 1); err != nil {
			t.Fatalf("KeyShareApply r1: %v", err)
		}

		if _, err := b.ContributionApply(desc3, r3, cer, con, 11); err != nil {
			t.Fatalf("ContributionApply r2: %v", err)
		}
		if _, _, _, err := b.KeyShareApply(desc3, cer, keys, con, 1); err != nil {
			t.Fatalf("KeyShareApply r2: %v", err)
		}

		var state GPUMPCVMState
		res, err := b.MPCTransition(descClose, cer, keys, con, &state)
		if err != nil {
			t.Fatalf("MPCTransition: %v", err)
		}
		return cer, keys, con, state, *res
	}

	cerA, keysA, conA, stateA, resA := runVia(t)
	cerB, keysB, conB, stateB, resB := runVia(t)

	for i := range cerA {
		if cerA[i] != cerB[i] {
			t.Errorf("ceremony slot %d differs across runs:\nA=%+v\nB=%+v", i, cerA[i], cerB[i])
		}
	}
	for i := range keysA {
		if keysA[i] != keysB[i] {
			t.Errorf("keyShare slot %d differs across runs:\nA=%+v\nB=%+v", i, keysA[i], keysB[i])
		}
	}
	for i := range conA {
		if conA[i] != conB[i] {
			t.Errorf("contribution slot %d differs across runs:\nA=%+v\nB=%+v", i, conA[i], conB[i])
		}
	}
	if stateA != stateB {
		t.Errorf("state differs across runs:\nA=%+v\nB=%+v", stateA, stateB)
	}
	if resA != resB {
		t.Errorf("result differs across runs:\nA=%+v\nB=%+v", resA, resB)
	}

	// Sanity — the ceremony MUST have finalized.
	if stateA.FinalizedCeremonyCount != 1 {
		t.Errorf("expected 1 finalized ceremony, got %d", stateA.FinalizedCeremonyCount)
	}
	if stateA.KeyShareCount != 5 {
		t.Errorf("expected 5 emitted key shares, got %d", stateA.KeyShareCount)
	}
	var zero [32]byte
	if stateA.MPCVMStateRoot == zero {
		t.Errorf("mpcvm_state_root is zero — the fold produced no output")
	}
}
