package seal

import (
	"bytes"
	"testing"
)

// TestSealUnsealRoundTrip verifies the OIDC seal/unseal round-trip.
func TestSealUnsealRoundTrip(t *testing.T) {
	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}
	blob, shards, err := Seal(masterKey, "user-oidc-sub-123", "https://idp.example")
	if err != nil {
		t.Fatalf("Seal: %v", err)
	}
	if len(shards) != 5 {
		t.Errorf("shards = %d, want 5", len(shards))
	}
	if blob.Mode != "oidc" {
		t.Errorf("mode = %q, want oidc", blob.Mode)
	}
	unsealed, err := Unseal(blob, "user-oidc-sub-123")
	if err != nil {
		t.Fatalf("Unseal: %v", err)
	}
	if !bytes.Equal(unsealed, masterKey) {
		t.Error("unsealed key != original")
	}
}

// TestSealWrongSubFails verifies unseal with the wrong subject fails.
func TestSealWrongSubFails(t *testing.T) {
	masterKey := make([]byte, 32)
	blob, _, err := Seal(masterKey, "correct-sub", "https://idp")
	if err != nil {
		t.Fatalf("Seal: %v", err)
	}
	_, err = Unseal(blob, "wrong-sub")
	if err == nil {
		t.Error("Unseal with wrong sub should fail")
	}
}

// TestShamirRecovery verifies 3-of-5 recovery works.
func TestShamirRecovery(t *testing.T) {
	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i + 1)
	}
	blob, shards, err := Seal(masterKey, "sub-123", "https://idp")
	if err != nil {
		t.Fatalf("Seal: %v", err)
	}
	// Recover with first 3 shards.
	recovered, err := UnsealWithShamir(blob, shards[:3])
	if err != nil {
		t.Fatalf("UnsealWithShamir (3 shards): %v", err)
	}
	if !bytes.Equal(recovered, masterKey) {
		t.Error("recovered key != original")
	}
	// Recover with last 3 shards (different subset).
	recovered2, err := UnsealWithShamir(blob, shards[2:])
	if err != nil {
		t.Fatalf("UnsealWithShamir (last 3): %v", err)
	}
	if !bytes.Equal(recovered2, masterKey) {
		t.Error("recovered key (last 3) != original")
	}
}

// TestShamirTwoShardsFails verifies 2 shards are insufficient.
func TestShamirTwoShardsFails(t *testing.T) {
	masterKey := make([]byte, 32)
	_, shards, _ := Seal(masterKey, "sub", "https://idp")
	_, err := UnsealWithShamir(nil, shards[:2])
	if err == nil {
		t.Error("2 shards should fail")
	}
}

// TestShamirSplitCombine verifies direct split/combine round-trip.
func TestShamirSplitCombine(t *testing.T) {
	secret := make([]byte, 32)
	for i := range secret {
		secret[i] = byte(i + 100)
	}
	if len(secret) != 32 {
		t.Fatalf("test secret is %d bytes, want 32", len(secret))
	}
	shards, err := ShamirSplit(secret, 5, 3)
	if err != nil {
		t.Fatalf("ShamirSplit: %v", err)
	}
	if len(shards) != 5 {
		t.Errorf("shards = %d, want 5", len(shards))
	}
	// Any 3 shards reconstruct the secret.
	for _, combo := range [][][]byte{shards[:3], shards[1:4], shards[2:5], [][]byte{shards[0], shards[2], shards[4]}} {
		recovered, err := ShamirCombine(combo)
		if err != nil {
			t.Fatalf("Combine: %v", err)
		}
		if !bytes.Equal(recovered, secret) {
			t.Error("recovered != secret")
		}
	}
}

// TestSealWithCA verifies the mTLS-only offline path.
func TestSealWithCA(t *testing.T) {
	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}
	blob, err := SealWithCA(masterKey, "sha256:abc123")
	if err != nil {
		t.Fatalf("SealWithCA: %v", err)
	}
	if blob.Mode != "ca" {
		t.Errorf("mode = %q, want ca", blob.Mode)
	}
	unsealed, err := UnsealWithCA(blob, "sha256:abc123")
	if err != nil {
		t.Fatalf("UnsealWithCA: %v", err)
	}
	if !bytes.Equal(unsealed, masterKey) {
		t.Error("unsealed key != original")
	}
	// Wrong CA fingerprint fails.
	_, err = UnsealWithCA(blob, "sha256:wrong")
	if err == nil {
		t.Error("UnsealWithCA with wrong fingerprint should fail")
	}
}

// TestSealedBlobModeMismatch verifies mode mismatch errors.
func TestSealedBlobModeMismatch(t *testing.T) {
	masterKey := make([]byte, 32)
	blob, _ := SealWithCA(masterKey, "fp")
	_, err := Unseal(blob, "sub") // blob is CA-mode, not OIDC
	if err == nil {
		t.Error("Unseal OIDC on CA blob should fail")
	}
}

// TestEncodeDecodeShard verifies shard base64 round-trip.
func TestEncodeDecodeShard(t *testing.T) {
	shard := []byte{1, 2, 3, 4, 5}
	encoded := EncodeShard(shard)
	decoded, err := DecodeShard(encoded)
	if err != nil {
		t.Fatalf("Decode: %v", err)
	}
	if !bytes.Equal(decoded, shard) {
		t.Error("decode != original")
	}
}

// TestVerifySealedKey verifies the verification function.
func TestVerifySealedKey(t *testing.T) {
	masterKey := make([]byte, 32)
	blob, _, _ := Seal(masterKey, "sub", "https://idp")
	if !VerifySealedKey(blob, masterKey, "sub") {
		t.Error("VerifySealedKey should confirm correct key")
	}
	wrongKey := make([]byte, 32)
	wrongKey[0] = 1
	if VerifySealedKey(blob, wrongKey, "sub") {
		t.Error("VerifySealedKey should reject wrong key")
	}
}
