package gallery

import (
	"bytes"
	"context"
	"fmt"
	"hash/fnv"
	"log/slog"
	"os"
	"path"
	"strings"
	"time"

	"github.com/valkey-io/valkey-go"
)

type Image struct {
	UID    string
	X      int
	Y      int
	Width  int
	Height int
}

const (
	imagesKey = "gallery:images:list"
	hashKey   = "gallery:images:hash"
)

func (s *Service) autoRefresh() {
	t := time.NewTicker(time.Minute)
	for {
		ctx, cancle := context.WithTimeout(context.Background(), time.Minute)

		if err := s.assembleImageList(ctx); err != nil {
			slog.Error("error while refreshing gallery", "err", err)
		}
		cancle()
		<-t.C
	}
}

func (s *Service) assembleImageList(ctx context.Context) error {
	files, err := s.readDirRecursive(s.cfg.Path)
	if err != nil {
		return fmt.Errorf("could not read contents of %s: %w", s.cfg.Path, err)
	}
	slog.Debug("loaded files", "files", files)

	savedHash, _ := s.cache.Do(ctx, s.cache.B().Get().Key(hashKey).Build()).AsBytes()

	newHash := hashImageList(files)

	if bytes.Equal(savedHash, newHash) {
		slog.Debug("images up to date no refreshing of gallery", "savedHash", savedHash, "newHash", newHash)
		return nil
	}

	slog.Info("starting gallery refresh")
	defer slog.Info("gallery refresh completed")

	imgs := make([]Image, 0, len(files))
	for _, file := range files {
		if strings.HasSuffix(file.Name(), ".md") {
			continue
		}

		imagePath := path.Join(s.cfg.Path, file.Name())
		img, err := s.img.GetImage(imagePath)
		if err != nil || img.Image == nil {
			slog.Warn("could not get image for gallery", "err", err, "img", img)
			continue
		}

		uid, err := img.UID()
		if err != nil {
			continue
		}

		imgs = append(imgs, Image{
			UID:    uid,
			Width:  img.Bounds().Dx(),
			Height: img.Bounds().Dy(),
		})
	}

	if err = s.cache.Do(ctx, s.cache.B().Set().Key(hashKey).Value(valkey.BinaryString(newHash)).Build()).Error(); err != nil {
		return err
	}

	if err = s.cache.Do(ctx, s.cache.B().Set().Key(imagesKey).Value(valkey.JSON(imgs)).Build()).Error(); err != nil {
		return err
	}

	return nil
}

func hashImageList(files []os.FileInfo) []byte {
	h := fnv.New128()

	for _, file := range files {
		fmt.Fprint(h, file.ModTime())
		fmt.Fprint(h, file.Size())
		fmt.Fprint(h, file.Name())
	}

	return h.Sum([]byte{})
}

func (s *Service) GetImages(ctx context.Context) (images []Image, err error) {
	err = s.cache.Do(ctx, s.cache.B().Get().Key(imagesKey).Build()).DecodeJSON(&images)
	return
}
