package sign

import (
	"crypto/ed25519"
	"encoding/json"
	"errors"
	"testing"
)

func mustKey(t *testing.T) (ed25519.PublicKey, ed25519.PrivateKey) {
	t.Helper()
	pub, priv, err := GenerateKey()
	if err != nil {
		t.Fatalf("GenerateKey: %v", err)
	}
	return pub, priv
}

func TestSignVerifyRoundTrip(t *testing.T) {
	pub, priv := mustKey(t)
	env := Envelope{
		Version:  1,
		IssuedAt: "2026-06-13T00:00:00Z",
		KeyID:    "k1",
		Payload:  json.RawMessage(`{"api_domains":["a.example.com"]}`),
	}
	if err := Sign(priv, &env); err != nil {
		t.Fatalf("Sign: %v", err)
	}
	if env.Sig == "" {
		t.Fatal("signature not set")
	}
	if err := Verify(env, KeyRing{"k1": pub}); err != nil {
		t.Fatalf("Verify: %v", err)
	}
}

func TestVerifyRejectsTamperedPayload(t *testing.T) {
	pub, priv := mustKey(t)
	env := Envelope{Version: 1, IssuedAt: "t", KeyID: "k1", Payload: json.RawMessage(`{"x":1}`)}
	if err := Sign(priv, &env); err != nil {
		t.Fatal(err)
	}
	env.Payload = json.RawMessage(`{"x":2}`) // tamper after signing
	if err := Verify(env, KeyRing{"k1": pub}); !errors.Is(err, ErrBadSignature) {
		t.Fatalf("want ErrBadSignature, got %v", err)
	}
}

func TestVerifyRejectsVersionTamper(t *testing.T) {
	pub, priv := mustKey(t)
	env := Envelope{Version: 5, IssuedAt: "t", KeyID: "k1", Payload: json.RawMessage(`{"x":1}`)}
	if err := Sign(priv, &env); err != nil {
		t.Fatal(err)
	}
	env.Version = 99 // attacker tries to inflate version
	if err := Verify(env, KeyRing{"k1": pub}); !errors.Is(err, ErrBadSignature) {
		t.Fatalf("want ErrBadSignature, got %v", err)
	}
}

func TestVerifyUnknownKeyID(t *testing.T) {
	pub, priv := mustKey(t)
	env := Envelope{Version: 1, IssuedAt: "t", KeyID: "k1", Payload: json.RawMessage(`{}`)}
	if err := Sign(priv, &env); err != nil {
		t.Fatal(err)
	}
	if err := Verify(env, KeyRing{"other": pub}); !errors.Is(err, ErrUnknownKeyID) {
		t.Fatalf("want ErrUnknownKeyID, got %v", err)
	}
}

func TestKeyRotationDoublePublicKey(t *testing.T) {
	oldPub, _ := mustKey(t)
	newPub, newPriv := mustKey(t)
	// Document signed with the NEW key, key_id "v2".
	env := Envelope{Version: 1, IssuedAt: "t", KeyID: "v2", Payload: json.RawMessage(`{}`)}
	if err := Sign(newPriv, &env); err != nil {
		t.Fatal(err)
	}
	// During the rotation window the client holds BOTH public keys.
	ring := KeyRing{"v1": oldPub, "v2": newPub}
	if err := Verify(env, ring); err != nil {
		t.Fatalf("rotation window verify failed: %v", err)
	}
	// A client that only has the old key must reject it.
	if err := Verify(env, KeyRing{"v1": oldPub}); !errors.Is(err, ErrUnknownKeyID) {
		t.Fatalf("want ErrUnknownKeyID for old-only ring, got %v", err)
	}
}

func TestCanonicalizeFieldOrderInvariant(t *testing.T) {
	_, priv := mustKey(t)
	// Same logical payload, different key order -> identical signature.
	envA := Envelope{Version: 1, IssuedAt: "t", KeyID: "k1", Payload: json.RawMessage(`{"a":1,"b":2}`)}
	envB := Envelope{Version: 1, IssuedAt: "t", KeyID: "k1", Payload: json.RawMessage(`{"b":2,"a":1}`)}
	if err := Sign(priv, &envA); err != nil {
		t.Fatal(err)
	}
	if err := Sign(priv, &envB); err != nil {
		t.Fatal(err)
	}
	if envA.Sig != envB.Sig {
		t.Fatalf("canonicalization not order-invariant:\n  A=%s\n  B=%s", envA.Sig, envB.Sig)
	}
}

func TestCanonicalizePreservesIntegers(t *testing.T) {
	out, err := Canonicalize([]byte(`{"v":12345678901234567}`))
	if err != nil {
		t.Fatal(err)
	}
	if string(out) != `{"v":12345678901234567}` {
		t.Fatalf("integer not preserved: %s", out)
	}
}

func TestParseKeyRing(t *testing.T) {
	pub, _ := mustKey(t)
	ring, err := ParseKeyRing([]string{"k1=" + EncodePublic(pub)})
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := ring["k1"]; !ok {
		t.Fatal("k1 missing from ring")
	}
	if _, err := ParseKeyRing([]string{"bad"}); err == nil {
		t.Fatal("want error for malformed spec")
	}
}

func TestDecodePrivateRoundTrip(t *testing.T) {
	_, priv := mustKey(t)
	got, err := DecodePrivate(EncodePrivate(priv))
	if err != nil {
		t.Fatal(err)
	}
	if !got.Equal(priv) {
		t.Fatal("private key round-trip mismatch")
	}
}
