package ocpp

import (
	"encoding/json"
	"testing"
)

func TestDecodeCall(t *testing.T) {
	m, err := DecodeMessage([]byte(`[2,"abc","BootNotification",{"chargePointModel":"A5191"}]`))
	if err != nil {
		t.Fatal(err)
	}
	if m.Type != MessageTypeCall || m.ID != "abc" || m.Action != "BootNotification" {
		t.Fatalf("bad decode: %+v", m)
	}
	var p struct {
		Model string `json:"chargePointModel"`
	}
	if err := json.Unmarshal(m.Payload, &p); err != nil || p.Model != "A5191" {
		t.Fatalf("payload = %s (%v)", m.Payload, err)
	}
}

func TestDecodeCallResultAndError(t *testing.T) {
	r, err := DecodeMessage([]byte(`[3,"abc",{"status":"Accepted"}]`))
	if err != nil || r.Type != MessageTypeCallResult {
		t.Fatalf("callresult decode: %+v %v", r, err)
	}
	e, err := DecodeMessage([]byte(`[4,"abc","NotImplemented","nope",{}]`))
	if err != nil || e.Type != MessageTypeCallError || e.ErrorCode != "NotImplemented" || e.ErrorDescription != "nope" {
		t.Fatalf("callerror decode: %+v %v", e, err)
	}
}

func TestEncodeRoundTrip(t *testing.T) {
	b, err := EncodeCall("id1", "Reset", map[string]any{"type": "Soft"})
	if err != nil {
		t.Fatal(err)
	}
	m, err := DecodeMessage(b)
	if err != nil || m.Action != "Reset" {
		t.Fatalf("round trip: %+v %v", m, err)
	}
}

func TestEncodeEmptyPayloadIsObject(t *testing.T) {
	// OCPP requires the payload be a JSON object, never null.
	b, err := EncodeCallResult("id1", nil)
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != `[3,"id1",{}]` {
		t.Fatalf("empty payload encoded as %s, want [3,\"id1\",{}]", b)
	}
}

func TestDecodeRejectsGarbage(t *testing.T) {
	if _, err := DecodeMessage([]byte(`{"not":"an array"}`)); err == nil {
		t.Fatal("expected error decoding non-array")
	}
	if _, err := DecodeMessage([]byte(`[2,"id"]`)); err == nil {
		t.Fatal("expected error decoding short CALL")
	}
}
