package service import ( "context" "encoding/json" "fmt" "time" "github.com/TopherMayor/unified-media-manager/internal/db" ) // CalendarEvent represents a single event on the calendar. type CalendarEvent struct { ID int64 `json:"id"` MediaType string `json:"media_type"` Title string `json:"title"` Date string `json:"date"` Year *int `json:"year,omitempty"` Status string `json:"status"` PosterURL string `json:"poster_url,omitempty"` } // CalendarService queries monitored media by release date for calendar views. type CalendarService struct { db *db.DB } // NewCalendarService creates a new CalendarService. func NewCalendarService(database *db.DB) *CalendarService { return &CalendarService{db: database} } type posterImage struct { URL string `json:"url"` Type string `json:"type"` } // EventsByMonth returns all monitored media with release dates in the given month. func (s *CalendarService) EventsByMonth(ctx context.Context, year int, month time.Month) ([]CalendarEvent, error) { startDate := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) endDate := startDate.AddDate(0, 1, 0).Add(-time.Nanosecond) query := `SELECT id, media_type, title, release_date, year, status, images FROM media WHERE monitored = true AND deleted_at IS NULL AND release_date IS NOT NULL AND release_date BETWEEN $1 AND $2 ORDER BY release_date` rows, err := s.db.Pool.Query(ctx, query, startDate, endDate) if err != nil { return nil, fmt.Errorf("query calendar events: %w", err) } defer rows.Close() var events []CalendarEvent for rows.Next() { var id int64 var mediaType, title, status string var releaseDate time.Time var yearVal *int var imagesJSON []byte if err := rows.Scan(&id, &mediaType, &title, &releaseDate, &yearVal, &status, &imagesJSON); err != nil { continue } posterURL := extractPosterURL(imagesJSON) events = append(events, CalendarEvent{ ID: id, MediaType: mediaType, Title: title, Date: releaseDate.Format("2006-01-02"), Year: yearVal, Status: status, PosterURL: posterURL, }) } if events == nil { events = []CalendarEvent{} } return events, nil } // extractPosterURL parses the images JSONB and returns the first poster URL. func extractPosterURL(imagesJSON []byte) string { if len(imagesJSON) == 0 { return "" } var images []posterImage if err := json.Unmarshal(imagesJSON, &images); err != nil { return "" } for _, img := range images { if img.Type == "poster" && img.URL != "" { return img.URL } } return "" }