package loop

import (
	"time"

	"github.com/kami/maven/internal/store"
)

// TickTrace — snapshot of one tick's rule evaluations.
type TickTrace struct {
	Now        time.Time   `json:"now"`
	Winner     string      `json:"winner"` // empty if nothing fired
	RuleTraces []RuleTrace `json:"rules"`
}

// RuleTrace — per-rule evaluation result for one tick.
type RuleTrace struct {
	RuleName        string     `json:"rule_name"`
	Severity        Severity   `json:"severity"`
	PredicateResult bool       `json:"predicate_result"`
	GateResult      bool       `json:"gate_result"`
	GateBlockedBy   string     `json:"gate_blocked_by,omitempty"` // "snooze"|"cooldown"|"quiet_hours"|"calendar_busy"|"presence"|"inert_no_data"|"" (passed)
	GateDetail      GateDetail `json:"gate_detail,omitempty"`
	WasSelected     bool       `json:"was_selected"`
	LostTo          string     `json:"lost_to,omitempty"` // rule that won instead
}

// GateDetail — snapshot of the values the gate checked.
type GateDetail struct {
	SnoozeUntil      *time.Time `json:"snooze_until,omitempty"`
	CooldownUntil    *time.Time `json:"cooldown_until,omitempty"`
	QuietHours       bool       `json:"quiet_hours"`
	CalendarBusy     bool       `json:"calendar_busy"`
	Presence         string     `json:"presence"` // "present"|"away"
	InertKeysMissing []string   `json:"inert_keys_missing,omitempty"`
}

// ExplainGate runs the same checks as Gate() but records the first blocker.
// Returns (passed, blockedBy, detail).
func ExplainGate(s State, r Rule) (bool, string, GateDetail) {
	var d GateDetail
	d.QuietHours = s.QuietHours
	d.CalendarBusy = s.CalendarBusy
	d.Presence = "present"
	if s.Presence == store.Away {
		d.Presence = "away"
	}

	// Snooze
	if until, ok := s.SnoozeUntil[r.Name]; ok && s.Now.Before(until) {
		d.SnoozeUntil = &until
		return false, "snooze", d
	}
	// Cooldown
	if until, ok := s.CooldownUntil[r.Name]; ok && s.Now.Before(until) {
		d.CooldownUntil = &until
		return false, "cooldown", d
	}
	// Quiet hours (care only)
	if s.QuietHours && r.Severity.IsCare() {
		return false, "quiet_hours", d
	}
	// Calendar busy (care only)
	if s.CalendarBusy && r.Severity.IsCare() {
		return false, "calendar_busy", d
	}
	// Presence away (care only)
	if s.Presence == store.Away && r.Severity.IsCare() {
		return false, "presence", d
	}
	// InertWhenNoData
	for _, k := range r.InertWhenNoData {
		if _, ok := s.Fact(k); !ok {
			d.InertKeysMissing = append(d.InertKeysMissing, k)
		}
	}
	if len(d.InertKeysMissing) > 0 {
		return false, "inert_no_data", d
	}

	return true, "", d
}

// ExplainTick evaluates all rules and returns both the best Candidate and
// a full TickTrace explaining every rule's result. Pure: no I/O.
func ExplainTick(s State, rules []Rule) (*Candidate, *TickTrace) {
	trace := &TickTrace{
		Now: s.Now,
	}
	var best *Candidate

	for _, r := range rules {
		tr := RuleTrace{
			RuleName: r.Name,
			Severity: r.Severity,
		}

		// Predicate
		tr.PredicateResult = r.Predicate(s)
		if !tr.PredicateResult {
			tr.GateBlockedBy = "predicate"
			trace.RuleTraces = append(trace.RuleTraces, tr)
			continue
		}

		// Gate
		var passed bool
		passed, tr.GateBlockedBy, tr.GateDetail = ExplainGate(s, r)
		tr.GateResult = passed
		if !passed {
			trace.RuleTraces = append(trace.RuleTraces, tr)
			continue
		}

		// Candidate comparison
		cand := &Candidate{Rule: r, Severity: r.Severity, State: s}
		if best == nil || cand.Severity > best.Severity ||
			(cand.Severity == best.Severity && cand.Rule.Name < best.Rule.Name) {
			if best != nil {
				for i := range trace.RuleTraces {
					if trace.RuleTraces[i].RuleName == best.Rule.Name {
						trace.RuleTraces[i].LostTo = r.Name
						break
					}
				}
			}
			best = cand
			tr.WasSelected = true
		} else {
			tr.LostTo = best.Rule.Name
		}

		trace.RuleTraces = append(trace.RuleTraces, tr)
	}

	if best != nil {
		trace.Winner = best.Rule.Name
	}
	return best, trace
}
