package ankersolix

import "testing"

// The site views are only worth asking for once the by-serial lookup has found a
// site, and that lookup names it differently depending on which shape answered.
func TestSiteIDFromReadsWhicheverKeyTheLookupUsed(t *testing.T) {
	cases := []struct {
		name  string
		attrs map[string]string
		want  string
	}{
		{"snake case", map[string]string{"site_id": "s-1"}, "s-1"},
		{"camel case", map[string]string{"siteId": "s-2"}, "s-2"},
		{"nested", map[string]string{"site_info.site_id": "s-3"}, "s-3"},
		{"standalone charger", map[string]string{"device_sn": "AT1"}, ""},
		{"blank is not an answer", map[string]string{"site_id": "  "}, ""},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			views := []chargerDetailView{{ID: "ocpp", Attrs: map[string]string{"site_id": "wrong"}}, {ID: "siteBySn", Attrs: tc.attrs}}
			if got := siteIDFrom(views); got != tc.want {
				t.Fatalf("siteIDFrom = %q, want %q", got, tc.want)
			}
		})
	}
}

// "source 0" is a number until the account's endpoint list gives it an address.
func TestNoteOcppSourceNamesTheBackend(t *testing.T) {
	views := []chargerDetailView{
		{ID: "ocpp", Attrs: map[string]string{"source": "1"}},
		{ID: "ocppEndpoints", Attrs: map[string]string{
			"list[0].source": "0", "list[0].url": "wss://ocpp.anker.example/0",
			"list[1].source": "1", "list[1].url": "wss://ocpp.anker.example/1",
		}},
	}
	noteOcppSource(views)
	if want := "source 1 = wss://ocpp.anker.example/1"; views[0].Note != want {
		t.Fatalf("note = %q, want %q", views[0].Note, want)
	}
}

// A number the list does not carry stays a number: a guessed backend would be
// worse than saying nothing.
func TestNoteOcppSourceSaysNothingWhenTheListCannotAnswer(t *testing.T) {
	views := []chargerDetailView{
		{ID: "ocpp", Attrs: map[string]string{"source": "7"}},
		{ID: "ocppEndpoints", Attrs: map[string]string{"list[0].source": "0", "list[0].url": "wss://x/0"}},
	}
	noteOcppSource(views)
	if views[0].Note != "" {
		t.Fatalf("note = %q, want none", views[0].Note)
	}
}

// The entry's own address, not a neighbour's.
func TestEndpointForSourceStaysWithinItsEntry(t *testing.T) {
	endpoints := map[string]string{
		"list[0].source": "0", "list[0].endpoint": "wss://a",
		"list[1].source": "2", "list[1].endpoint": "wss://b",
	}
	if got := endpointForSource(endpoints, "2"); got != "wss://b" {
		t.Fatalf("endpointForSource = %q, want wss://b", got)
	}
	if got := endpointForSource(endpoints, "3"); got != "" {
		t.Fatalf("endpointForSource = %q, want empty", got)
	}
}

// The multi-serial views accept either spelling, and reject neither silently.
func TestSerialList(t *testing.T) {
	if got := serialList(invokeParams{SN: "AT1"}); len(got) != 1 || got[0] != "AT1" {
		t.Fatalf("single serial = %v", got)
	}
	got := serialList(invokeParams{SN: "AT1", SNs: []string{"AT2", "  ", "AT3"}})
	if len(got) != 2 || got[0] != "AT2" || got[1] != "AT3" {
		t.Fatalf("several serials = %v", got)
	}
	if got := serialList(invokeParams{}); len(got) != 0 {
		t.Fatalf("nothing given = %v", got)
	}
}

// The paged and multi-view endpoints get a sane ask when the caller does not
// care, rather than a zero the cloud reads as "no page at all".
func TestPagingAndViewDefaults(t *testing.T) {
	if pageNum(0) != 1 || pageNum(3) != 3 {
		t.Fatalf("pageNum: %d %d", pageNum(0), pageNum(3))
	}
	if pageSize(0) != 20 || pageSize(500) != 20 || pageSize(50) != 50 {
		t.Fatalf("pageSize: %d %d %d", pageSize(0), pageSize(500), pageSize(50))
	}
	if upgradeRecordView(0) != 1 || upgradeRecordView(4) != 1 || upgradeRecordView(2) != 2 {
		t.Fatalf("upgradeRecordView: %d %d %d", upgradeRecordView(0), upgradeRecordView(4), upgradeRecordView(2))
	}
}
