// Production - The Idle Game: Backend Synchronization & Authentication const TOKEN_KEY = 'production_auth_token'; const USERNAME_KEY = 'production_username'; export class SyncManager { constructor(stateManager) { this.sm = stateManager; this.token = localStorage.getItem(TOKEN_KEY) || null; this.username = localStorage.getItem(USERNAME_KEY) || null; this.isSyncing = false; this.pendingActions = []; this.debounceTimer = null; } isLoggedIn() { return !!this.token; } getUsername() { return this.username || 'Executive Guest'; } setAuth(token, username) { this.token = token; this.username = username; localStorage.setItem(TOKEN_KEY, token); localStorage.setItem(USERNAME_KEY, username); } logout() { this.token = null; this.username = null; localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USERNAME_KEY); } queueAction(action) { this.pendingActions.push(action); clearTimeout(this.debounceTimer); this.debounceTimer = setTimeout(() => this.flush(), 1000); } async flush() { if (!this.token || this.isSyncing || this.pendingActions.length === 0) return; this.isSyncing = true; const actionsToSend = [...this.pendingActions]; this.pendingActions = []; try { const res = await fetch('/api/game/tick', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ seq: Date.now(), actions: actionsToSend, client_tick: Date.now() / 1000 }) }); if (!res.ok) { // If unauthorized, clear token if (res.status === 401) this.logout(); // Return unsent actions to queue this.pendingActions.unshift(...actionsToSend); } } catch (err) { console.warn('Sync failed (will retry):', err); this.pendingActions.unshift(...actionsToSend); } finally { this.isSyncing = false; } } async fetchCloudState() { if (!this.token) return null; try { const res = await fetch('/api/game/state', { headers: { 'Authorization': `Bearer ${this.token}` } }); if (res.ok) { const data = await res.json(); return data.save_data || null; } } catch (err) { console.error('Error fetching cloud state:', err); } return null; } async login(username, password) { const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Login failed'); this.setAuth(data.token, data.username); return data; } async saveCloudState(saveData) { if (!this.token) return false; try { const res = await fetch('/api/game/save', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.token}` }, body: JSON.stringify({ save_data: saveData }) }); return res.ok; } catch (err) { console.error('Error saving cloud state:', err); return false; } } async register(username, password, website_hp = '') { const res = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password, website_hp }) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Registration failed'); this.setAuth(data.token, data.username); return data; } }