package router

import "testing"

func TestParseTaskStatus(t *testing.T) {
	cases := []struct {
		utterance string
		ok        bool
		status    string
		text      string
	}{
		// The shapes that reached nothing before this rule.
		{"закрой задачу купить молоко", true, "done", "купить молоко"},
		{"задачу купить молоко закрой", true, "done", "купить молоко"},
		{"убери из задач купить молоко", true, "dropped", "купить молоко"},
		{"убери из моих задач купить молоко", true, "dropped", "купить молоко"},
		{"отмени задачу оплатить интернет", true, "dropped", "оплатить интернет"},
		{"please close task buy milk", true, "done", "buy milk"},
		{"не мог бы ты закрыть задачу купить молоко?", true, "done", "купить молоко"},
		// Referents come from the bounded command frame. Interior prepositions
		// remain part of a real title; marker verbs and topic prepositions do not.
		{"закрой задачу сходить в банк", true, "done", "сходить в банк"},
		{"mark task about backups as done", true, "done", "backups"},
		// Question-shaped prose containing the same change word is not an
		// instruction. This parser feeds a mutating stage-0 decision, so its
		// cautious direction is to decline and let the query cascade answer.
		{"как отменить задачу купить хлеб?", false, "", ""},
		{"как отменить задачу купить хлеб", false, "", ""},
		{"отменить задачу купить хлеб? пожалуйста", false, "", ""},
		{"how do I cancel task buy bread", false, "", ""},
		{"что будет если удалить задачу купить хлеб", false, "", ""},
		{"объясни как закрыть задачу купить хлеб", false, "", ""},
		// Capture verbs normally outrank question shape. They do not get that
		// exception here, because this mutating grammar runs before capture.
		{"запиши как отменить задачу купить хлеб", false, "", ""},
		{"отметь как отменить задачу купить хлеб", false, "", ""},
		// The established state-marker command is not a question: как means
		// "as" inside a closed marker frame here.
		{"отметь задачу про бэкапы как сделано", true, "done", "бэкапы"},
		{"отметь задачу про бэкапы как сделанную", true, "done", "бэкапы"},
		// A marker-shaped verb can itself belong to the title. Frame position,
		// not global word subtraction, decides which occurrence is control.
		{"отметь задачу отметить выходные как сделанную", true, "done", "отметить выходные"},
		{"задачу отметить выходные отметь как сделанную", true, "done", "отметить выходные"},
		// Token boundaries matter in both directions. Interrogative roots inside
		// a word or an indefinite compound do not turn a direct command into a
		// question, and an interrogative in the already-commanded task title is
		// part of its name.
		{"отмени задачу купить какао", true, "dropped", "купить какао"},
		{"закрой задачу чтобы купить хлеб", true, "done", "чтобы купить хлеб"},
		{"отмени задачу купить что-нибудь", true, "dropped", "купить что-нибудь"},
		{"отмени задачу узнать когда рейс", true, "dropped", "узнать когда рейс"},
		// The referent may be missing. The turn is still his, and the daemon has
		// the list to ask about.
		{"закрой задачу", true, "done", ""},
		{"убери задачу", true, "dropped", ""},
		// No board noun: ordinary speech, and every one of these means something
		// else. "закрой" alone belongs to Praxis.
		{"готово", false, "", ""},
		{"закрой", false, "", ""},
		{"убери со стола", false, "", ""},
		{"я всё сделал", false, "", ""},
		{"закрой шторы в комнате", false, "", ""},
		// Reports, plans and desires name a transition but do not address Maven.
		// The old mixed-mood accessor lemma-matched every one into a write.
		{"я закрыл задачу купить молоко", false, "", ""},
		{"я отменил задачу купить молоко", false, "", ""},
		{"I closed the task buy milk", false, "", ""},
		{"I cancelled the task buy milk", false, "", ""},
		{"надо закрыть задачу купить молоко", false, "", ""},
		{"я хочу закрыть задачу купить молоко", false, "", ""},
		{"I want to close task buy milk", false, "", ""},
		// Direct negative authority and the ambiguous don't-forget board idiom
		// fail closed before any transition slot is exposed.
		{"не закрывай задачу купить молоко", false, "", ""},
		{"не закрой задачу купить молоко", false, "", ""},
		{"не закрыть задачу купить молоко", false, "", ""},
		{"don't close task buy milk", false, "", ""},
		{"never cancel task buy milk", false, "", ""},
		{"не забудь закрыть задачу купить молоко", false, "", ""},
		// The board noun with no status word is a list query, not a move.
		{"какие у меня задачи", false, "", ""},
		{"добавь в задачи купить молоко", false, "", ""},
		// Two transitions in one sentence. Asking beats picking.
		{"задачу купить молоко готово убери", false, "", ""},
		{"", false, "", ""},
	}
	for _, c := range cases {
		got, ok := ParseTaskStatus(c.utterance)
		if ok != c.ok {
			t.Errorf("ParseTaskStatus(%q) ok = %v, want %v", c.utterance, ok, c.ok)
			continue
		}
		if !ok {
			continue
		}
		if got.Status != c.status || got.Text != c.text {
			t.Errorf("ParseTaskStatus(%q) = %+v, want status %q text %q", c.utterance, got, c.status, c.text)
		}
	}
}

func TestTaskStatusGrammarFillsTheFnSlot(t *testing.T) {
	g := TaskStatusGrammar()
	dec, matched, ok := g.Evaluate("закрой задачу купить молоко")
	if !matched || !ok {
		t.Fatal("structural grammar declined")
	}
	if dec.Intent != IntentAct || !dec.Slots.HasFn || dec.Slots.Fn != TaskStatusFn {
		t.Fatalf("decision = %+v, want act with fn %q", dec, TaskStatusFn)
	}
	if dec.Slots.Value != "done" || dec.Slots.Text != "купить молоко" {
		t.Fatalf("slots = %+v, want value done text \"купить молоко\"", dec.Slots)
	}
}

func TestTaskStatusGrammarAcceptsInflectedDoneMarker(t *testing.T) {
	g := TaskStatusGrammar()
	dec, matched, accepted := g.Evaluate("отметь задачу про бэкапы как сделанную")
	if !matched || !accepted {
		t.Fatalf("inflected state-marker command matched=%v accepted=%v", matched, accepted)
	}
	if dec.Intent != IntentAct || dec.Slots.Fn != TaskStatusFn || dec.Slots.Value != "done" || dec.Slots.Text != "бэкапы" {
		t.Fatalf("decision = %+v, want task_status/done with the backup task referent", dec)
	}
}

func TestTaskStatusGrammarDeclinesQuestionsWithoutMutationSlots(t *testing.T) {
	g := TaskStatusGrammar()
	for _, utterance := range []string{
		"как отменить задачу купить хлеб?",
		"how do I drop task buy bread",
		"запиши как отменить задачу купить хлеб",
	} {
		dec, matched, accepted := g.Evaluate(utterance)
		if matched || accepted {
			t.Errorf("TaskStatusGrammar(%q) matched=%v accepted=%v; a structural refusal must reach the query cascade", utterance, matched, accepted)
		}
		if dec.Slots.HasFn || dec.Slots.Fn != "" {
			t.Errorf("TaskStatusGrammar(%q) exposed mutation slots: %+v", utterance, dec.Slots)
		}
	}
}
