package router

import (
	"strings"
	"unicode"

	"github.com/kami/maven/internal/lexicon"
	"github.com/kami/maven/internal/morph"
)

// ParseNoteCapture extracts the user's note body from a leading capture
// command. It is the durable-write boundary: the router's model may label a
// turn as a note, but it may not rewrite what the notes table holds.
//
// The parser is structural, not a phrase pattern. It tracks Unicode word spans
// in the original utterance, accepts an optional wake word and leading
// particles, then requires one exact imperative from the capture lexicon. It
// removes only that command frame and returns the untouched remainder. A
// capture verb later in an ordinary sentence does not authorize a rewrite.
//
// Russian "что" is removed only when morphology proves that what follows is a
// clause with an inflected verb. When the evidence is ambiguous, as in "что
// такое TCP" or "что купить к ужину", it remains part of the note.
func ParseNoteCapture(utterance string) (string, bool) {
	text := strings.TrimSpace(utterance)
	if stripped, ok := StripWakeToken(text); ok {
		text = stripped
	}
	words := noteCaptureWords(text)
	if len(words) == 0 {
		return "", false
	}

	i := 0
	for i < len(words) && lexicon.IsFillerParticle(words[i].text) {
		i++
	}
	if i >= len(words) || !isCaptureImperative(words[i].text) {
		return "", false
	}

	end := words[i].end
	i++
	for i < len(words) && lexicon.IsCaptureFrameParticle(words[i].text) {
		end = words[i].end
		i++
	}
	if i < len(words) && words[i].text == "что" && hasInflectedClauseVerb(words[i+1:]) {
		end = words[i].end
	}

	body := strings.TrimLeftFunc(text[end:], isNoteCaptureDelimiter)
	if strings.TrimSpace(body) == "" {
		return "", false
	}
	return body, true
}

type noteCaptureWord struct {
	text       string
	start, end int
}

// noteCaptureWords tokenizes only far enough to locate safe cut points. Byte
// offsets keep the returned body in the user's original case and punctuation.
func noteCaptureWords(text string) []noteCaptureWord {
	var out []noteCaptureWord
	start := -1
	for at, r := range text {
		if unicode.IsLetter(r) || unicode.IsDigit(r) {
			if start < 0 {
				start = at
			}
			continue
		}
		if start >= 0 {
			out = append(out, noteCaptureWord{
				text: strings.ToLower(text[start:at]), start: start, end: at,
			})
			start = -1
		}
	}
	if start >= 0 {
		out = append(out, noteCaptureWord{
			text: strings.ToLower(text[start:]), start: start, end: len(text),
		})
	}
	return out
}

func isCaptureImperative(word string) bool {
	for _, candidate := range captureVerbs {
		if word == candidate {
			return true
		}
	}
	return false
}

func hasInflectedClauseVerb(words []noteCaptureWord) bool {
	for _, word := range words {
		if morph.IsVerbForm(word.text) && morph.Lemma(word.text) != word.text {
			return true
		}
	}
	return false
}

func isNoteCaptureDelimiter(r rune) bool {
	if unicode.IsSpace(r) {
		return true
	}
	switch r {
	case ',', ':', ';', '.', '!', '?', '-', '–', '—':
		return true
	default:
		return false
	}
}
