// Package pattern extracts normalized events from facts and detects recurring
// patterns to propose as routines (recurring reminders).
//
// The pipeline: fact write → extractor (action+object) → detector (intervals) →
// proposed routine → human confirms → recurring reminder.
package pattern

import (
	"strings"
	"time"
)

// Event is a normalized, derived observation — the output of the extractor
// and the input to the pattern detector.
type Event struct {
	FactID int64
	Action string // normalized action, e.g. "refill"
	Object string // normalized object, e.g. "cat_water_fountain"
	Ts     time.Time
}

// actionLexicon maps observed value words to canonical actions. The key is the
// canonical form; the values are observed inflections/alternatives (lowercase).
// Pure additive — unrecognized values silently produce no event (false
// negative), which is harmless.
var actionLexicon = map[string][]string{
	"refill": {"refilled", "refill", "fills", "filling", "заправил", "налил", "долил", "пополнил"},
	"feed":   {"fed", "feed", "feeds", "feeding", "покормил", "кормил", "покормить"},
	"change": {"changed", "change", "changes", "changing", "поменял", "сменил", "заменил"},
	"clean":  {"cleaned", "clean", "cleans", "cleaning", "почистил", "убрал", "убирал", "помыл", "мыл"},
	"take":   {"took", "take", "takes", "taking", "принял", "выпил", "пил", "съел", "ел"},
	"walk":   {"walked", "walk", "walks", "walking", "гулял", "выгулял", "прогулка"},
	"water":  {"watered", "water", "waters", "watering", "полил", "поливал"},
}

// invertLexicon builds a fast map from any variant → canonical action.
var variantToAction map[string]string

func init() {
	variantToAction = make(map[string]string)
	for canonical, variants := range actionLexicon {
		for _, v := range variants {
			variantToAction[v] = canonical
		}
	}
}

// Extract attempts to normalize a fact (key + value) into an Event.
// Returns nil when the fact doesn't describe a recognizable action —
// structured JSON values, empty values, and unrecognized actions all
// produce no event (false negative by design).
//
// Rules:
//   - If value starts with '{' or '[' (JSON), skip — it's structured data,
//     not an action statement.
//   - The value (trimmed, lowered) is looked up in the action lexicon.
//   - The key (trimmed, lowered) becomes the object.
//   - Both action and object must be non-empty.
func Extract(factID int64, key, value string, ts time.Time) *Event {
	v := strings.TrimSpace(value)
	if v == "" {
		return nil
	}
	// Skip structured JSON values — measurements, config, etc.
	if v[0] == '{' || v[0] == '[' {
		return nil
	}

	action, ok := variantToAction[strings.ToLower(v)]
	if !ok {
		return nil
	}

	object := strings.TrimSpace(strings.ToLower(key))
	if object == "" {
		return nil
	}

	return &Event{
		FactID: factID,
		Action: action,
		Object: object,
		Ts:     ts,
	}
}
