package models

import (
	"strings"
	"sync"
	"time"
)

// RunState is the in-memory, non-persisted status of a backup target's current
// or most recent run. It intentionally lives outside cfg/persist(): a crash or
// restart should never leave a stale "still running" flag behind.
type RunState struct {
	Running   bool         `json:"running"`
	TargetID  string       `json:"target_id"`
	Method    BackupMethod `json:"method,omitempty"`
	Percent   float64      `json:"percent"`
	ETA       string       `json:"eta,omitempty"`
	Message   string       `json:"message,omitempty"`
	StartedAt *time.Time   `json:"started_at,omitempty"`
}

var (
	progressMu sync.RWMutex
	progress   = map[string]RunState{}
)

// progressKey builds the scoped key a target's run state is stored under.
// scope is "node" or "svc:<serviceID>".
func progressKey(scope, targetID string) string {
	return scope + ":" + targetID
}

func setProgress(key string, s RunState) {
	progressMu.Lock()
	defer progressMu.Unlock()
	progress[key] = s
}

// GetProgress returns the current run state for a single target, if any is known.
func GetProgress(scope, targetID string) (RunState, bool) {
	progressMu.RLock()
	defer progressMu.RUnlock()
	s, ok := progress[progressKey(scope, targetID)]
	return s, ok
}

// GetProgressForScope returns all known run states for a scope ("node" or "svc:<id>").
func GetProgressForScope(scope string) []RunState {
	prefix := scope + ":"
	progressMu.RLock()
	defer progressMu.RUnlock()
	result := make([]RunState, 0)
	for k, s := range progress {
		if strings.HasPrefix(k, prefix) {
			result = append(result, s)
		}
	}
	return result
}
