import { useState, useCallback, useEffect, useRef } from 'react' import { useInstanceStore } from '../store/instanceStore' import { contactApi, type BackendContact } from '../services/chatApiService' const PAGE_SIZE = 50 export function usePhonebookLogic() { const instanceStore = useInstanceStore() // ── Data ────────────────────────────────────────────────────────────────── const [contacts, setContacts] = useState([]) const [total, setTotal] = useState(0) const [stats, setStats] = useState({ total: 0, blocked: 0, flagged: 0, active: 0 }) const [loading, setLoading] = useState(false) // ── Filters ─────────────────────────────────────────────────────────────── const [search, setSearch] = useState('') const [blockedFilter, setBlockedFilter] = useState<'all' | 'blocked' | 'active'>('all') const [offset, setOffset] = useState(0) // ── Edit modal ──────────────────────────────────────────────────────────── const [editingContact, setEditingContact] = useState(null) const [editName, setEditName] = useState('') const [editSaving, setEditSaving] = useState(false) // ── Toast ───────────────────────────────────────────────────────────────── const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null) const showToast = useCallback((message: string, type: 'success' | 'error') => { setToast({ message, type }) setTimeout(() => setToast(null), 3000) }, []) // ── Derived ─────────────────────────────────────────────────────────────── const activeInstanceId = instanceStore.activeInstanceId const searchDebounceRef = useRef>() // ── Load contacts ───────────────────────────────────────────────────────── const load = useCallback(async (newOffset = 0) => { setLoading(true) try { const params: Parameters[0] = { instanceId: activeInstanceId ?? undefined, limit: PAGE_SIZE, offset: newOffset, } if (search.trim()) params.search = search.trim() if (blockedFilter === 'blocked') params.blocked = true if (blockedFilter === 'active') params.blocked = false const [data, statsData] = await Promise.all([ contactApi.list(params), contactApi.stats(activeInstanceId ?? undefined), ]) setContacts(data.contacts) setTotal(data.total) setStats(statsData) setOffset(newOffset) } catch { showToast('Erro ao carregar contatos.', 'error') } finally { setLoading(false) } }, [activeInstanceId, search, blockedFilter, showToast]) // Initial load + instance change useEffect(() => { instanceStore.loadInstances() }, []) useEffect(() => { load(0) }, [activeInstanceId, blockedFilter]) // Debounce search useEffect(() => { clearTimeout(searchDebounceRef.current) searchDebounceRef.current = setTimeout(() => load(0), 350) return () => clearTimeout(searchDebounceRef.current) }, [search]) // ── Pagination ──────────────────────────────────────────────────────────── const goToPage = useCallback((page: number) => { load(page * PAGE_SIZE) }, [load]) const currentPage = Math.floor(offset / PAGE_SIZE) const totalPages = Math.ceil(total / PAGE_SIZE) // ── Block/unblock ───────────────────────────────────────────────────────── const handleToggleBlock = useCallback(async (contact: BackendContact) => { try { const updated = await contactApi.update(contact.id, { isBlocked: !contact.isBlocked }) setContacts((prev) => prev.map((c) => c.id === updated.id ? updated : c)) showToast(updated.isBlocked ? 'Contato bloqueado.' : 'Contato desbloqueado.', 'success') } catch { showToast('Erro ao alterar bloqueio.', 'error') } }, [showToast]) // ── Delete ──────────────────────────────────────────────────────────────── const handleDelete = useCallback(async (id: string) => { try { await contactApi.delete(id) setContacts((prev) => prev.filter((c) => c.id !== id)) setTotal((t) => t - 1) showToast('Contato removido.', 'success') } catch { showToast('Erro ao remover contato.', 'error') } }, [showToast]) // ── Edit ────────────────────────────────────────────────────────────────── const openEdit = useCallback((contact: BackendContact) => { setEditingContact(contact) setEditName(contact.name ?? '') }, []) const closeEdit = useCallback(() => { setEditingContact(null) setEditName('') }, []) const saveEdit = useCallback(async () => { if (!editingContact) return setEditSaving(true) try { const updated = await contactApi.update(editingContact.id, { name: editName.trim() || undefined }) setContacts((prev) => prev.map((c) => c.id === updated.id ? updated : c)) showToast('Contato atualizado.', 'success') closeEdit() } catch { showToast('Erro ao salvar contato.', 'error') } finally { setEditSaving(false) } }, [editingContact, editName, showToast, closeEdit]) // ── Display name helper ─────────────────────────────────────────────────── function displayName(c: BackendContact & { displayName?: string }): string { if (c.displayName || c.name || c.verifiedName || c.notify) { return c.displayName || c.name || c.verifiedName || c.notify || '' } if (c.phone) return `+${c.phone}` // @lid — identificador interno do WhatsApp: exibe como +número if (c.jid.endsWith('@lid')) { const id = c.jid.split('@')[0] return `+${id}` } return c.jid.split('@')[0] } return { // instances instances: instanceStore.instances, activeInstanceId, selectInstance: instanceStore.setActiveInstance.bind(instanceStore), // data contacts, total, stats, loading, // filters search, setSearch, blockedFilter, setBlockedFilter, // pagination currentPage, totalPages, goToPage, // actions handleToggleBlock, handleDelete, refresh: () => load(0), // edit modal editingContact, editName, setEditName, editSaving, openEdit, closeEdit, saveEdit, // feedback toast, displayName, } }