package main

import (
	"os/exec"
	"sync"
	"time"

	"github.com/robfig/cron/v3"
)

type stateKey string

type profileState struct {
	errMsg      string
	repoErr     string
	scheduleErr string
	needsInit   bool
	entryID     cron.EntryID
}

type appState struct {
	mu            sync.Mutex
	busyProfiles  map[stateKey]*exec.Cmd
	onBusyChanged func(bool)
	mountCmds     map[stateKey]*exec.Cmd
	mountStopping map[stateKey]bool
	notifications string
	failStatus    map[stateKey]string
	lastBackup    map[stateKey]time.Time
	activeProfile stateKey
	profileStates map[stateKey]profileState
}

var state = appState{
	busyProfiles:  make(map[stateKey]*exec.Cmd),
	mountCmds:     make(map[stateKey]*exec.Cmd),
	mountStopping: make(map[stateKey]bool),
	failStatus:    make(map[stateKey]string),
	lastBackup:    make(map[stateKey]time.Time),
	profileStates: make(map[stateKey]profileState),
}

func resetProfileState(keys []stateKey) stateKey {
	state.mu.Lock()
	defer state.mu.Unlock()

	profiles := make(map[stateKey]profileState, len(keys))
	for _, key := range keys {
		profiles[key] = state.profileStates[key]
	}
	state.profileStates = profiles
	if _, ok := profiles[state.activeProfile]; !ok {
		state.activeProfile = ""
		if len(keys) > 0 {
			state.activeProfile = keys[0]
		}
	}
	return state.activeProfile
}

func activeProfileKey() stateKey {
	state.mu.Lock()
	defer state.mu.Unlock()
	return state.activeProfile
}

func setActiveProfileKey(key stateKey) {
	state.mu.Lock()
	if _, ok := state.profileStates[key]; ok {
		state.activeProfile = key
	}
	state.mu.Unlock()
}

func getProfileState(key stateKey) (profileState, bool) {
	state.mu.Lock()
	defer state.mu.Unlock()
	ps, ok := state.profileStates[key]
	return ps, ok
}

func setProfileProbeState(key stateKey, probe profileState) {
	state.mu.Lock()
	if current, ok := state.profileStates[key]; ok {
		probe.entryID = current.entryID
		state.profileStates[key] = probe
	}
	state.mu.Unlock()
}

func setProfileCronEntry(key stateKey, entryID cron.EntryID) {
	state.mu.Lock()
	if current, ok := state.profileStates[key]; ok {
		current.entryID = entryID
		state.profileStates[key] = current
	}
	state.mu.Unlock()
}

func setLastBackup(key stateKey, t time.Time) {
	state.mu.Lock()
	state.lastBackup[key] = t
	state.mu.Unlock()
}

func getLastBackup(key stateKey) time.Time {
	state.mu.Lock()
	defer state.mu.Unlock()
	return state.lastBackup[key]
}

func isProfileBusy(key stateKey) bool {
	state.mu.Lock()
	defer state.mu.Unlock()
	_, ok := state.busyProfiles[key]
	return ok
}

func isAnyBusy() bool {
	state.mu.Lock()
	defer state.mu.Unlock()
	return len(state.busyProfiles) > 0
}

func releaseProfile(key stateKey) {
	state.mu.Lock()
	wasBusy := len(state.busyProfiles) > 0
	delete(state.busyProfiles, key)
	nowBusy := len(state.busyProfiles) > 0
	cb := state.onBusyChanged
	state.mu.Unlock()
	finishBusyChange(wasBusy, nowBusy, cb)
}

func acquireProfile(key stateKey) bool {
	state.mu.Lock()
	if _, busy := state.busyProfiles[key]; busy {
		state.mu.Unlock()
		return false
	}
	wasBusy := len(state.busyProfiles) > 0
	state.busyProfiles[key] = nil
	nowBusy := len(state.busyProfiles) > 0
	cb := state.onBusyChanged
	state.mu.Unlock()
	finishBusyChange(wasBusy, nowBusy, cb)
	return true
}

func finishBusyChange(wasBusy, nowBusy bool, cb func(bool)) {
	if nowBusy && !wasBusy {
		setIconAnimated("busy")
	}
	if cb != nil {
		cb(nowBusy)
	}
}

func setProfileBusyCmd(key stateKey, cmd *exec.Cmd) {
	state.mu.Lock()
	defer state.mu.Unlock()
	state.busyProfiles[key] = cmd
}

func cancelProfile(key stateKey) {
	state.mu.Lock()
	cmd := state.busyProfiles[key]
	state.mu.Unlock()
	if cmd != nil && cmd.Process != nil {
		interruptProcess(cmd.Process)
	}
}
