"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.useCollaboration = useCollaboration;
const react_1 = require("react");
const Y = __importStar(require("yjs"));
const y_indexeddb_1 = require("y-indexeddb");
const socket_io_client_1 = require("socket.io-client");
/**
 * React Hook for Y.js Collaboration
 */
function useCollaboration(options) {
    const { documentId, documentType, userId, userInfo, serverUrl = 'http://localhost:3000', token, onUserJoined, onUserLeft, onCursorUpdate, onAwarenessUpdate, onDocumentChange, autoConnect = true } = options;
    // Y.js references
    const docRef = (0, react_1.useRef)(null);
    const persistenceRef = (0, react_1.useRef)(null);
    const socketRef = (0, react_1.useRef)(null);
    // State
    const [state, setState] = (0, react_1.useState)({
        connected: false,
        activeUsers: [],
        localUser: { ...userInfo, id: userId, cursor: undefined },
        isSyncing: false,
        hasUnsavedChanges: false
    });
    // Initialize Y.js document
    const initializeDocument = (0, react_1.useCallback)(() => {
        if (docRef.current)
            return docRef.current;
        const doc = new Y.Doc();
        docRef.current = doc;
        // Set up persistence
        const persistence = new y_indexeddb_1.IndexeddbPersistence(`${documentType}-${documentId}`, doc);
        persistenceRef.current = persistence;
        persistence.on('synced', () => {
            setState(prev => ({ ...prev, isSyncing: false }));
        });
        // Set up document structure based on type
        const dataMap = doc.getMap(documentType);
        if (documentType === 'task') {
            dataMap.set('id', documentId);
            dataMap.set('title', new Y.Text());
            dataMap.set('description', new Y.Text());
            dataMap.set('status', 'pending');
            dataMap.set('priority', 'medium');
            dataMap.set('assignees', new Y.Array());
            dataMap.set('tags', new Y.Array());
            dataMap.set('subtasks', new Y.Array());
            dataMap.set('comments', new Y.Array());
            dataMap.set('customFields', new Y.Map());
        }
        else {
            dataMap.set('id', documentId);
            dataMap.set('name', new Y.Text());
            dataMap.set('description', new Y.Text());
            dataMap.set('tasks', new Y.Array());
            dataMap.set('members', new Y.Array());
            dataMap.set('settings', new Y.Map());
        }
        // Set up change observer
        doc.on('update', (update, origin) => {
            if (origin !== 'remote') {
                setState(prev => ({ ...prev, hasUnsavedChanges: true }));
                // Send update to server
                if (socketRef.current?.connected) {
                    socketRef.current.emit('collaboration:update', {
                        documentId,
                        update: Array.from(update),
                        userId
                    });
                }
            }
            if (onDocumentChange) {
                onDocumentChange({ update, origin });
            }
        });
        return doc;
    }, [documentId, documentType, userId, onDocumentChange]);
    // Initialize Socket.io connection
    const initializeSocket = (0, react_1.useCallback)(() => {
        if (socketRef.current)
            return socketRef.current;
        const socket = (0, socket_io_client_1.io)(serverUrl, {
            auth: { token },
            transports: ['websocket', 'polling']
        });
        socketRef.current = socket;
        // Socket event handlers
        socket.on('connect', () => {
            console.log('🔌 Collaboration connected');
            setState(prev => ({ ...prev, connected: true }));
            // Join document room
            socket.emit('collaboration:join', {
                documentId,
                documentType,
                userId,
                userInfo
            });
        });
        socket.on('disconnect', () => {
            console.log('🔌 Collaboration disconnected');
            setState(prev => ({ ...prev, connected: false }));
        });
        // Handle initial document state
        socket.on('collaboration:init', (data) => {
            if (docRef.current && data.state) {
                Y.applyUpdate(docRef.current, new Uint8Array(data.state), 'remote');
            }
            setState(prev => ({ ...prev, lastSyncTime: new Date() }));
        });
        // Handle remote updates
        socket.on('collaboration:sync', (data) => {
            if (docRef.current && data.documentId === documentId) {
                Y.applyUpdate(docRef.current, new Uint8Array(data.update), 'remote');
                setState(prev => ({
                    ...prev,
                    hasUnsavedChanges: false,
                    lastSyncTime: new Date()
                }));
            }
        });
        // Handle user events
        socket.on('collaboration:user-joined', (data) => {
            if (data.documentId === documentId) {
                setState(prev => ({
                    ...prev,
                    activeUsers: [...prev.activeUsers, data.userInfo]
                }));
                if (onUserJoined)
                    onUserJoined(data.userInfo);
            }
        });
        socket.on('collaboration:user-left', (data) => {
            if (data.documentId === documentId) {
                setState(prev => ({
                    ...prev,
                    activeUsers: prev.activeUsers.filter(u => u.id !== data.userId)
                }));
                if (onUserLeft)
                    onUserLeft(data.userId);
            }
        });
        // Handle cursor updates
        socket.on('collaboration:cursor-update', (data) => {
            if (data.documentId === documentId) {
                setState(prev => ({
                    ...prev,
                    activeUsers: prev.activeUsers.map(u => u.id === data.userId ? { ...u, cursor: data.cursor } : u)
                }));
                if (onCursorUpdate)
                    onCursorUpdate(data.userId, data.cursor);
            }
        });
        // Handle awareness updates
        socket.on('collaboration:awareness-update', (data) => {
            if (data.documentId === documentId && onAwarenessUpdate) {
                onAwarenessUpdate(data.userId, data.awareness);
            }
        });
        socket.on('collaboration:error', (error) => {
            console.error('Collaboration error:', error.message);
        });
        return socket;
    }, [
        serverUrl,
        token,
        documentId,
        documentType,
        userId,
        userInfo,
        onUserJoined,
        onUserLeft,
        onCursorUpdate,
        onAwarenessUpdate
    ]);
    // Connect to collaboration
    const connect = (0, react_1.useCallback)(() => {
        const doc = initializeDocument();
        const socket = initializeSocket();
        return { doc, socket };
    }, [initializeDocument, initializeSocket]);
    // Disconnect from collaboration
    const disconnect = (0, react_1.useCallback)(() => {
        if (socketRef.current) {
            socketRef.current.emit('collaboration:leave', { documentId, userId });
            socketRef.current.disconnect();
            socketRef.current = null;
        }
        if (persistenceRef.current) {
            persistenceRef.current.destroy();
            persistenceRef.current = null;
        }
        if (docRef.current) {
            docRef.current.destroy();
            docRef.current = null;
        }
        setState({
            connected: false,
            activeUsers: [],
            localUser: { ...userInfo, id: userId, cursor: undefined },
            isSyncing: false,
            hasUnsavedChanges: false
        });
    }, [documentId, userId, userInfo]);
    // Get Y.js document
    const getDocument = (0, react_1.useCallback)(() => docRef.current, []);
    // Get specific Y.js type
    const getYType = (0, react_1.useCallback)((field) => {
        if (!docRef.current)
            return null;
        const dataMap = docRef.current.getMap(documentType);
        return dataMap.get(field);
    }, [documentType]);
    // Update field value
    const updateField = (0, react_1.useCallback)((field, value) => {
        if (!docRef.current)
            return;
        const dataMap = docRef.current.getMap(documentType);
        docRef.current.transact(() => {
            if (value instanceof Y.AbstractType) {
                dataMap.set(field, value);
            }
            else if (typeof value === 'string' && (field === 'title' || field === 'description' || field === 'name')) {
                const yText = dataMap.get(field);
                if (yText) {
                    yText.delete(0, yText.length);
                    yText.insert(0, value);
                }
            }
            else if (Array.isArray(value)) {
                const yArray = dataMap.get(field);
                if (yArray) {
                    yArray.delete(0, yArray.length);
                    yArray.insert(0, value);
                }
            }
            else if (typeof value === 'object' && value !== null) {
                const yMap = dataMap.get(field);
                if (yMap) {
                    yMap.forEach((_, key) => yMap.delete(key));
                    Object.entries(value).forEach(([k, v]) => yMap.set(k, v));
                }
            }
            else {
                dataMap.set(field, value);
            }
        }, userId);
    }, [documentType, userId]);
    // Update cursor position
    const updateCursor = (0, react_1.useCallback)((field, position, selection) => {
        const cursor = { field, position, selection };
        setState(prev => ({
            ...prev,
            localUser: { ...prev.localUser, cursor }
        }));
        if (socketRef.current?.connected) {
            socketRef.current.emit('collaboration:cursor', {
                documentId,
                userId,
                cursor
            });
        }
    }, [documentId, userId]);
    // Update awareness
    const updateAwareness = (0, react_1.useCallback)((awareness) => {
        if (socketRef.current?.connected) {
            socketRef.current.emit('collaboration:awareness', {
                documentId,
                userId,
                awareness
            });
        }
    }, [documentId, userId]);
    // Get current value of a field
    const getValue = (0, react_1.useCallback)((field) => {
        if (!docRef.current)
            return null;
        const dataMap = docRef.current.getMap(documentType);
        const value = dataMap.get(field);
        if (value instanceof Y.Text) {
            return value.toString();
        }
        else if (value instanceof Y.Array) {
            return value.toArray();
        }
        else if (value instanceof Y.Map) {
            const result = {};
            value.forEach((v, k) => {
                result[k] = v;
            });
            return result;
        }
        return value;
    }, [documentType]);
    // Auto-connect on mount
    (0, react_1.useEffect)(() => {
        if (autoConnect) {
            connect();
        }
        return () => {
            if (autoConnect) {
                disconnect();
            }
        };
    }, [autoConnect, connect, disconnect]);
    return {
        // State
        state,
        // Connection management
        connect,
        disconnect,
        // Document access
        getDocument,
        getYType,
        getValue,
        // Update methods
        updateField,
        updateCursor,
        updateAwareness,
        // Utility
        isConnected: state.connected,
        activeUsers: state.activeUsers,
        hasUnsavedChanges: state.hasUnsavedChanges
    };
}
//# sourceMappingURL=useCollaboration.js.map