package domain import ( "errors" "strings" contract "git.cin.su/m/Assistant/contracts/continuity" ) var ( ErrProspectiveNotFound = errors.New("prospective item not found") ErrProspectiveRevision = errors.New("prospective item revision conflict") ErrInvalidTransition = errors.New("invalid prospective item transition") ErrInvalidSatisfaction = errors.New("invalid delivered satisfaction command") ErrInvalidReminder = errors.New("invalid reminder request") ErrInvalidAttention = errors.New("invalid attention item") ) type Store struct { items map[contract.ProspectiveItemID]contract.ProspectiveItem } func ValidateSatisfyDelivered(command contract.SatisfyDeliveredCommand) error { if command.ActionIntentID == "" || command.AuthorizedActionID == "" || command.OwnerPrincipalID == "" || command.Subject.ProspectiveItemID == "" || command.Subject.ExpectedRevision == 0 || command.Subject.FromState != contract.ProspectiveDelivered || command.Subject.ToState != contract.ProspectiveSatisfied || strings.TrimSpace(command.Subject.EvidenceRef) == "" { return ErrInvalidSatisfaction } return nil } func BuildReminder(request contract.ScheduleReminderRequest, id contract.ProspectiveItemID) (contract.ProspectiveItem, error) { if id == "" || request.OwnerPrincipalID == "" || request.ScopeID == "" || strings.TrimSpace(request.SourceIntentID) == "" || strings.TrimSpace(request.SourceUtteranceID) == "" || strings.TrimSpace(request.Summary) == "" || request.TriggerAtUnixMillis <= request.RequestedAtUnixMillis || request.RequestedAtUnixMillis < 1 { return contract.ProspectiveItem{}, ErrInvalidReminder } return contract.ProspectiveItem{ ID: id, CommitmentID: contract.CommitmentID(""), OwnerPrincipalID: request.OwnerPrincipalID, ScopeID: request.ScopeID, GoalID: "", SourceIntentID: strings.TrimSpace(request.SourceIntentID), SourceUtteranceID: strings.TrimSpace(request.SourceUtteranceID), Summary: strings.TrimSpace(request.Summary), TriggerAtUnixMillis: request.TriggerAtUnixMillis, State: contract.ProspectiveArmed, Revision: 1, }, nil } func BuildDueReminderAttention(item contract.ProspectiveItem, id contract.AttentionItemID, nowUnixMillis int64) (contract.AttentionItem, error) { if id == "" || item.ID == "" || item.OwnerPrincipalID == "" || item.ScopeID == "" || strings.TrimSpace(item.Summary) == "" || item.TriggerAtUnixMillis < 1 || nowUnixMillis < 1 { return contract.AttentionItem{}, ErrInvalidAttention } if item.State != contract.ProspectiveDelivered && item.State != contract.ProspectiveRevalidating { return contract.AttentionItem{}, ErrInvalidAttention } return contract.AttentionItem{ ID: id, OwnerPrincipalID: item.OwnerPrincipalID, ScopeID: item.ScopeID, SourceKind: contract.AttentionSourceProspectiveReminder, SourceProspectiveID: item.ID, Level: contract.AttentionDue, State: contract.AttentionPending, Summary: item.Summary, DueAtUnixMillis: item.TriggerAtUnixMillis, CreatedAtUnixMillis: nowUnixMillis, Revision: 1, }, nil } func (s *Store) SatisfyDelivered(id contract.ProspectiveItemID, revision contract.ProspectiveRevision, evidenceRef string) (contract.ProspectiveItem, error) { item, err := s.current(id, revision) if err != nil { return contract.ProspectiveItem{}, err } if item.State != contract.ProspectiveDelivered || strings.TrimSpace(evidenceRef) == "" { return contract.ProspectiveItem{}, ErrInvalidTransition } item.State = contract.ProspectiveSatisfied item.Revision++ s.items[id] = item return item, nil } func Recover(items []contract.ProspectiveItem) Store { stored := make(map[contract.ProspectiveItemID]contract.ProspectiveItem, len(items)) for _, item := range items { stored[item.ID] = item } return Store{items: stored} } func (s *Store) Snapshot() []contract.ProspectiveItem { items := make([]contract.ProspectiveItem, 0, len(s.items)) for _, item := range s.items { items = append(items, item) } return items } func (s *Store) Trigger(id contract.ProspectiveItemID, revision contract.ProspectiveRevision) (contract.ProspectiveItem, error) { item, err := s.current(id, revision) if err != nil { return contract.ProspectiveItem{}, err } if item.State != contract.ProspectiveArmed { return contract.ProspectiveItem{}, ErrInvalidTransition } item.State = contract.ProspectiveTriggered item.Revision++ s.items[id] = item return item, nil } func (s *Store) BeginRevalidation(id contract.ProspectiveItemID, revision contract.ProspectiveRevision) (contract.ProspectiveItem, error) { item, err := s.current(id, revision) if err != nil { return contract.ProspectiveItem{}, err } if item.State != contract.ProspectiveTriggered { return contract.ProspectiveItem{}, ErrInvalidTransition } item.State = contract.ProspectiveRevalidating item.Revision++ s.items[id] = item return item, nil } func (s *Store) FinishRevalidation(id contract.ProspectiveItemID, revision contract.ProspectiveRevision, relevant bool) (contract.ProspectiveItem, error) { item, err := s.current(id, revision) if err != nil { return contract.ProspectiveItem{}, err } if item.State != contract.ProspectiveRevalidating { return contract.ProspectiveItem{}, ErrInvalidTransition } if relevant { item.State = contract.ProspectiveDelivered } else { item.State = contract.ProspectiveSuppressed } item.Revision++ s.items[id] = item return item, nil } func (s *Store) current(id contract.ProspectiveItemID, revision contract.ProspectiveRevision) (contract.ProspectiveItem, error) { item, ok := s.items[id] if !ok { return contract.ProspectiveItem{}, ErrProspectiveNotFound } if item.Revision != revision { return contract.ProspectiveItem{}, ErrProspectiveRevision } return item, nil }