package router

import "testing"

func TestParseListCaptureReadsListAndItem(t *testing.T) {
	cases := []struct {
		utterance string
		list      string
		item      string
	}{
		{"добавь в список покупок молоко", "покупки", "молоко"},
		{"добавь в список молоко", "покупки", "молоко"},
		{"Добавь в покупки хлеб и яйца", "покупки", "хлеб и яйца"},
		{"запиши в список аптеки бинт", "аптека", "бинт"},
		{"добавь в список хозяйства лампочки.", "хозяйство", "лампочки"},
		{"add to the shopping list milk", "покупки", "milk"},
	}
	for _, c := range cases {
		got, ok := ParseListCapture(c.utterance)
		if !ok {
			t.Errorf("ParseListCapture(%q) did not claim it", c.utterance)
			continue
		}
		if got.List != c.list || got.Item != c.item {
			t.Errorf("ParseListCapture(%q) = %+v; want list %q item %q", c.utterance, got, c.list, c.item)
		}
	}
}

// A marker with no item is not a capture, and an utterance that only mentions
// shopping is not one either.
func TestParseListCapturePasses(t *testing.T) {
	for _, u := range []string{
		"добавь в список покупок",
		"добавь в список",
		"молоко закончилось",
		"надо бы съездить в магазин",
		"добавь в задачи купить молоко",
	} {
		if got, ok := ParseListCapture(u); ok {
			t.Errorf("ParseListCapture(%q) claimed it as %+v", u, got)
		}
	}
}

func TestParseListQueryNamesTheList(t *testing.T) {
	cases := []struct{ utterance, list string }{
		{"что в списке покупок?", "покупки"},
		{"что в списке", "покупки"},
		{"что мне купить", "покупки"},
		{"покажи список аптеки", "аптека"},
		{"what's on the list", "покупки"},
	}
	for _, c := range cases {
		list, ok := ParseListQuery(c.utterance)
		if !ok {
			t.Errorf("ParseListQuery(%q) did not claim it", c.utterance)
			continue
		}
		if list != c.list {
			t.Errorf("ParseListQuery(%q) = %q; want %q", c.utterance, list, c.list)
		}
	}
	if _, ok := ParseListQuery("какие у меня задачи"); ok {
		t.Error("ParseListQuery claimed a task question")
	}
}

func TestParseListClearAndRemove(t *testing.T) {
	if list, ok := ParseListClear("всё купил"); !ok || list != "покупки" {
		t.Errorf("ParseListClear = %q, %v; want покупки, true", list, ok)
	}
	if list, ok := ParseListClear("очисти список аптеки"); !ok || list != "аптека" {
		t.Errorf("ParseListClear = %q, %v; want аптека, true", list, ok)
	}
	if _, ok := ParseListClear("купил молоко"); ok {
		t.Error("ParseListClear claimed a single item")
	}
	got, ok := ParseListRemove("вычеркни молоко")
	if !ok || got.Item != "молоко" || got.List != "покупки" {
		t.Errorf("ParseListRemove = %+v, %v; want молоко on покупки", got, ok)
	}
	if got, ok := ParseListRemove("убери из списка аптеки бинт"); !ok || got.Item != "бинт" || got.List != "аптека" {
		t.Errorf("ParseListRemove = %+v, %v; want бинт on аптека", got, ok)
	}
	if _, ok := ParseListRemove("вычеркни"); ok {
		t.Error("ParseListRemove claimed a marker with no item")
	}
}

// TestListTagIsAWordNotAPrefix — "покуп" was a stem matched with HasPrefix, so
// every word starting with it read as the shopping list (Vikunja #529). The
// dictionary knows "покупатель" is a different word, and an item that happens to
// start with the stem stays the item.
func TestListTagIsAWordNotAPrefix(t *testing.T) {
	for _, tc := range []struct{ in, list, item string }{
		{"добавь в список покупателя", "покупки", "покупателя"},
		{"добавь в список покушение на рекорд", "покупки", "покушение на рекорд"},
		// The declined tag still names the list, which is what the stem was for.
		{"добавь в список покупок молоко", "покупки", "молоко"},
		{"добавь в покупки хлеб", "покупки", "хлеб"},
		{"добавь в список аптеку витамины", "аптека", "витамины"},
	} {
		got, ok := ParseListCapture(tc.in)
		if !ok || got.List != tc.list || got.Item != tc.item {
			t.Errorf("ParseListCapture(%q) = (%q, %q, %v), want (%q, %q, true)",
				tc.in, got.List, got.Item, ok, tc.list, tc.item)
		}
	}
}

// TestNamedTaskListIsNotAGroceryItem — "добавь в список" is a marker on both
// sides, and ListGrammars is wired before TaskCaptureGrammar, so the list used
// to claim every one of them (Vikunja #520). takeListTag does not know "дел" as
// a list name, so the item filed was "дел хлеб". The bare marker stays a
// grocery item; a named task list falls through to task capture.
func TestNamedTaskListIsNotAGroceryItem(t *testing.T) {
	for _, in := range []string{
		"добавь в список дел хлеб",
		"добавь в список задач позвонить врачу",
		"запиши в список дел купить молоко",
		"add to the list of todos milk",
	} {
		if got, ok := ParseListCapture(in); ok {
			t.Errorf("ParseListCapture(%q) claimed it as list %q item %q, want a pass to task capture",
				in, got.List, got.Item)
		}
	}
	// The bare form is still the grocery list, and the tagged forms are untouched.
	for _, tc := range []struct{ in, list, item string }{
		{"добавь в список хлеб", "покупки", "хлеб"},
		{"добавь в список покупок молоко", "покупки", "молоко"},
		{"добавь в список аптеку витамины", "аптека", "витамины"},
	} {
		got, ok := ParseListCapture(tc.in)
		if !ok || got.List != tc.list || got.Item != tc.item {
			t.Errorf("ParseListCapture(%q) = (%q, %q, %v), want (%q, %q, true)",
				tc.in, got.List, got.Item, ok, tc.list, tc.item)
		}
	}
	// A read-back and a cross-off name the same lists and must split the same way.
	if _, ok := ParseListQuery("покажи список дел"); ok {
		t.Error(`ParseListQuery("покажи список дел") claimed a task list`)
	}
	if got, ok := ParseListRemove("вычеркни из списка дел хлеб"); ok {
		t.Errorf("ParseListRemove claimed a task list: %q / %q", got.List, got.Item)
	}
}
