package protocol

import (
	"encoding/json"
	"testing"
	"time"
)

func TestEnvelopeValidate_AgentHello(t *testing.T) {
	payload := AgentHelloPayload{
		NodeID:          "node-01",
		AgentVersion:    "0.1.0",
		ProtocolVersion: CurrentProtocolVersion,
	}
	raw, err := json.Marshal(payload)
	if err != nil {
		t.Fatal(err)
	}

	env := &Envelope{
		ProtocolVersion: CurrentProtocolVersion,
		MessageID:       "msg-001",
		Type:            TypeAgentHello,
		Timestamp:       time.Now().UTC(),
		Payload:         raw,
	}

	if err := env.Validate(); err != nil {
		t.Fatalf("expected valid envelope, got %v", err)
	}
	if !env.Type.IsAgentToControl() {
		t.Fatal("agent.hello should be agent-to-control")
	}
}

func TestEnvelopeValidate_ServerStart(t *testing.T) {
	payload := ServerStartPayload{
		OperationMeta: OperationMeta{
			ServerID:   "srv-01",
			Generation: 4,
		},
		DesiredGeneration: 4,
	}
	raw, err := json.Marshal(payload)
	if err != nil {
		t.Fatal(err)
	}

	env := &Envelope{
		ProtocolVersion: CurrentProtocolVersion,
		MessageID:       "msg-002",
		CorrelationID:   "corr-002",
		Type:            TypeServerStart,
		Timestamp:       time.Now().UTC(),
		Payload:         raw,
	}

	if err := env.Validate(); err != nil {
		t.Fatalf("expected valid envelope, got %v", err)
	}
	if !env.Type.IsControlToAgent() {
		t.Fatal("server.start should be control-to-agent")
	}
}

func TestEnvelopeValidate_RejectsInvalid(t *testing.T) {
	tests := []struct {
		name string
		env  Envelope
	}{
		{
			name: "missing message id",
			env: Envelope{
				ProtocolVersion: 1,
				Type:            TypeAgentHeartbeat,
				Timestamp:       time.Now().UTC(),
				Payload:         json.RawMessage(`{"nodeId":"n1"}`),
			},
		},
		{
			name: "unknown type",
			env: Envelope{
				ProtocolVersion: 1,
				MessageID:       "m1",
				Type:            "agent.unknown",
				Timestamp:       time.Now().UTC(),
				Payload:         json.RawMessage(`{}`),
			},
		},
		{
			name: "non-utc timestamp",
			env: Envelope{
				ProtocolVersion: 1,
				MessageID:       "m1",
				Type:            TypeAgentHeartbeat,
				Timestamp:       time.Now().Local(),
				Payload:         json.RawMessage(`{"nodeId":"n1"}`),
			},
		},
		{
			name: "invalid payload json",
			env: Envelope{
				ProtocolVersion: 1,
				MessageID:       "m1",
				Type:            TypeAgentHeartbeat,
				Timestamp:       time.Now().UTC(),
				Payload:         json.RawMessage(`{not-json`),
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			if err := tc.env.Validate(); err == nil {
				t.Fatal("expected validation error")
			}
		})
	}
}

func TestDecodeEnvelope(t *testing.T) {
	data := []byte(`{
		"protocolVersion": 1,
		"messageId": "01HXYZ",
		"type": "agent.heartbeat",
		"timestamp": "2026-01-01T00:00:00Z",
		"payload": {"nodeId":"node-01","runningServers":0}
	}`)

	env, err := DecodeEnvelope(data)
	if err != nil {
		t.Fatalf("decode failed: %v", err)
	}
	if env.Type != TypeAgentHeartbeat {
		t.Fatalf("unexpected type %s", env.Type)
	}
}

func TestNewEnvelope(t *testing.T) {
	env, err := NewEnvelope(TypeAgentHello, "msg-new", AgentHelloPayload{
		NodeID:          "n1",
		AgentVersion:    "0.1.0",
		ProtocolVersion: 1,
	})
	if err != nil {
		t.Fatalf("new envelope: %v", err)
	}
	if env.ProtocolVersion != CurrentProtocolVersion {
		t.Fatalf("protocol version %d", env.ProtocolVersion)
	}
}
