//go:build golden_generate

// The golden_generate build tag is intentionally separate so CI never
// regenerates the committed fixture. Run ONCE locally to (re)commit:
//
//	~/sdk/go1.26.5/bin/go test -tags golden_generate                 \
//	    -run TestGenerateGoldenFixture -v                             \
//	    ./pkg/adapters/crypto/composite/...
//
// Then commit the produced testdata/golden-1byte.pqenc and
// testdata/golden-keys.json. Non-`-update` runs of TestGoldenFormat load
// the committed artifacts and verify decrypt-equality.
package composite

import (
	"bytes"
	"crypto/sha256"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"os"
	"testing"

	"git.tswf.io/infra/go-synapse-backupper/pkg/domain/crypto"
)

// deterministicRand implements io.Reader via a SHA-256 counter stream so the
// golden fixture is byte-for-byte reproducible across machines and Go
// toolchain versions.
type deterministicRand struct {
	seq uint64
}

func (d *deterministicRand) Read(
	p []byte,
) (int, error) {
	for offset := 0; offset < len(p); {
		var b [8]byte
		binary.BigEndian.PutUint64(b[:], d.seq)
		d.seq++
		out := sha256.New()
		out.Write(b[:])
		hashed := out.Sum(nil)
		n := copy(p[offset:], hashed)
		offset += n
	}
	return len(p), nil
}

// Hard-coded priv seeds so the committed golden-keys.json stays stable across
// builds — these are the test-only private "keys" the committed golden file
// decrypts against.
var (
	goldenPqSeed = [32]byte{
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
		0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
	}
	goldenClassicalSeed = [32]byte{
		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
		0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
		0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
	}
)

func TestGenerateGoldenFixture(
	t *testing.T,
) {
	pqPub := newFakePub(fakePqSchemeID, goldenPqSeed[:])
	pqPriv := newFakePriv(fakePqSchemeID, goldenPqSeed[:])
	classicalPub := newFakePub(fakeClassicalSchemeID, goldenClassicalSeed[:])
	classicalPriv := newFakePriv(fakeClassicalSchemeID, goldenClassicalSeed[:])

	reg := crypto.NewRegistry()
	if err := reg.Register(fakePqSchemeID, newFakePqKem); err != nil {
		t.Fatalf("register pq fake: %v", err)
	}
	if err := reg.Register(fakeClassicalSchemeID, newFakeClassicalKem); err != nil {
		t.Fatalf("register classical fake: %v", err)
	}

	enc := NewEncryptor(reg)
	rng := &deterministicRand{}

	var encrypted bytes.Buffer
	if err := enc.Encrypt(
		bytes.NewReader([]byte{0xAA}),
		makeRecipientPubs(pqPub, classicalPub),
		&encrypted,
		rng,
	); err != nil {
		t.Fatalf("Encrypt: %v", err)
	}

	if err := os.MkdirAll("testdata", 0o755); err != nil {
		t.Fatalf("mkdir testdata: %v", err)
	}
	if err := os.WriteFile("testdata/golden-1byte.pqenc", encrypted.Bytes(), 0o644); err != nil {
		t.Fatalf("write golden file: %v", err)
	}

	keys := goldenKeyFile{
		Pq:        base64.StdEncoding.EncodeToString(pqPriv.Raw()),
		Classical: base64.StdEncoding.EncodeToString(classicalPriv.Raw()),
	}
	marshalled, err := json.MarshalIndent(keys, "", "  ")
	if err != nil {
		t.Fatalf("marshal keys: %v", err)
	}
	marshalled = append(marshalled, '\n')
	if err := os.WriteFile("testdata/golden-keys.json", marshalled, 0o644); err != nil {
		t.Fatalf("write keys json: %v", err)
	}

	fmt.Printf("Golden fixture written: testdata/golden-1byte.pqenc (%d bytes), "+
		"testdata/golden-keys.json (%d bytes)\n", len(encrypted.Bytes()), len(marshalled))
}
