package httpapi

import (
	"context"
	"log"
	"sync"

	"git.azzamo.net/Michilis/Spanglish/photo-api/internal/store"
)

// statConcurrency bounds the one-off Stat burst below; a gallery of ~70
// photos then costs a handful of round-trips against S3, once ever.
const statConcurrency = 8

// fillPreviewSizes measures the preview objects of photos whose
// preview_size_bytes is still unset and writes the result back, updating the
// slice in place.
//
// The worker records the size when it generates the preview, so this only
// ever fires for rows that predate the column (or arrived via `photo-api
// sync`). It is a self-healing cache fill rather than a migration step: the
// first gallery view after deploy pays for one Stat per photo, every view
// after that reads the stored value. Errors are logged and swallowed — a
// missing size costs the save sheet its row label, which is not worth
// failing a gallery response over.
func (s *Server) fillPreviewSizes(ctx context.Context, photos []store.Photo) {
	var (
		wg   sync.WaitGroup
		sem  = make(chan struct{}, statConcurrency)
		mu   sync.Mutex
		size = make(map[string]int64, len(photos))
	)
	for _, p := range photos {
		if p.PreviewKey == "" || p.PreviewSizeBytes > 0 {
			continue
		}
		wg.Add(1)
		go func(p store.Photo) {
			defer wg.Done()
			sem <- struct{}{}
			defer func() { <-sem }()
			n, err := s.storage.Stat(ctx, p.PreviewKey)
			if err != nil {
				log.Printf("preview size %s: %v", p.PreviewKey, err)
				return
			}
			mu.Lock()
			size[p.ID] = n
			mu.Unlock()
		}(p)
	}
	wg.Wait()
	if len(size) == 0 {
		return
	}
	for i := range photos {
		if n, ok := size[photos[i].ID]; ok {
			photos[i].PreviewSizeBytes = n
			if err := s.db.SetPreviewSize(ctx, photos[i].ID, n); err != nil {
				log.Printf("store preview size %s: %v", photos[i].ID, err)
			}
		}
	}
}
