package downloader

import (
	"io/fs"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/mudler/xlog"
)

// PartialFileSuffix marks an in-progress download. The success path renames the
// partial to its final name, so any leftover with this suffix is an unfinished
// transfer.
const PartialFileSuffix = ".partial"

// CleanupStalePartialFiles removes *.partial files under root whose last
// modification is older than olderThan, returning the number removed. These are
// abandoned downloads left by a process killed mid-transfer (OOM, restart) or
// by a stall whose cleanup never ran; without reaping they accumulate and can
// fill the models volume. A still-in-progress download touches its .partial on
// every write, so a generous olderThan never trims an active transfer.
//
// A missing root is not an error (nothing to clean). Unreadable entries are
// skipped so one bad file does not abort the whole sweep.
func CleanupStalePartialFiles(root string, olderThan time.Duration) (int, error) {
	if _, err := os.Stat(root); err != nil {
		if os.IsNotExist(err) {
			return 0, nil
		}
		return 0, err
	}

	cutoff := time.Now().Add(-olderThan)

	// Collect candidates during the walk and delete them afterwards rather than
	// mutating the tree from inside the WalkDir callback (avoids the symlink
	// TOCTOU class flagged by gosec G122, and never removes an entry mid-walk).
	var stale []string
	err := filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return nil // skip unreadable subtree, keep going
		}
		if d.IsDir() || !strings.HasSuffix(d.Name(), PartialFileSuffix) {
			return nil
		}
		info, err := d.Info()
		if err != nil || info.ModTime().After(cutoff) {
			return nil
		}
		stale = append(stale, path)
		return nil
	})
	if err != nil {
		return 0, err
	}

	removed := 0
	for _, path := range stale {
		if err := os.Remove(path); err != nil {
			xlog.Warn("failed to remove stale partial download", "file", path, "error", err)
			continue
		}
		removed++
		xlog.Info("removed stale partial download", "file", path)
	}
	return removed, nil
}
