package store

import (
	"context"
	"fmt"
	"time"
)

// Event — a normalized, derived observation from a fact. An event has an action
// (what happened, e.g. "refill") and an object (what it happened to, e.g.
// "cat_water_fountain"). Events are lossy — not every fact produces one.
// They are the input to the pattern detector.
type Event struct {
	ID     int64
	FactID int64
	Action string
	Object string
	Ts     time.Time
}

// CreateEvent persists a derived event from a fact. Best-effort: the caller
// (the pattern extractor) decides whether the fact warrants an event; this
// just writes the row. Not every fact produces an event.
func (s *Store) CreateEvent(ctx context.Context, factID int64, action, object string, ts time.Time) (int64, error) {
	res, err := s.db.ExecContext(ctx,
		`INSERT INTO events (fact_id, action, object, ts) VALUES (?,?,?,?)`,
		factID, action, object, ts.UnixMilli())
	if err != nil {
		return 0, fmt.Errorf("create event: %w", err)
	}
	id, err := res.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("create event: last insert id: %w", err)
	}
	return id, nil
}

// EventPair identifies one action+object grouping in the events table — the
// unit the pattern detector reasons about.
type EventPair struct {
	Action string
	Object string
}

// DistinctEventPairs returns every distinct action+object pair that has at
// least one event, in no particular order. This is what lets the proactive
// digestion tick run the pattern detector over everything accumulated so far
// instead of only the pair touched by the utterance that just landed
// (Vikunja #43) — the tick has no "current utterance," so it has to ask the
// store what to look at.
func (s *Store) DistinctEventPairs(ctx context.Context) ([]EventPair, error) {
	rows, err := s.db.QueryContext(ctx, `SELECT DISTINCT action, object FROM events`)
	if err != nil {
		return nil, fmt.Errorf("distinct event pairs: %w", err)
	}
	defer rows.Close()
	var out []EventPair
	for rows.Next() {
		var p EventPair
		if err := rows.Scan(&p.Action, &p.Object); err != nil {
			return nil, err
		}
		out = append(out, p)
	}
	return out, rows.Err()
}

// EventsFor returns all events matching action+object, ordered by ts ascending
// (oldest first — the order the pattern detector needs for interval computation).
func (s *Store) EventsFor(ctx context.Context, action, object string) ([]Event, error) {
	rows, err := s.db.QueryContext(ctx, `
		SELECT id, fact_id, action, object, ts
		  FROM events
		 WHERE action = ? AND object = ?
		 ORDER BY ts ASC, id ASC`, action, object)
	if err != nil {
		return nil, fmt.Errorf("events for %s/%s: %w", action, object, err)
	}
	defer rows.Close()
	var out []Event
	for rows.Next() {
		var e Event
		var tsMillis int64
		if err := rows.Scan(&e.ID, &e.FactID, &e.Action, &e.Object, &tsMillis); err != nil {
			return nil, err
		}
		e.Ts = time.UnixMilli(tsMillis).UTC()
		out = append(out, e)
	}
	return out, rows.Err()
}
