import { useRef, forwardRef, useImperativeHandle, useEffect } from "react"; import io from "socket.io-client"; import PropTypes from "prop-types"; const SOCKET_URL = window.location.hostname === "localhost" ? "http://localhost:8082/database.io" : "/database.io"; const socket = io(SOCKET_URL, { path: "/database.io/socket.io", transports: ["websocket", "polling"], autoConnect: false, }); export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSuccess, onFailure }, ref) => { const socketRef = useRef(socket); const currentUser = useRef(null); useEffect(() => { socketRef.current.connect(); return () => socketRef.current.disconnect(); }, []); useEffect(() => { const verifySession = async () => { const storedSession = localStorage.getItem("sessionToken"); if (!storedSession || storedSession === "undefined") return; try { const response = await new Promise((resolve) => { socketRef.current.emit("verifySession", { sessionToken: storedSession }, resolve); }); if (response?.success) { currentUser.current = { id: response.user.id, username: response.user.username, sessionToken: storedSession, }; onLoginSuccess(response.user); } else { localStorage.removeItem("sessionToken"); onFailure("Session expired"); } } catch (error) { onFailure(error.message); } }; verifySession(); }, []); const createUser = async (userConfig) => { try { const response = await new Promise((resolve) => { socketRef.current.emit("createUser", userConfig, resolve); }); if (response?.user?.sessionToken) { currentUser.current = { id: response.user.id, username: response.user.username, sessionToken: response.user.sessionToken, }; localStorage.setItem("sessionToken", response.user.sessionToken); onCreateSuccess(response.user); } else { throw new Error(response?.error || "User creation failed"); } } catch (error) { onFailure(error.message); } }; const loginUser = async ({ username, password, sessionToken }) => { try { const response = await new Promise((resolve) => { const credentials = sessionToken ? { sessionToken } : { username, password }; socketRef.current.emit("loginUser", credentials, resolve); }); if (response?.success) { currentUser.current = { id: response.user.id, username: response.user.username, sessionToken: response.user.sessionToken, }; localStorage.setItem("sessionToken", response.user.sessionToken); onLoginSuccess(response.user); } else { throw new Error(response?.error || "Login failed"); } } catch (error) { onFailure(error.message); } }; const logoutUser = () => { localStorage.removeItem("sessionToken"); currentUser.current = null; onLoginSuccess(null); }; const deleteUser = async () => { if (!currentUser.current) return onFailure("No user logged in"); try { const response = await new Promise((resolve) => { socketRef.current.emit("deleteUser", { userId: currentUser.current.id, sessionToken: currentUser.current.sessionToken, }, resolve); }); if (response?.success) { logoutUser(); onDeleteSuccess(response); } else { throw new Error(response?.error || "User deletion failed"); } } catch (error) { onFailure(error.message); } }; const saveHost = async (hostConfig) => { if (!currentUser.current) return onFailure("Not authenticated"); try { const response = await new Promise((resolve) => { socketRef.current.emit("saveHostConfig", { userId: currentUser.current.id, sessionToken: currentUser.current.sessionToken, ...hostConfig }, resolve); }); if (!response?.success) { throw new Error(response?.error || "Failed to save host"); } } catch (error) { onFailure(error.message); } }; const getAllHosts = async () => { if (!currentUser.current) return []; try { const response = await new Promise((resolve) => { socketRef.current.emit("getHosts", { userId: currentUser.current.id, sessionToken: currentUser.current.sessionToken, }, resolve); }); if (response?.success) { return response.hosts; } else { throw new Error(response?.error || "Failed to fetch hosts"); } } catch (error) { onFailure(error.message); return []; } }; const deleteHost = async ({ hostId }) => { if (!currentUser.current) return onFailure("Not authenticated"); try { const response = await new Promise((resolve) => { socketRef.current.emit("deleteHost", { userId: currentUser.current.id, sessionToken: currentUser.current.sessionToken, hostId: hostId, }, resolve); }); if (!response?.success) { throw new Error(response?.error || "Failed to delete host"); } } catch (error) { onFailure(error.message); } }; const editHost = async ({ oldHostConfig, newHostConfig }) => { if (!currentUser.current) return onFailure("Not authenticated"); try { console.log('Editing host with configs:', { oldHostConfig, newHostConfig }); const response = await new Promise((resolve) => { socketRef.current.emit("editHost", { userId: currentUser.current.id, sessionToken: currentUser.current.sessionToken, oldHostConfig, newHostConfig, }, resolve); }); if (!response?.success) { throw new Error(response?.error || "Failed to edit host"); } } catch (error) { onFailure(error.message); } }; const shareHost = async (hostId, targetUsername) => { if (!currentUser.current) return onFailure("Not authenticated"); try { const response = await new Promise((resolve) => { socketRef.current.emit("shareHost", { userId: currentUser.current.id, sessionToken: currentUser.current.sessionToken, hostId, targetUsername, }, resolve); }); if (!response?.success) { throw new Error(response?.error || "Failed to share host"); } } catch (error) { onFailure(error.message); } }; useImperativeHandle(ref, () => ({ createUser, loginUser, logoutUser, deleteUser, saveHost, getAllHosts, deleteHost, shareHost, editHost, getUser: () => currentUser.current, })); return null; }); User.displayName = "User"; User.propTypes = { onLoginSuccess: PropTypes.func.isRequired, onCreateSuccess: PropTypes.func.isRequired, onDeleteSuccess: PropTypes.func.isRequired, onFailure: PropTypes.func.isRequired, };