package core

import (
	"context"
	"io/ioutil"
	"time"

	"github.com/deluan/navidrome/conf"
	"github.com/deluan/navidrome/core/pool"
	"github.com/deluan/navidrome/log"
)

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, &artworkItem{}, 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) {
	tick := time.NewTicker(time.Second)
	defer tick.Stop()
	for {
		<-tick.C
		if w.artworkCache.Ready(ctx) {
			return
		}
	}
}

func (w *warmer) Flush(ctx context.Context) {
	w.waitForCacheReady(ctx)
	if w.artworkCache.Available(ctx) {
		if conf.Server.DevPreCacheAlbumArtwork {
			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, "Pre-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)
	err := w.artwork.Get(ctx, item.albumID, 0, ioutil.Discard)
	if err != nil {
		log.Warn("Error pre-caching artwork from album", "id", item.albumID, err)
	}
}

type artworkItem struct {
	albumID string
}