package reporting import ( "context" "time" "gorm.io/gorm" ) // ReportingService provides CRUD and aggregation queries for reporting events and rollups. // Reference: Chatwoot Reports::ReportingEventService + Reports::ReportingEventsRollupService type ReportingService struct { db *gorm.DB } func NewReportingService(db *gorm.DB) *ReportingService { return &ReportingService{db: db} } // CreateEvent records a new reporting event. func (s *ReportingService) CreateEvent(ctx context.Context, event *ReportingEvent) error { return s.db.WithContext(ctx).Create(event).Error } // GetEvent retrieves a single reporting event by ID. func (s *ReportingService) GetEvent(ctx context.Context, id uint) (*ReportingEvent, error) { var event ReportingEvent if err := s.db.WithContext(ctx).First(&event, id).Error; err != nil { return nil, err } return &event, nil } // ListEventsByAccount returns all reporting events for an account within a time range. func (s *ReportingService) ListEventsByAccount(ctx context.Context, accountID uint, since, until time.Time) ([]ReportingEvent, error) { var events []ReportingEvent err := s.db.WithContext(ctx).Where("account_id = ? AND event_start_time >= ? AND event_start_time <= ?", accountID, since, until). Find(&events).Error return events, err } // ListEventsByAccountAndName returns events filtered by account, metric name, and time range. func (s *ReportingService) ListEventsByAccountAndName(ctx context.Context, accountID uint, name string, since, until time.Time) ([]ReportingEvent, error) { var events []ReportingEvent err := s.db.WithContext(ctx).Where("account_id = ? AND name = ? AND event_start_time >= ? AND event_start_time <= ?", accountID, name, since, until). Find(&events).Error return events, err } // CreateRollup records a rollup aggregation row. Uses upsert to handle the unique index constraint. func (s *ReportingService) CreateRollup(ctx context.Context, rollup *ReportingEventsRollup) error { return s.db.WithContext(ctx).Where("account_id = ? AND date = ? AND dimension_type = ? AND dimension_id = ? AND metric = ?", rollup.AccountID, rollup.Date, rollup.DimensionType, rollup.DimensionID, rollup.Metric). Assign(map[string]interface{}{ "count": rollup.Count, "sum_value": rollup.SumValue, "sum_value_business_hours": rollup.SumValueBusinessHours, }).FirstOrCreate(rollup).Error } // GetRollups returns rollup records for an account within a date range. func (s *ReportingService) GetRollups(ctx context.Context, accountID uint, dimensionType DimensionType, dimensionID uint, since, until time.Time) ([]ReportingEventsRollup, error) { var rollups []ReportingEventsRollup err := s.db.WithContext(ctx).Where("account_id = ? AND dimension_type = ? AND dimension_id = ? AND date >= ? AND date <= ?", accountID, dimensionType, dimensionID, since, until). Find(&rollups).Error return rollups, err } // GetRollupsByMetric returns rollup records for a specific metric. func (s *ReportingService) GetRollupsByMetric(ctx context.Context, accountID uint, dimensionType DimensionType, dimensionID uint, metric RollupMetric, since, until time.Time) ([]ReportingEventsRollup, error) { var rollups []ReportingEventsRollup err := s.db.WithContext(ctx).Where("account_id = ? AND dimension_type = ? AND dimension_id = ? AND metric = ? AND date >= ? AND date <= ?", accountID, dimensionType, dimensionID, metric, since, until). Find(&rollups).Error return rollups, err } // DeleteRollupsByDate removes rollup records for a given date range (for re-computation). func (s *ReportingService) DeleteRollupsByDate(ctx context.Context, accountID uint, since, until time.Time) error { return s.db.WithContext(ctx).Where("account_id = ? AND date >= ? AND date <= ?", accountID, since, until). Delete(&ReportingEventsRollup{}).Error }