package ankersolix

import (
	"context"
	"encoding/json"
	"strings"
	"testing"
)

// A card number typed with punctuation is the same card as the one the account
// stores bare — anything else deletes nothing and adds a duplicate.
func TestNormalizeCardNumber(t *testing.T) {
	for _, tc := range []struct{ in, want string }{
		{"4754DC2A", "4754DC2A"},
		{"47:54:dc:2a", "4754DC2A"},
		{" 47-54 dc 2a ", "4754DC2A"},
		{"04dfe672151a90", "04DFE672151A90"},
		{"", ""},
		{" :- ", ""},
	} {
		if got := normalizeCardNumber(tc.in); got != tc.want {
			t.Errorf("normalizeCardNumber(%q) = %q, want %q", tc.in, got, tc.want)
		}
	}
}

// The name a card gets when it is added without one, in the shape the Anker app
// writes so a card added here does not stand out in it.
func TestRfidCardLabel(t *testing.T) {
	if got := rfidCardLabel("4754DC2A"); got != "RFID DC2A" {
		t.Errorf("rfidCardLabel = %q, want %q", got, "RFID DC2A")
	}
	if got := rfidCardLabel("2A"); got != "RFID 2A" {
		t.Errorf("short number: rfidCardLabel = %q, want %q", got, "RFID 2A")
	}
}

func TestParseRfidCards(t *testing.T) {
	body := []byte(`{"code":0,"data":{"list":[
		{"alias_name":"RFID DC2A","card_number":"4754DC2A","create_time":1788187299},
		{"alias_name":"RFID B93F","card_number":"DA7CB93F","create_time":1788187315}]}}`)
	cards, err := parseRfidCards(body)
	if err != nil {
		t.Fatalf("parseRfidCards: %v", err)
	}
	if len(cards) != 2 {
		t.Fatalf("got %d cards, want 2", len(cards))
	}
	if cards[0].Name != "RFID DC2A" || cards[0].Number != "4754DC2A" {
		t.Fatalf("first card = %+v", cards[0])
	}
	if string(cards[0].Added) != "1788187299" {
		t.Fatalf("create_time = %q, want it relayed as it arrived", cards[0].Added)
	}

	// A charger with no cards answers with an empty document, which is an answer
	// and not a failure.
	cards, err = parseRfidCards([]byte(`{"code":0,"data":{}}`))
	if err != nil || len(cards) != 0 {
		t.Fatalf("empty list: got %d cards, err %v", len(cards), err)
	}
}

// Presence is what a write is judged by, and it is judged on the number rather
// than on how either side punctuated it.
func TestCardPresent(t *testing.T) {
	cards := []rfidCard{{Number: "4754DC2A"}, {Number: "DA7CB93F"}}
	if !cardPresent(cards, "47:54:dc:2a") {
		t.Error("punctuated number: want present")
	}
	if cardPresent(cards, "DEADBEEF") {
		t.Error("unknown number: want absent")
	}
	if cardPresent(nil, "4754DC2A") {
		t.Error("empty charger: want absent")
	}
}

// Neither write may reach the cloud without knowing which charger and which
// card: an endpoint nobody has documented is not one to send half a request to.
func TestCardWritesRefuseIncompleteRequests(t *testing.T) {
	p := &Plugin{}
	_ = p.Init(context.Background(), map[string]string{"email": "u@example.com", "password": "p"})

	for _, action := range []string{"rfid-card-save", "rfid-card-delete"} {
		if _, err := p.Invoke(context.Background(), action, nil); err == nil ||
			!strings.Contains(err.Error(), "requires an sn") {
			t.Errorf("%s without a serial: got %v, want an sn-required error", action, err)
		}
		// A serial but no card, and no card number to be found in punctuation
		// either: both stop before anything is sent.
		for _, params := range []string{`{"sn":"EVSN1"}`, `{"sn":"EVSN1","cardNumber":" :- "}`} {
			_, err := p.Invoke(context.Background(), action, json.RawMessage(params))
			if err == nil || !strings.Contains(err.Error(), "card number is required") {
				t.Errorf("%s with %s: got %v, want a card-number-required error", action, params, err)
			}
		}
	}
}
