import { ipcMain, nativeTheme, systemPreferences, BrowserWindow, app, shell } from 'electron' import { IPC } from '@shared/ipc' import type { Challenge, Exercise, GameId, Settings } from '@shared/types' import { addChallenge, addExercise, clearHistory, deleteChallenge, deleteExercise, getHistory, getState, markDone, setGameEnabled, skip, snooze, updateChallenge, updateExercise, updateSettings } from './store' import { broadcastState } from './state-actions' import { setAutostart, isAutostartEnabled } from './autostart' import { setPaused, forceCheck } from './scheduler' import { hideReminderWindow, getMainWindow } from './windows' import { broadcastGames, installGame, listGamesStatus, simulateMatchEnd, toggleGame, uninstallGame } from './games/registry' import { checkForUpdates, downloadUpdate, getUpdaterStatus, quitAndInstall } from './updater' export function registerIpc(): void { ipcMain.handle(IPC.getState, () => { const state = getState() state.settings.startWithWindows = isAutostartEnabled() return state }) ipcMain.handle( IPC.addExercise, (_e, input: Omit) => { const ex = addExercise(input) broadcastState() return ex } ) ipcMain.handle(IPC.updateExercise, (_e, id: string, patch: Partial) => { const ex = updateExercise(id, patch) broadcastState() return ex }) ipcMain.handle(IPC.deleteExercise, (_e, id: string) => { const ok = deleteExercise(id) broadcastState() return ok }) ipcMain.handle(IPC.toggleExercise, (_e, id: string, enabled: boolean) => { const patch: Partial = { enabled } if (enabled) { const ex = getState().exercises.find((e) => e.id === id) if (ex) patch.nextFireAt = Date.now() + ex.intervalMinutes * 60_000 } const ex = updateExercise(id, patch) broadcastState() return ex }) ipcMain.handle( IPC.markDone, (_e, id: string, actualReps?: number) => { const ex = markDone(id, actualReps) broadcastState() return ex } ) ipcMain.handle(IPC.snooze, (_e, id: string, minutes: number) => { const ex = snooze(id, minutes) broadcastState() return ex }) ipcMain.handle(IPC.skip, (_e, id: string) => { const ex = skip(id) broadcastState() return ex }) ipcMain.handle(IPC.updateSettings, (_e, patch: Partial) => { if (patch.startWithWindows !== undefined) { setAutostart(patch.startWithWindows) } const merged: Partial = { ...patch } if (patch.startWithWindows !== undefined) { merged.startWithWindows = isAutostartEnabled() } const settings = updateSettings(merged) broadcastState() return settings }) ipcMain.handle(IPC.getAccentColor, () => { try { return '#' + systemPreferences.getAccentColor() } catch { return '#5B8DEF' } }) ipcMain.handle(IPC.getOsTheme, () => nativeTheme.shouldUseDarkColors ? 'dark' : 'light' ) ipcMain.handle(IPC.pauseAll, () => setPaused(true)) ipcMain.handle(IPC.resumeAll, () => { setPaused(false) forceCheck() }) ipcMain.handle(IPC.quit, () => app.quit()) ipcMain.handle(IPC.reminderClose, () => hideReminderWindow()) ipcMain.on(IPC.minimizeMain, (event) => { BrowserWindow.fromWebContents(event.sender)?.minimize() }) ipcMain.on(IPC.closeMain, () => { const main = getMainWindow() if (!main) return if (getState().settings.minimizeToTray) main.hide() else main.close() }) ipcMain.on(IPC.hideMain, () => getMainWindow()?.hide()) // Games ipcMain.handle(IPC.gamesList, async () => listGamesStatus()) ipcMain.handle(IPC.gameInstall, async (_e, id: GameId) => { const status = await installGame(id) setGameEnabled(id, true) await toggleGame(id, true) const all = await listGamesStatus() broadcastGames(all) broadcastState() return status }) ipcMain.handle(IPC.gameUninstall, async (_e, id: GameId) => { const status = await uninstallGame(id) setGameEnabled(id, false) const all = await listGamesStatus() broadcastGames(all) broadcastState() return status }) ipcMain.handle(IPC.gameToggle, async (_e, id: GameId, enabled: boolean) => { setGameEnabled(id, enabled) await toggleGame(id, enabled) const all = await listGamesStatus() broadcastGames(all) broadcastState() }) ipcMain.handle(IPC.gameOpenLaunchOptions, (_e, _id: GameId) => { // Opens Steam's library; user manually adds launch options. shell.openExternal('steam://nav/games/details/570') }) // Challenges ipcMain.handle(IPC.addChallenge, (_e, input: Omit) => { const c = addChallenge(input) broadcastState() return c }) ipcMain.handle( IPC.updateChallenge, (_e, id: string, patch: Partial) => { const c = updateChallenge(id, patch) broadcastState() return c } ) ipcMain.handle(IPC.deleteChallenge, (_e, id: string) => { const ok = deleteChallenge(id) broadcastState() return ok }) ipcMain.handle(IPC.toggleChallenge, (_e, id: string, enabled: boolean) => { const c = updateChallenge(id, { enabled }) broadcastState() return c }) ipcMain.handle(IPC.closeMatchSummary, () => hideReminderWindow()) // Dev helper: simulate a match end with given stats. ipcMain.handle( 'dev:simulateMatchEnd', (_e, id: GameId, stats: Record) => { simulateMatchEnd(id, stats) } ) // Auto-updater ipcMain.handle(IPC.updaterStatus, () => getUpdaterStatus()) ipcMain.handle(IPC.updaterCheck, () => checkForUpdates()) ipcMain.handle(IPC.updaterDownload, () => downloadUpdate()) ipcMain.handle(IPC.updaterInstall, () => quitAndInstall()) // History ipcMain.handle(IPC.getHistory, (_e, sinceMs?: number) => getHistory(sinceMs)) ipcMain.handle(IPC.clearHistory, (_e, beforeTs?: number) => clearHistory(beforeTs) ) }