package core

import (
	"context"
	"io"
	"time"

	"github.com/navidrome/navidrome/conf"
	"github.com/navidrome/navidrome/log"
	"github.com/navidrome/navidrome/utils/pool"
)

type CacheWarmer interface {
	AddAlbum(ctx context.Context, albumID string)
	Flush(ctx context.Context)
}

func NewCacheWarmer(artwork Artwork, artworkCache ArtworkCache) CacheWarmer {
	w := &warmer{
		artwork:      artwork,
		artworkCache: artworkCache,
		albums:       map[string]struct{}{},
	}
	p, err := pool.NewPool("artwork", 3, w.execute)
	if err != nil {
		log.Error(context.Background(), "Error creating pool for Album Artwork Cache Warmer", err)
	} else {
		w.pool = p
	}

	return w
}

type warmer struct {
	pool         *pool.Pool
	artwork      Artwork
	artworkCache ArtworkCache
	albums       map[string]struct{}
}

func (w *warmer) AddAlbum(ctx context.Context, albumID string) {
	if albumID == "" {
		return
	}
	w.albums[albumID] = struct{}{}
}

func (w *warmer) waitForCacheReady(ctx context.Context) {
	for !w.artworkCache.Ready(ctx) {
		time.Sleep(time.Second)
	}
}

func (w *warmer) Flush(ctx context.Context) {
	if conf.Server.DevPreCacheAlbumArtwork {
		w.waitForCacheReady(ctx)
		if w.artworkCache.Available(ctx) {
			if w.pool == nil || len(w.albums) == 0 {
				return
			}
			log.Info(ctx, "Pre-caching album artworks", "numAlbums", len(w.albums))
			for id := range w.albums {
				w.pool.Submit(artworkItem{albumID: id})
			}
		} else {
			log.Warn(ctx, "Cache warmer is not available as ImageCache is DISABLED")
		}
	}
	w.albums = map[string]struct{}{}
}

func (w *warmer) execute(workload interface{}) {
	ctx := context.Background()
	item := workload.(artworkItem)
	log.Trace(ctx, "Pre-caching album artwork", "albumID", item.albumID)
	r, err := w.artwork.Get(ctx, item.albumID, 0)
	if err != nil {
		log.Warn("Error pre-caching artwork from album", "id", item.albumID, err)
		return
	}
	defer r.Close()
	_, _ = io.Copy(io.Discard, r)
}

type artworkItem struct {
	albumID string
}