import { useState, useCallback, useEffect } from 'react'; import { UserProfile, BookProject, Chapter, ChatMessage, PlanType, Entity, Idea, WorkflowData } from './types'; import { INITIAL_CHAPTER } from './constants'; import { generateStoryContent } from './services/geminiService'; import { authService, dataService } from './services/api.bak2'; export function useAuth() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const checkSession = useCallback(async (injectedUser: any = null) => { setLoading(true); // On affiche l'écran de chargement console.log("[useAuth]checkSession ==> Démarrage de la connexion complète..."); try { // 1. Vérification de la session Auth const sessionUser = injectedUser || await authService.getSession(); if (sessionUser) { console.log("[useAuth] 1/2 Session validée. Récupération du profil..."); // 2. Récupération du profil (BLOQUANT avec await) // On attend les 2 secondes ici pour avoir une transition propre let profile = await dataService.getProfile(sessionUser.id); // 3. Si le profil n'existe pas, on le crée avant de continuer if (!profile) { console.log("[useAuth] Profil absent, création en cours..."); const createResult = await dataService.createItem('profiles', { user_id: sessionUser.id, email: sessionUser.email, full_name: sessionUser.name || 'Nouvel Écrivain' // ... autres champs par défaut }); // On simule l'objet profil pour éviter un second appel API profile = { user_id: sessionUser.id, email: sessionUser.email, full_name: sessionUser.name }; } // 4. On remplit l'état final setUser({ id: sessionUser.id, email: sessionUser.email, name: profile.full_name || sessionUser.name, avatar: profile.avatar_url || `https://i.pravatar.cc/150?u=${sessionUser.email}`, subscription: { plan: profile.subscription_plan || 'free', startDate: 0, status: 'active' }, usage: { aiActionsCurrent: profile.ai_actions_current || 0, aiActionsLimit: profile.ai_actions_limit || 10, projectsLimit: profile.projects_limit || 1 }, preferences: { theme: profile.theme || 'light', dailyWordGoal: 500, language: 'fr' }, stats: { totalWordsWritten: 0, writingStreak: 0, lastWriteDate: 0 }, bio: profile.bio || "" }); console.log("[useAuth] 2/2 Profil récupéré. Accès au Dashboard."); } else { console.log("[useAuth] 2/2 Profil non récupéré. Redirection vers la page de connexion."); setUser(null); } } catch (err) { console.error("[useAuth] Erreur lors de la connexion:", err); setUser(null); } finally { // C'est seulement ici que l'écran de chargement disparaît setLoading(false); } }, []); useEffect(() => { checkSession(); }, [checkSession]); const login = async (data: any) => { // UI Feedback immédiat si nécessaire setLoading(true); // Appel API const result = await authService.signIn(data.email, data.password); console.log("[useAuth] Résultat de la connexion:", result); if (result && !result.error) { // Utilisation directe de la réponse API pour connecter l'utilisateur // Cela évite d'attendre le round-trip réseau de getSession const userObj = result.user || (result.id ? result : null); if (userObj) { console.log("[useAuth] Login réussi, injection immédiate."); await checkSession(userObj); // Note: checkSession est maintenant optimisé pour ne pas re-bloquer } else { await checkSession(); } } else { // En cas d'erreur, on arrête le chargement pour afficher le message setLoading(false); } return result; }; const signup = async (data: any) => { setLoading(true); const result = await authService.signUp(data.email, data.password, data.name); if (result && !result.error) { const userObj = result.user || (result.id ? result : null); // Injection immédiate comme pour le login await checkSession(userObj); } else { setLoading(false); } return result; }; const logout = async () => { setLoading(true); await authService.signOut(); setUser(null); setLoading(false); }; const incrementUsage = async () => { if (user) { setUser(prev => prev ? { ...prev, usage: { ...prev.usage, aiActionsCurrent: prev.usage.aiActionsCurrent + 1 } } : null); } }; return { user, login, signup, logout, incrementUsage, loading }; } export function useProjects(user: UserProfile | null) { const [projects, setProjects] = useState([]); const [currentProjectId, setCurrentProjectId] = useState(null); const [loading, setLoading] = useState(false); // DEBUG: Check if user is present useEffect(() => { console.log("[useProjects DEBUG] Hook initialized/updated with user:", user); }, [user]); const fetchProjects = useCallback(async () => { if (!user) { console.log("[useProjects DEBUG] fetchProjects skipped: No user"); return; } console.log("[useProjects DEBUG] fetchProjects starting for user:", user.id); setLoading(true); try { const data = await dataService.getProjects(user.id); console.log("[useProjects DEBUG] Projects fetched:", data); setProjects(data.map((p: any) => ({ id: p.id.toString(), title: p.title, author: p.author, lastModified: Date.now(), settings: { genre: p.genre, subGenre: p.sub_genre, targetAudience: p.target_audience, tone: p.tone, pov: p.pov, tense: p.tense, synopsis: p.synopsis, themes: p.themes }, styleGuide: p.style_guide, chapters: [], entities: [], ideas: [], workflow: { nodes: [], connections: [] } }))); } catch (e) { console.error("[useProjects] Erreur chargement projets:", e); } finally { setLoading(false); } }, [user]); useEffect(() => { fetchProjects(); }, [fetchProjects]); const fetchProjectDetails = async (projectId: string) => { const id = parseInt(projectId); try { const [chapters, entities, ideas, workflows] = await Promise.all([ dataService.getRelatedData('chapters', id), dataService.getRelatedData('entities', id), dataService.getRelatedData('ideas', id), dataService.getRelatedData('workflows', id) ]); setProjects(prev => prev.map(p => p.id === projectId ? { ...p, chapters: chapters.map((c: any) => ({ ...c, id: c.id.toString() })), entities: entities.map((e: any) => ({ ...e, id: e.id.toString(), customValues: e.custom_values ? JSON.parse(e.custom_values) : {}, attributes: e.attributes ? (typeof e.attributes === 'string' ? JSON.parse(e.attributes) : e.attributes) : undefined })), ideas: ideas.map((i: any) => ({ ...i, id: i.id.toString() })), workflow: workflows[0] ? { nodes: JSON.parse(workflows[0].nodes || '[]'), connections: JSON.parse(workflows[0].connections || '[]') } : { nodes: [], connections: [] } } : p)); } catch (e) { console.error("[useProjects] Erreur détails projet:", e); } }; const createProject = async () => { if (!user) return null; const result = await dataService.createProject({ user_id: user.id, title: "Nouveau Roman", author: user.name }); if (result.status === 'success') { await fetchProjects(); return result.id.toString(); } return null; }; const addChapter = async () => { if (!currentProjectId) return null; const projectId = parseInt(currentProjectId); const result = await dataService.createItem('chapters', { project_id: projectId, title: "Nouveau Chapitre", content: "

Écrivez ici votre prochain chapitre...

", summary: "" }); if (result.status === 'success' && result.id) { await fetchProjectDetails(currentProjectId); return result.id.toString(); } return null; }; const createEntity = async (entity: Omit) => { if (!currentProjectId) return null; const projectId = parseInt(currentProjectId); const dbPayload = { project_id: projectId, type: entity.type, name: entity.name, description: entity.description, details: entity.details, story_context: entity.storyContext, attributes: JSON.stringify(entity.attributes || {}), custom_values: JSON.stringify(entity.customValues || {}) }; const result = await dataService.createItem('entities', dbPayload); if (result.status === 'success') { const newId = result.id.toString(); const newEntity = { ...entity, id: newId }; setProjects(prev => prev.map(p => p.id === currentProjectId ? { ...p, entities: [...p.entities, newEntity] } : p)); return newId; } return null; }; const updateEntity = async (entityId: string, updates: Partial) => { if (!currentProjectId) return; const pId = parseInt(currentProjectId); const eId = parseInt(entityId); const dbPayload: any = {}; if (updates.name !== undefined) dbPayload.name = updates.name; if (updates.description !== undefined) dbPayload.description = updates.description; if (updates.details !== undefined) dbPayload.details = updates.details; if (updates.storyContext !== undefined) dbPayload.story_context = updates.storyContext; if (updates.attributes !== undefined) dbPayload.attributes = JSON.stringify(updates.attributes); if (updates.customValues !== undefined) dbPayload.custom_values = JSON.stringify(updates.customValues); if (Object.keys(dbPayload).length > 0) { await dataService.updateItem('entities', eId, dbPayload); } setProjects(prev => prev.map(p => p.id === currentProjectId ? { ...p, entities: p.entities.map(e => e.id === entityId ? { ...e, ...updates } : e) } : p)); }; const deleteEntity = async (entityId: string) => { if (!currentProjectId) return; const eId = parseInt(entityId); await dataService.deleteItem('entities', eId); setProjects(prev => prev.map(p => p.id === currentProjectId ? { ...p, entities: p.entities.filter(e => e.id !== entityId) } : p)); }; const updateProject = async (updates: Partial) => { if (!currentProjectId) return; const id = parseInt(currentProjectId); const ncbData: any = {}; if (updates.title) ncbData.title = updates.title; if (updates.author) ncbData.author = updates.author; if (updates.settings) { ncbData.genre = updates.settings.genre; ncbData.sub_genre = updates.settings.subGenre; ncbData.synopsis = updates.settings.synopsis; ncbData.tone = updates.settings.tone; ncbData.pov = updates.settings.pov; ncbData.tense = updates.settings.tense; } // Note: workflow, ideas, templates are not yet persisted via updateProject fully if complex types // Using separate updaters is better. if (Object.keys(ncbData).length > 0) { await dataService.updateItem('projects', id, ncbData); } setProjects(prev => prev.map(p => p.id === currentProjectId ? { ...p, ...updates } as BookProject : p)); }; const updateChapter = async (chapterId: string, updates: Partial) => { if (!currentProjectId) return; const pId = parseInt(currentProjectId); const cId = parseInt(chapterId); // Call API await dataService.updateItem('chapters', cId, updates); // Update Local State setProjects(prev => prev.map(p => p.id === currentProjectId ? { ...p, chapters: p.chapters.map(c => c.id === chapterId ? { ...c, ...updates } : c) } : p)); }; return { projects, currentProjectId, setCurrentProjectId: (id: string | null) => { setCurrentProjectId(id); if (id) fetchProjectDetails(id); }, createProject, updateProject, updateChapter, addChapter, createEntity, updateEntity, deleteEntity, loading }; } export function useChat() { const [chatHistory, setChatHistory] = useState([]); const [isGenerating, setIsGenerating] = useState(false); const sendMessage = async (project: BookProject, chapterId: string, msg: string, user: UserProfile, onUsed: () => void) => { const userMsg: ChatMessage = { id: Date.now().toString(), role: 'user', text: msg }; setChatHistory(prev => [...prev, userMsg]); setIsGenerating(true); try { const response = await generateStoryContent(project, chapterId, msg, user, onUsed); const aiMsg: ChatMessage = { id: (Date.now() + 1).toString(), role: 'model', text: response.text, responseType: response.type }; setChatHistory(prev => [...prev, aiMsg]); } catch (error) { console.error(error); } finally { setIsGenerating(false); } }; return { chatHistory, isGenerating, sendMessage }; }