package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
)

var (
	photoMu       sync.Mutex
	photoFiles    []string
	photoIdx      int
	photoDir      string
	photoStop     chan struct{}
	photoDone     chan struct{}
	photoCacheMu  sync.Mutex
	photoCacheMap map[string]string
	photoCacheDir string
)

func scanPhotoDir(dir string) []string {
	entries, err := os.ReadDir(dir)
	if err != nil {
		return nil
	}
	var files []string
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		ext := strings.ToLower(filepath.Ext(e.Name()))
		switch ext {
		case ".jpg", ".jpeg", ".png", ".bmp", ".webp", ".gif":
			files = append(files, e.Name())
		}
	}
	sort.Strings(files)
	return files
}

func preCachePhotos(dir string, files []string) {
	cache := make(map[string]string, len(files))
	for _, name := range files {
		uri := imageToDataURI(filepath.Join(dir, name))
		if uri != "" {
			cache[name] = uri
		}
	}
	photoCacheMu.Lock()
	photoCacheMap = cache
	photoCacheDir = dir
	photoCacheMu.Unlock()
	log.Printf("相册: 预缓存 %d/%d 张", len(cache), len(files))
}

func getCachedPhotoURI(dir, name string) string {
	photoCacheMu.Lock()
	if photoCacheMap != nil && photoCacheDir == dir {
		if uri, ok := photoCacheMap[name]; ok {
			photoCacheMu.Unlock()
			return uri
		}
	}
	photoCacheMu.Unlock()
	return imageToDataURI(filepath.Join(dir, name))
}

func pushCurrentPhoto(interval int) {
	photoMu.Lock()
	files := photoFiles
	idx := photoIdx
	dir := photoDir
	photoMu.Unlock()

	if len(files) == 0 || dir == "" {
		evalJS(`if(window.updatePhotoFromGo) updatePhotoFromGo(null)`)
		return
	}
	if idx >= len(files) {
		idx = 0
	}

	src := getCachedPhotoURI(dir, files[idx])
	if src == "" {
		return
	}

	data, _ := json.Marshal(map[string]interface{}{
		"src":      src,
		"counter":  fmt.Sprintf("%d / %d", idx+1, len(files)),
		"interval": interval,
	})
	evalJS(fmt.Sprintf(`if(window.updatePhotoFromGo) updatePhotoFromGo(%s)`, string(data)))
}

func startPhotoLoop() {
	cfg := loadConfig()
	if cfg.PhotoDir == "" || cfg.HidePhoto {
		return
	}

	interval := cfg.PhotoInterval
	if interval <= 0 {
		interval = 15
	}

	files := scanPhotoDir(cfg.PhotoDir)
	if len(files) == 0 {
		log.Println("相册: 目录为空或无图片")
		return
	}

	photoMu.Lock()
	photoDir = cfg.PhotoDir
	photoFiles = files
	photoIdx = 0
	stop := make(chan struct{})
	done := make(chan struct{})
	photoStop = stop
	photoDone = done
	photoMu.Unlock()

	photoCacheMu.Lock()
	photoCacheMap = nil
	photoCacheDir = cfg.PhotoDir
	photoCacheMu.Unlock()

	log.Printf("相册: 共 %d 张, 间隔 %ds", len(files), interval)
	go preCachePhotos(cfg.PhotoDir, files)
	pushCurrentPhoto(interval)

	go func() {
		ticker := time.NewTicker(time.Duration(interval) * time.Second)
		defer func() {
			ticker.Stop()
			close(done)
		}()
		for {
			select {
			case <-ticker.C:
				photoMu.Lock()
				if len(photoFiles) > 0 {
					photoIdx = (photoIdx + 1) % len(photoFiles)
				}
				photoMu.Unlock()
				pushCurrentPhoto(interval)
			case <-stop:
				return
			}
		}
	}()
}

func stopPhotoLoop() {
	photoMu.Lock()
	stop := photoStop
	done := photoDone
	photoStop = nil
	photoDone = nil
	photoFiles = nil
	photoIdx = 0
	photoDir = ""
	photoMu.Unlock()

	photoCacheMu.Lock()
	photoCacheMap = nil
	photoCacheDir = ""
	photoCacheMu.Unlock()

	if stop != nil {
		close(stop)
	}
	if done != nil {
		<-done
	}
}

func restartPhotoLoop() {
	stopPhotoLoop()
	evalJS(`if(window.updatePhotoFromGo) updatePhotoFromGo(null)`)
	startPhotoLoop()
}
