package action import ( "sort" "github.com/cnachtigall/heatwave-autopilot/internal/heat" "github.com/cnachtigall/heatwave-autopilot/internal/risk" ) // Matches checks if an action's conditions are met for a given hour context. func Matches(a Action, ctx HourContext) bool { w := a.When // Hour range check (only if at least one is non-zero) if w.HourFrom != 0 || w.HourTo != 0 { if ctx.Hour < w.HourFrom || ctx.Hour > w.HourTo { return false } } // Temperature threshold if w.MinTempC > 0 && ctx.TempC < w.MinTempC { return false } if w.MaxTempC > 0 && ctx.TempC > w.MaxTempC { return false } // Night only if w.NightOnly && ctx.IsDay { return false } // Risk level if w.MinRisk != "" { required := parseRiskLevel(w.MinRisk) if ctx.RiskLevel < required { return false } } // Budget status if w.BudgetStatus != "" { required := parseBudgetStatus(w.BudgetStatus) if ctx.BudgetStatus < required { return false } } // High humidity if w.HighHumidity && ctx.HumidityPct <= 70 { return false } return true } // SelectActions returns all matching actions for a given hour, sorted by priority. func SelectActions(actions []Action, ctx HourContext) []Action { var matched []Action for _, a := range actions { if Matches(a, ctx) { matched = append(matched, a) } } sort.Slice(matched, func(i, j int) bool { return priority(matched[i]) > priority(matched[j]) }) return matched } // priority scores an action: impact * 10 + (4 - effort) func priority(a Action) int { return impactScore(a.Impact)*10 + (4 - effortScore(a.Effort)) } func impactScore(i Impact) int { switch i { case ImpactHigh: return 3 case ImpactMedium: return 2 case ImpactLow: return 1 default: return 0 } } func effortScore(e Effort) int { switch e { case EffortNone: return 0 case EffortLow: return 1 case EffortMedium: return 2 case EffortHigh: return 3 default: return 0 } } func parseRiskLevel(s string) risk.RiskLevel { switch s { case "moderate": return risk.Moderate case "high": return risk.High case "extreme": return risk.Extreme default: return risk.Low } } func parseBudgetStatus(s string) heat.BudgetStatus { switch s { case "marginal": return heat.Marginal case "overloaded": return heat.Overloaded default: return heat.Comfortable } }