package main

import (
	"io/fs"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func updateCache() {
	log.Print("updating cache")

	cfg, err := readConfig()
	if err != nil {
		log.Printf("update cache failed: read config failed: %v", err)
		return
	}

	errs := 0
	inUse := map[string]bool{}

	fetch := func(path ...string) {
		assetPath, err := distFetch(path...)
		if err != nil {
			log.Printf("update cache: dist fetch failed: %v", err)
			errs += 1
		}
		inUse[assetPath] = true
	}

	fetch("grub-support", *grubSupportVersion)

	for _, host := range cfg.Hosts {
		ctx, err := newRenderContext(host, cfg)
		if err != nil {
			log.Printf("update cache: %s: build context failed: %v", host.Name, err)
			errs += 1
			continue
		}

		_, cfg, err := ctx.Config()
		if err != nil {
			log.Printf("update cache: %s: render config failed: %v", host.Name, err)
			errs += 1
			continue
		}

		signer, err := getSigner(host.ClusterName)
		if err != nil {
			log.Printf("update cache: %s: get signer failed: %v", host.Name, err)
			errs += 1
			continue
		}

		fetch("kernels", host.Kernel)
		fetch("layers", "modules", host.Kernel)
		fetch("initrd", host.Initrd)

		allErofs := true
		for layer, version := range host.Versions {
			fetch("layers", layer, version)

			if layer != "modules" && !strings.HasSuffix(version, ".erofs") {
				allErofs = false
			}
		}

		if allErofs {
			path, err := layersCombo(host, cfg, signer)
			if err != nil {
				log.Printf("update cache: layer combo failed: %v", err)
				continue
			}
			inUse[path] = true
		}
	}

	if errs != 0 {
		log.Printf("update cache: %d errors, not cleaning", errs)
		return
	}

	distDir := filepath.Join(*dataDir, "dist")
	cacheDir := filepath.Join(*dataDir, "cache")

	existing := []string{}

	cleanDirs := []string{cacheDir}
	if !*keepDist {
		cleanDirs = append(cleanDirs, distDir)
	}

	for _, dir := range cleanDirs {
		fs.WalkDir(os.DirFS(dir), ".", func(path string, d fs.DirEntry, err error) error {
			if err != nil {
				log.Printf("update cache: walking %s failed: %v", dir, err)
				return nil
			}
			if !d.IsDir() {
				existing = append(existing, filepath.Join(dir, path))
			}
			return nil
		})
	}

	log.Printf("update cache: %d/%d assets in use", len(inUse), len(existing))

	for _, path := range existing {
		if inUse[path] {
			continue
		}
		log.Print("update cache: removing ", path)
		if err := os.Remove(path); err != nil {
			log.Print("update cache: failed to remove: ", err)
		}
	}
}
