'use client' import { useQueryClient, useMutation, useQuery } from '@tanstack/react-query' import { queryKeys } from '@/lib/query-keys' import { BrainstormQuotaError, type BrainstormQuotaPayload, } from '@/lib/brainstorm-quota-client' import type { BrainstormSession, BrainstormSessionListItem } from '@/types/brainstorm' function parseBrainstormQuota(res: Response, data: BrainstormQuotaPayload & Record) { if (res.status === 402 && data?.error === 'QUOTA_EXCEEDED') { throw new BrainstormQuotaError(data, 'QUOTA_EXCEEDED') } } /** i18n key for host-pays quota toasts (Story 3.4). */ export function brainstormQuotaMessageKey(err: unknown): string | null { if (err instanceof BrainstormQuotaError) { return err.isGuestActor ? 'brainstorm.quotaGuest' : 'brainstorm.quotaHost' } return null } export interface CreateBrainstormResult { session: BrainstormSession contextSummary?: { support: number tension: number extension: number } } export function useBrainstormSessions() { return useQuery({ queryKey: queryKeys.brainstormSessions(), queryFn: async (): Promise => { const res = await fetch('/api/brainstorm', { credentials: 'include' }) const data = await res.json() return data.data || [] }, }) } export function useSharedBrainstormSessions() { return useQuery({ queryKey: queryKeys.brainstormSharedSessions(), queryFn: async (): Promise => { const res = await fetch('/api/brainstorm/shared', { credentials: 'include' }) const data = await res.json() return data.data || [] }, }) } export interface BrainstormSessionMeta { role: 'owner' | 'editor' | 'viewer' | 'guest' | 'none' canEdit: boolean } export interface BrainstormSessionResult { session: BrainstormSession meta?: BrainstormSessionMeta } export function useBrainstormSession(sessionId: string | null) { return useQuery({ queryKey: queryKeys.brainstormSession(sessionId || ''), queryFn: async (): Promise => { if (!sessionId) return null const res = await fetch(`/api/brainstorm/${sessionId}`, { credentials: 'include' }) const data = await res.json() if (!data.data) return null return { session: data.data, meta: data._meta || undefined, } }, enabled: !!sessionId, }) } export function useCreateBrainstorm() { const queryClient = useQueryClient() return useMutation({ mutationFn: async (input: { seedIdea: string sourceNoteId?: string contextNoteIds?: string[] locale?: string }): Promise => { const res = await fetch('/api/brainstorm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }) const data = await res.json() parseBrainstormQuota(res, data) if (!data.success) throw new Error(data.error || 'Failed to create brainstorm') return { session: data.data, contextSummary: data.contextSummary } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useExpandIdea(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (input: { ideaId: string; locale?: string }): Promise => { const res = await fetch(`/api/brainstorm/${sessionId}/expand`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }) const data = await res.json() parseBrainstormQuota(res, data) if (!data.success) throw new Error(data.error || 'Failed to expand idea') return data.data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useDismissIdea(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (ideaId: string) => { const res = await fetch(`/api/brainstorm/${sessionId}/dismiss`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ ideaId }), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to dismiss idea') }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) }, }) } export function useConvertIdea(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (ideaId: string) => { const res = await fetch(`/api/brainstorm/${sessionId}/convert`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ ideaId }), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to convert idea') return data.data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useExportBrainstorm(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async () => { const res = await fetch(`/api/brainstorm/${sessionId}/export`, { method: 'POST', credentials: 'include', }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to export brainstorm') return data.data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useFinalizeBrainstorm(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async () => { const res = await fetch(`/api/brainstorm/${sessionId}/finalize`, { method: 'POST', credentials: 'include', }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to finalize brainstorm') return data.impact }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useDeleteBrainstorm() { const queryClient = useQueryClient() return useMutation({ mutationFn: async (sessionId: string) => { const res = await fetch(`/api/brainstorm/${sessionId}`, { method: 'DELETE', credentials: 'include', }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to delete brainstorm') }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useInviteParticipant(sessionId: string) { return useMutation({ mutationFn: async (input: { role: 'editor' | 'viewer'; expiresInHours?: number; email?: string }) => { const res = await fetch(`/api/brainstorm/${sessionId}/invite`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to create invite') return data }, }) } export function useJoinBrainstorm() { const queryClient = useQueryClient() return useMutation({ mutationFn: async (token: string) => { const res = await fetch(`/api/brainstorm/join`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ token }), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to join') return data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) } export function useAddManualIdea(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (input: { title: string; description?: string; parentIdeaId?: string; locale?: string }) => { const res = await fetch(`/api/brainstorm/${sessionId}/manual-idea`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }) const data = await res.json() parseBrainstormQuota(res, data) if (!data.success) throw new Error(data.error || 'Failed to add idea') return data.data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) }, }) } export function useBrainstormActivity(sessionId: string | null) { return useQuery({ queryKey: ['brainstorm', 'activity', sessionId], queryFn: async () => { if (!sessionId) return [] const res = await fetch(`/api/brainstorm/${sessionId}/activity`, { credentials: 'include' }) const data = await res.json() return data.data || [] }, enabled: !!sessionId, refetchInterval: 10000, }) } export function useUpdateBrainstormSettings(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (input: { isPublic?: boolean; guestCanEdit?: boolean }) => { const res = await fetch(`/api/brainstorm/${sessionId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(input), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to update settings') return data.data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) }, }) } export function useBrainstormSnapshots(sessionId: string | null) { return useQuery({ queryKey: ['brainstorm', 'snapshots', sessionId], queryFn: async () => { if (!sessionId) return [] const res = await fetch(`/api/brainstorm/${sessionId}/snapshots`, { credentials: 'include' }) const data = await res.json() return data.data || [] }, enabled: !!sessionId, }) } export function useStarIdea(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (ideaId: string) => { const res = await fetch(`/api/brainstorm/${sessionId}/star`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ ideaId }), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to star idea') return data.data as { ideaId: string; isStarred: boolean } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) }, }) } export function useSummarizeBrainstorm(sessionId: string) { return useMutation({ mutationFn: async (locale?: string): Promise => { const res = await fetch(`/api/brainstorm/${sessionId}/summarize`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ locale }), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to summarize') return data.data.summary as string }, }) } export function useRenameBrainstormSession(sessionId: string) { const queryClient = useQueryClient() return useMutation({ mutationFn: async (seedIdea: string) => { const res = await fetch(`/api/brainstorm/${sessionId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ seedIdea }), }) const data = await res.json() if (!data.success) throw new Error(data.error || 'Failed to rename session') return data.data }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSession(sessionId) }) queryClient.invalidateQueries({ queryKey: queryKeys.brainstormSessions() }) }, }) }