package main

import "testing"

// The colour is interpolated into a CSS color-mix() by the grid, so anything
// that is not plainly a hex colour has to be refused rather than passed on.
func TestRowColorsRefuseAnythingButHex(t *testing.T) {
	in := RowColorSettings{Enabled: true, Rules: []RowColorRule{
		{ID: "confirmed", Color: "#123abc", Enabled: true},
		{ID: "sent", Color: "red; background:url(x)", Enabled: true},
		{ID: "to_send", Color: "", Enabled: true},
	}}
	got := normRowColors(in)

	byID := map[string]RowColorRule{}
	for _, r := range got.Rules {
		byID[r.ID] = r
	}
	if byID["confirmed"].Color != "#123abc" {
		t.Errorf("a valid colour was rewritten: %q", byID["confirmed"].Color)
	}
	if byID["sent"].Color != rowColorDefaults["sent"] {
		t.Errorf("an injection attempt survived: %q", byID["sent"].Color)
	}
	if byID["to_send"].Color != rowColorDefaults["to_send"] {
		t.Errorf("an empty colour was kept: %q", byID["to_send"].Color)
	}
}

// Priority lives in the data, not in a chain of ifs: a contact confirmed on
// LoTW AND by card is confirmed, not "sent, awaiting reply". The panel shows
// the rules in the order they apply, so that order must survive a round trip.
func TestRowColorsKeepPriorityOrder(t *testing.T) {
	// Saved in a jumbled order, as a hand-edited settings row could be.
	got := normRowColors(RowColorSettings{Rules: []RowColorRule{
		{ID: "to_send", Color: "#111111"},
		{ID: "confirmed", Color: "#222222"},
	}})
	if len(got.Rules) != len(rowColorOrder) {
		t.Fatalf("got %d rules, want every one present", len(got.Rules))
	}
	for i, id := range rowColorOrder {
		if got.Rules[i].ID != id {
			t.Errorf("rule %d is %q, want %q", i, got.Rules[i].ID, id)
		}
	}
	// The saved colours survived the reordering: each stayed with its own rule.
	byID := map[string]string{}
	for _, r := range got.Rules {
		byID[r.ID] = r.Color
	}
	if byID["to_send"] != "#111111" || byID["confirmed"] != "#222222" {
		t.Errorf("colours moved between rules: %v", byID)
	}
}

// Style and strength are normalised, not rejected: they only shape a colour, and
// a value that got away is a slider, not a fault worth resetting the operator's
// whole choice for.
func TestRowColorsNormaliseStyleAndIntensity(t *testing.T) {
	for _, tc := range []struct {
		inStyle   string
		inPct     int
		wantStyle string
		wantPct   int
	}{
		{"bar", 12, "bar", 12},
		{"tint", 30, "tint", 30},
		{"both", 45, "both", 45},
		{"", 0, "bar", 12},           // never configured
		{"wallpaper", 12, "bar", 12}, // not a style we draw
		{"tint", 900, "tint", 45},    // clamped, not reset
		{"tint", -5, "tint", 12},
	} {
		got := normRowColors(RowColorSettings{Style: tc.inStyle, Intensity: tc.inPct})
		if got.Style != tc.wantStyle || got.Intensity != tc.wantPct {
			t.Errorf("(%q,%d) -> (%q,%d), want (%q,%d)",
				tc.inStyle, tc.inPct, got.Style, got.Intensity, tc.wantStyle, tc.wantPct)
		}
	}
}

// Channels are normalised to the canonical order, and anything unknown is
// dropped. Empty stays empty, because empty means "every channel" — a rule the
// operator has not narrowed must not silently become a rule about nothing.
func TestRowColorChannelsNormalise(t *testing.T) {
	got := normRowColors(RowColorSettings{Rules: []RowColorRule{
		{ID: "confirmed", Color: "#111111", Channels: []string{"LOTW", "pigeon", "qsl", "lotw"}},
		{ID: "sent", Color: "#222222"},
	}})
	byID := map[string][]string{}
	for _, r := range got.Rules {
		byID[r.ID] = r.Channels
	}
	if len(byID["confirmed"]) != 2 || byID["confirmed"][0] != "qsl" || byID["confirmed"][1] != "lotw" {
		t.Errorf("confirmed channels = %v, want [qsl lotw] — canonical order, deduped, unknown dropped", byID["confirmed"])
	}
	if len(byID["sent"]) != 0 {
		t.Errorf("sent channels = %v, want empty (meaning every channel)", byID["sent"])
	}
}

// The order IS the priority, and "to send" leads. A contact confirmed on LoTW
// that still owes a paper card must read as owing the card — put after
// "confirmed" it would go green and never be printed.
func TestToSendOutranksConfirmed(t *testing.T) {
	got := normRowColors(RowColorSettings{})
	if got.Rules[0].ID != "to_send" {
		t.Errorf("first rule is %q, want to_send", got.Rules[0].ID)
	}
	if got.Rules[len(got.Rules)-1].ID != "worked" {
		t.Errorf("last rule is %q, want worked as the catch-all", got.Rules[len(got.Rules)-1].ID)
	}
}
