// PHP API Database Wrapper window.db = { apiBase: '', init(baseUrl = '') { this.apiBase = baseUrl || (window.location.origin + '/api'); }, // Convert snake_case keys to camelCase toCamel(obj) { if (Array.isArray(obj)) { return obj.map(item => this.toCamel(item)); } if (obj !== null && typeof obj === 'object') { const converted = {}; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); converted[camelKey] = this.toCamel(obj[key]); } } return converted; } return obj; }, async request(endpoint, options = {}) { const url = `${this.apiBase}${endpoint}`; const defaultOptions = { headers: { 'Content-Type': 'application/json', }, }; const response = await fetch(url, { ...defaultOptions, ...options }); const data = await response.json(); if (!response.ok) { // 404 is a valid "not found" response, not an error if (response.status === 404) { return data; } throw new Error(data.error || `HTTP ${response.status}`); } return data; }, async saveScenario(scenarioData) { try { const sessionId = scenarioData.sessionId; if (!sessionId) throw new Error('sessionId required'); const data = { sessionId, title: scenarioData.title, description: scenarioData.description, style: scenarioData.style, mood: scenarioData.mood, target: scenarioData.target, goal: scenarioData.goal, specificGoal: scenarioData.specificGoal, risk: scenarioData.risk, language: scenarioData.language, languageLevel: scenarioData.languageLevel, password: scenarioData.password, isFrozen: scenarioData.isFrozen ? 1 : 0, vocabulary: scenarioData.vocabulary || [], chatHistory: scenarioData.chatHistory || [] }; if (scenarioData._objectId) { // Update existing return await this.request('/scenarios.php', { method: 'PUT', body: JSON.stringify({ session_id: sessionId, ...data }) }); } // Check if exists const existing = await this.getScenarioBySessionId(sessionId); if (existing) { return await this.request('/scenarios.php', { method: 'PUT', body: JSON.stringify({ session_id: sessionId, ...data }) }); } // Create new return await this.request('/scenarios.php', { method: 'POST', body: JSON.stringify(data) }); } catch (e) { console.error("DB Save Scenario Error", e); throw e; } }, async getScenarios() { try { const response = await this.request('/scenarios.php'); const data = response.data || []; return this.toCamel(data); } catch (e) { console.error("DB Get Scenarios Error", e); return []; } }, async getScenarioBySessionId(sessionId) { try { const response = await this.request(`/scenarios.php?session_id=${encodeURIComponent(sessionId)}`); if (!response.data) return null; return this.toCamel(response.data); } catch (e) { console.error("DB Get Scenario Error", e); return null; } }, async updateScenarioVocabulary(sessionId, vocabulary) { try { return await this.request('/scenarios.php', { method: 'PUT', body: JSON.stringify({ session_id: sessionId, vocabulary }) }); } catch (e) { console.error("DB Update Vocabulary Error", e); } }, async updateScenarioChatHistory(sessionId, chatHistory) { try { return await this.request('/scenarios.php', { method: 'PUT', body: JSON.stringify({ session_id: sessionId, chat_history: chatHistory }) }); } catch (e) { console.error("DB Update Chat Error", e); } }, async getPrompts() { try { const response = await this.request('/prompts.php'); return response.data || null; } catch (e) { console.error("DB Get Prompts Error", e); return null; } }, async toggleFreezeScenario(objectId, isFrozen) { try { // Need to get session_id from objectId first const scenarios = await this.getScenarios(); const match = scenarios.find(s => s.id == objectId); if (match) { return await this.request('/scenarios.php', { method: 'PUT', body: JSON.stringify({ session_id: match.session_id, is_frozen: isFrozen ? 1 : 0 }) }); } return false; } catch(e) { console.error("DB Freeze Scenario Error", e); return false; } }, async deleteScenario(objectId) { try { const scenarios = await this.getScenarios(); const match = scenarios.find(s => s.id == objectId); if (match) { await this.request(`/scenarios.php?session_id=${encodeURIComponent(match.session_id)}`, { method: 'DELETE' }); return true; } return false; } catch (e) { console.error("DB Delete Scenario Error", e); return false; } }, async savePrompts(prompts) { try { return await this.request('/prompts.php', { method: 'POST', body: JSON.stringify({ prompts }) }); } catch (e) { console.error("DB Save Prompts Error", e); } } }; // Auto-initialize with current origin window.db.init();