package cli

import (
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"time"
)

const nameCompletionCacheTTL = 30 * time.Second

type nameCompletionCacheFile struct {
	UpdatedAt time.Time `json:"updatedAt"`
	Names     []string  `json:"names"`
}

func readNameCompletionCacheFile() (nameCompletionCacheFile, bool) {
	path, err := nameCompletionCachePath()
	if err != nil {
		return nameCompletionCacheFile{}, false
	}

	raw, err := os.ReadFile(path)
	if err != nil {
		return nameCompletionCacheFile{}, false
	}
	// Avoid large reads if the cache ever gets corrupted.
	if len(raw) > 64*1024 {
		return nameCompletionCacheFile{}, false
	}

	var cache nameCompletionCacheFile
	if err := json.Unmarshal(raw, &cache); err != nil {
		return nameCompletionCacheFile{}, false
	}
	if cache.UpdatedAt.IsZero() || len(cache.Names) == 0 {
		return nameCompletionCacheFile{}, false
	}

	return cache, true
}

func cachedNameCompletions(now time.Time) ([]string, bool) {
	cache, ok := readNameCompletionCacheFile()
	if !ok {
		return nil, false
	}
	if now.Sub(cache.UpdatedAt) > nameCompletionCacheTTL {
		return nil, false
	}
	return cache.Names, true
}

func storeNameCompletions(now time.Time, names []string) error {
	if len(names) == 0 {
		return errors.New("no names")
	}
	path, err := nameCompletionCachePath()
	if err != nil {
		return err
	}

	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return err
	}

	cache := nameCompletionCacheFile{
		UpdatedAt: now,
		Names:     names,
	}
	raw, err := json.Marshal(cache)
	if err != nil {
		return err
	}

	f, err := os.CreateTemp(dir, "name-completions-*.json")
	if err != nil {
		return err
	}
	tmp := f.Name()
	defer func() { _ = os.Remove(tmp) }()

	if _, err := f.Write(raw); err != nil {
		_ = f.Close()
		return err
	}
	if err := f.Close(); err != nil {
		return err
	}

	return os.Rename(tmp, path)
}

func nameCompletionCachePath() (string, error) {
	if override := os.Getenv("SONOSCLI_COMPLETION_CACHE_DIR"); override != "" {
		return filepath.Join(override, "sonoscli", "name-completions.json"), nil
	}
	dir, err := os.UserCacheDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, "sonoscli", "name-completions.json"), nil
}
