import { useState, useEffect, useCallback } from 'react'; import type { Paciente, Cama, Area, Internacion, Evolucion, Laboratorio, AcidoBase, Cultivo, EstudioComplementario, Interconsulta, ATB, Vista } from '@/types'; // Fallback UUID generator for non-secure contexts (HTTP without localhost) function generateUUID(): string { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } interface HospitalState { pacientes: Paciente[]; camas: Cama[]; areas: Area[]; internaciones: Internacion[]; evoluciones: Evolucion[]; laboratorios: Laboratorio[]; acidosBase: AcidoBase[]; cultivos: Cultivo[]; estudiosComplementarios: EstudioComplementario[]; interconsultas: Interconsulta[]; atb: ATB[]; vistaActual: Vista; currentInternacionId?: string | null; } const API_BASE = import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`; const defaultState = (): HospitalState => ({ pacientes: [], areas: [], internaciones: [], evoluciones: [], laboratorios: [], acidosBase: [], cultivos: [], estudiosComplementarios: [], interconsultas: [], atb: [], vistaActual: 'dashboard', camas: [] }); export function useHospitalStore() { const [state, setState] = useState(defaultState); const [isLoaded, setIsLoaded] = useState(false); // Load state from backend on mount useEffect(() => { let mounted = true; (async () => { try { const res = await fetch(`${API_BASE}/state`); if (!res.ok) throw new Error('no state'); const body = await res.json(); if (mounted && body) { setState(body as HospitalState); } } catch (err) { // no remote state or unreachable — keep defaults } finally { if (mounted) setIsLoaded(true); } })(); return () => { mounted = false; }; }, []); // Persist state to backend when it changes (debounced) useEffect(() => { if (!isLoaded) return; const t = setTimeout(() => { (async () => { try { await fetch(`${API_BASE}/state`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(state), }); } catch (err) { // ignore save errors for now } })(); }, 300); return () => clearTimeout(t); }, [state, isLoaded]); // Acciones de navegación const setVista = useCallback((vista: Vista) => { setState(prev => ({ ...prev, vistaActual: vista })); }, []); // Acciones de pacientes const agregarPaciente = useCallback((paciente: Omit) => { const nuevoPaciente: Paciente = { ...paciente, id: generateUUID(), fechaRegistro: new Date().toISOString().split('T')[0], }; setState(prev => ({ ...prev, pacientes: [...prev.pacientes, nuevoPaciente], })); return nuevoPaciente.id; }, []); const actualizarPaciente = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, pacientes: prev.pacientes.map(p => p.id === id ? { ...p, ...datos } : p), })); }, []); const eliminarPaciente = useCallback((id: string) => { setState(prev => ({ ...prev, pacientes: prev.pacientes.filter(p => p.id !== id), })); }, []); // Acciones de camas const actualizarCama = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, camas: prev.camas.map(c => c.id === id ? { ...c, ...datos } : c), })); }, []); const agregarCama = useCallback((cama: Omit) => { const nuevaCama: Cama = { ...cama, id: generateUUID(), } as Cama; setState(prev => ({ ...prev, camas: [...prev.camas, nuevaCama], })); return nuevaCama.id; }, []); const eliminarCama = useCallback((id: string) => { setState(prev => ({ ...prev, camas: prev.camas.filter(c => c.id !== id), internaciones: prev.internaciones.map(i => i.camaId === id ? { ...i, activa: false } : i), })); }, []); // Acciones de internaciones const iniciarInternacion = useCallback((internacion: Omit) => { const nuevaInternacion: Internacion = { ...internacion, id: generateUUID(), activa: true, }; setState(prev => ({ ...prev, internaciones: [...prev.internaciones, nuevaInternacion], camas: prev.camas.map(c => c.id === internacion.camaId ? { ...c, estado: 'Ocupada', pacienteId: internacion.pacienteId, internacionId: nuevaInternacion.id } : c ), })); return nuevaInternacion.id; }, []); const finalizarInternacion = useCallback((internacionId: string, datos: { fechaEgreso: string; diagnosticoEgreso: string; motivoEgreso: Internacion['motivoEgreso'] }) => { setState(prev => { const internacion = prev.internaciones.find(i => i.id === internacionId); if (!internacion) return prev; return { ...prev, internaciones: prev.internaciones.map(i => i.id === internacionId ? { ...i, ...datos, activa: false } : i ), cams: prev.camas.map(c => c.id === internacion.camaId ? { ...c, estado: 'Disponible', pacienteId: undefined, internacionId: undefined } : c ), }; }); }, []); const actualizarInternacion = useCallback((internacionId: string, datos: Partial) => { setState(prev => ({ ...prev, internaciones: prev.internaciones.map(i => i.id === internacionId ? { ...i, ...datos } : i ), })); }, []); // Acciones de evoluciones const agregarEvolucion = useCallback((evolucion: Omit) => { const nuevaEvolucion: Evolucion = { ...evolucion, id: generateUUID(), }; setState(prev => ({ ...prev, evoluciones: [...prev.evoluciones, nuevaEvolucion], })); return nuevaEvolucion.id; }, []); const eliminarEvolucion = useCallback((id: string) => { setState(prev => ({ ...prev, evoluciones: prev.evoluciones.filter(e => e.id !== id), })); }, []); const actualizarEvolucion = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, evoluciones: prev.evoluciones.map(e => e.id === id ? { ...e, ...datos } : e), })); }, []); // Acciones de laboratorios const agregarLaboratorio = useCallback((laboratorio: Omit) => { const nuevoLaboratorio: Laboratorio = { ...laboratorio, id: generateUUID(), }; setState(prev => ({ ...prev, laboratorios: [...prev.laboratorios, nuevoLaboratorio], })); return nuevoLaboratorio.id; }, []); const eliminarLaboratorio = useCallback((id: string) => { setState(prev => ({ ...prev, laboratorios: prev.laboratorios.filter(l => l.id !== id), })); }, []); const actualizarLaboratorio = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, laboratorios: prev.laboratorios.map(l => l.id === id ? { ...l, ...datos } : l), })); }, []); // Acciones de ácido-base const agregarAcidoBase = useCallback((acidoBase: Omit) => { const nuevoAcidoBase: AcidoBase = { ...acidoBase, id: generateUUID(), }; setState(prev => ({ ...prev, acidosBase: [...prev.acidosBase, nuevoAcidoBase], })); return nuevoAcidoBase.id; }, []); const actualizarAcidoBase = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, acidosBase: prev.acidosBase.map(a => a.id === id ? { ...a, ...datos } : a), })); }, []); const eliminarAcidoBase = useCallback((id: string) => { setState(prev => ({ ...prev, acidosBase: prev.acidosBase.filter(a => a.id !== id), })); }, []); // Acciones de cultivos const agregarCultivo = useCallback((cultivo: Omit) => { const nuevoCultivo: Cultivo = { ...cultivo, id: generateUUID(), }; setState(prev => ({ ...prev, cultivos: [...prev.cultivos, nuevoCultivo], })); return nuevoCultivo.id; }, []); const actualizarCultivo = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, cultivos: prev.cultivos.map(c => c.id === id ? { ...c, ...datos } : c), })); }, []); const eliminarCultivo = useCallback((id: string) => { setState(prev => ({ ...prev, cultivos: prev.cultivos.filter(c => c.id !== id), })); }, []); // Acciones de estudios complementarios const agregarEstudioComplementario = useCallback((estudio: Omit) => { const nuevoEstudio: EstudioComplementario = { ...estudio, id: generateUUID(), }; setState(prev => ({ ...prev, estudiosComplementarios: [...prev.estudiosComplementarios, nuevoEstudio], })); return nuevoEstudio.id; }, []); const actualizarEstudioComplementario = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, estudiosComplementarios: prev.estudiosComplementarios.map(e => e.id === id ? { ...e, ...datos } : e), })); }, []); const eliminarEstudioComplementario = useCallback((id: string) => { setState(prev => ({ ...prev, estudiosComplementarios: prev.estudiosComplementarios.filter(e => e.id !== id), })); }, []); // Acciones de interconsultas const agregarInterconsulta = useCallback((interconsulta: Omit) => { const nuevaInterconsulta: Interconsulta = { ...interconsulta, id: generateUUID() }; setState(prev => ({ ...prev, interconsultas: [...(prev.interconsultas || []), nuevaInterconsulta], })); return nuevaInterconsulta.id; }, []); const actualizarInterconsulta = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, interconsultas: (prev.interconsultas || []).map(ic => ic.id === id ? { ...ic, ...datos } : ic), })); }, []); const eliminarInterconsulta = useCallback((id: string) => { setState(prev => ({ ...prev, interconsultas: (prev.interconsultas || []).filter(ic => ic.id !== id), })); }, []); const agregarATB = useCallback((atb: Omit) => { const nuevoATB: ATB = { ...atb, id: generateUUID() }; setState(prev => ({ ...prev, atb: [...prev.atb, nuevoATB], })); return nuevoATB.id; }, []); const actualizarATB = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, atb: prev.atb.map(a => a.id === id ? { ...a, ...datos } : a), })); }, []); const eliminarATB = useCallback((id: string) => { setState(prev => ({ ...prev, atb: prev.atb.filter(a => a.id !== id), })); }, []); // Acciones de areas const agregarArea = useCallback((area: Omit) => { const nuevaArea: Area = { ...area, id: generateUUID() }; setState(prev => ({ ...prev, areas: [...prev.areas, nuevaArea], })); return nuevaArea.id; }, []); const actualizarArea = useCallback((id: string, datos: Partial) => { setState(prev => ({ ...prev, areas: prev.areas.map(a => a.id === id ? { ...a, ...datos } : a), })); }, []); const eliminarArea = useCallback((id: string) => { setState(prev => ({ ...prev, areas: prev.areas.filter(a => a.id !== id), camas: prev.camas.map(c => c.areaId === id ? { ...c, areaId: undefined } : c), })); }, []); // Funciones de utilidad const getPacienteById = useCallback((id: string) => { return state.pacientes.find(p => p.id === id); }, [state.pacientes]); const getCamaById = useCallback((id: string) => { return state.camas.find(c => c.id === id); }, [state.camas]); const getInternacionById = useCallback((id: string) => { return state.internaciones.find(i => i.id === id); }, [state.internaciones]); const getInternacionActivaByPaciente = useCallback((pacienteId: string) => { return state.internaciones.find(i => i.pacienteId === pacienteId && i.activa); }, [state.internaciones]); const getEvolucionesByInternacion = useCallback((internacionId: string) => { return state.evoluciones .filter(e => e.internacionId === internacionId) .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); }, [state.evoluciones]); const getLaboratoriosByPaciente = useCallback((pacienteId: string) => { return state.laboratorios .filter(l => l.pacienteId === pacienteId) .sort((a, b) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime()); }, [state.laboratorios]); const getAcidosBaseByPaciente = useCallback((pacienteId: string) => { return state.acidosBase .filter(a => a.pacienteId === pacienteId) .sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime()); }, [state.acidosBase]); const getCultivosByPaciente = useCallback((pacienteId: string) => { return state.cultivos .filter(c => c.pacienteId === pacienteId) .sort((a, b) => new Date(b.fechaToma).getTime() - new Date(a.fechaToma).getTime()); }, [state.cultivos]); const getEstadisticas = useCallback(() => { const fueraDeAreaList = state.camas.filter(c => c.areaId === 'Fuera de Area'); const camasActivas = state.camas.filter(c => c.areaId !== 'Fuera de Area'); const camaPrincipal = state.camas.filter(c => c.areaId !== 'Fuera de Area'); const camaOcupadas = state.camas.filter(c => c.estado === 'Ocupada' && c.areaId !== 'Fuera de Area').length; const camasDisponibles = state.camas.filter(c => c.estado === 'Disponible' && c.areaId !== 'Fuera de Area').length; const camasMantenimiento = state.camas.filter(c => c.estado === 'Reparacion').length; const internacionesActivas = state.internaciones.filter(i => i.activa).length; const totalPacientes = state.pacientes.length; const cultivosPendientes = state.cultivos.filter(c => c.estado === 'NAF/Pendiente').length; return { camaOcupadas, camasOcupadas: camaOcupadas, camasDisponibles, camasMantenimiento, totalCamas: state.camas.length, totalCamasActivas: camasActivas.length, camasFueraDeArea: fueraDeAreaList.length, internacionesActivas, totalPacientes, cultivosPendientes, porcentajeOcupacion: camaPrincipal.length > 0 ? Math.round((camaOcupadas / camaPrincipal.length) * 100) : 0, }; }, [state]); // Current internacion selection (for HC page) const setCurrentInternacion = useCallback((id?: string | null) => { setState(prev => ({ ...prev, currentInternacionId: id ?? null })); }, []); return { ...state, isLoaded, setVista, agregarPaciente, actualizarPaciente, eliminarPaciente, actualizarCama, iniciarInternacion, finalizarInternacion, actualizarInternacion, agregarCama, eliminarCama, agregarEvolucion, eliminarEvolucion, agregarLaboratorio, eliminarLaboratorio, agregarAcidoBase, actualizarAcidoBase, eliminarAcidoBase, agregarCultivo, actualizarCultivo, eliminarCultivo, agregarEstudioComplementario, actualizarEstudioComplementario, eliminarEstudioComplementario, agregarInterconsulta, actualizarInterconsulta, eliminarInterconsulta, agregarATB, actualizarATB, eliminarATB, agregarArea, actualizarArea, eliminarArea, getPacienteById, getCamaById, getInternacionById, getInternacionActivaByPaciente, getEvolucionesByInternacion, getLaboratoriosByPaciente, getAcidosBaseByPaciente, getCultivosByPaciente, actualizarEvolucion, actualizarLaboratorio, setCurrentInternacion, getEstadisticas, }; }