package stub

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"os"
	"path/filepath"
	"slices"
)

const cacheKeyFile = ".cache-key"

// stubGeneratorCacheVersion invalidates generated stubs when cp9-owned
// generator/template source changes but the source proto/swagger files do not.
const stubGeneratorCacheVersion = "v2"

// ComputeCacheKey computes a SHA256 hash over the sorted contents of the given files.
// This is the cache key used to skip unnecessary stub rebuilds.
func ComputeCacheKey(files []string) (string, error) {
	if len(files) == 0 {
		return "", fmt.Errorf("no files to hash")
	}

	sorted := make([]string, len(files))
	copy(sorted, files)
	slices.Sort(sorted)

	h := sha256.New()
	_, _ = fmt.Fprintf(h, "generator:%s\n", stubGeneratorCacheVersion)
	for _, f := range sorted {
		data, err := os.ReadFile(f)
		if err != nil {
			return "", fmt.Errorf("read %s: %w", f, err)
		}
		// Include filename as separator to avoid collisions from concatenation.
		_, _ = fmt.Fprintf(h, "file:%s\n", f)
		_, _ = h.Write(data)
	}
	return hex.EncodeToString(h.Sum(nil)), nil
}

// CheckCache returns true if the output directory's cached key matches the given key.
func CheckCache(outputDir, cacheKey string) bool {
	data, err := os.ReadFile(filepath.Join(outputDir, cacheKeyFile))
	if err != nil {
		return false
	}
	return string(data) == cacheKey
}

// WriteCacheKey writes the cache key to the output directory.
func WriteCacheKey(outputDir, cacheKey string) error {
	return os.WriteFile(filepath.Join(outputDir, cacheKeyFile), []byte(cacheKey), 0o600)
}
