package envelope

import (
	"testing"
)

func TestValidateRequiredFields(t *testing.T) {
	cases := []struct {
		name string
		e    *Envelope
		want string
	}{
		{"missing id", &Envelope{}, "id required"},
		{"missing run", &Envelope{ID: "x"}, "run_id required"},
		{"missing from", &Envelope{ID: "x", RunID: "r"}, "from required"},
		{"missing to", &Envelope{ID: "x", RunID: "r", From: "a"}, "to required"},
		{"unknown type", &Envelope{ID: "x", RunID: "r", From: "a", To: "b", Type: "bogus", TTLMs: 1}, "unknown type"},
		// TTL=0 is legal post-V54 (means infinite). Negative ttl is rejected.
		{"negative ttl", &Envelope{ID: "x", RunID: "r", From: "a", To: "b", Type: TypeDelegate, TTLMs: -1}, "ttl_ms must be >= 0"},
	}
	for _, c := range cases {
		err := c.e.Validate()
		if err == nil {
			t.Errorf("%s: want error containing %q, got nil", c.name, c.want)
			continue
		}
		if !contains(err.Error(), c.want) {
			t.Errorf("%s: want error containing %q, got %v", c.name, c.want, err)
		}
	}
}

func TestValidateInterruptRequiresAck(t *testing.T) {
	e := &Envelope{ID: "x", RunID: "r", From: "a", To: "b", Type: TypeInterrupt, TTLMs: 1000}
	if err := e.Validate(); err == nil {
		t.Fatal("expected error: interrupt without ack")
	}
	e.Payload.Expects = ExpectsAck
	if err := e.Validate(); err != nil {
		t.Fatalf("with ack, want nil, got %v", err)
	}
}

func TestValidateSteeringRequiresTaskAndAck(t *testing.T) {
	e := &Envelope{ID: "x", RunID: "r", From: "a", To: "b", Type: TypeSteering, TTLMs: 1000}
	if err := e.Validate(); err == nil {
		t.Fatal("expected error: steering needs task_id")
	}
	e.TaskID = "t"
	if err := e.Validate(); err == nil {
		t.Fatal("expected error: steering needs ack")
	}
	e.Payload.Expects = ExpectsAck
	if err := e.Validate(); err != nil {
		t.Fatalf("with task+ack, want nil, got %v", err)
	}
}

func TestMarshalRoundtrip(t *testing.T) {
	in := &Envelope{
		ID: "m1", RunID: "r1", From: "a", To: "b", Type: TypeDelegate, TTLMs: 5000,
		Payload: Payload{Intent: "do thing", Expects: ExpectsReport, ContextRefs: []string{"x.md"}},
	}
	raw, err := in.Marshal()
	if err != nil {
		t.Fatal(err)
	}
	out, err := Unmarshal(raw)
	if err != nil {
		t.Fatal(err)
	}
	if out.ID != in.ID || out.Type != in.Type || out.Payload.Intent != in.Payload.Intent {
		t.Fatalf("roundtrip mismatch: %+v vs %+v", out, in)
	}
}

func TestPriorityForInterrupt(t *testing.T) {
	if PriorityFor(TypeInterrupt) <= PriorityFor(TypeDelegate) {
		t.Fatal("interrupt should outrank delegate")
	}
}

func contains(s, sub string) bool {
	for i := 0; i+len(sub) <= len(s); i++ {
		if s[i:i+len(sub)] == sub {
			return true
		}
	}
	return false
}
