'use strict';

var React = require('react');

/**
 * collaboration.ts — Real-time collaborative editing, comments, and version history.
 *
 * Uses a provider-agnostic architecture so consumers can plug in
 * WebSocket, WebRTC, or any custom transport.
 */
// ── WebSocket Transport ─────────────────────────────────────────
class WebSocketTransport {
    constructor(serverUrl, maxReconnectMs = 5000) {
        this.serverUrl = serverUrl;
        this.maxReconnectMs = maxReconnectMs;
        this.ws = null;
        this.handlers = [];
        this.reconnectTimer = null;
        this.roomId = "";
        this.userId = "";
    }
    connect(roomId, userId) {
        this.roomId = roomId;
        this.userId = userId;
        this.doConnect();
    }
    disconnect() {
        var _a;
        if (this.reconnectTimer)
            clearTimeout(this.reconnectTimer);
        (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
        this.ws = null;
    }
    send(message) {
        var _a;
        if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message));
        }
    }
    onMessage(handler) {
        this.handlers.push(handler);
        return () => {
            this.handlers = this.handlers.filter((h) => h !== handler);
        };
    }
    doConnect() {
        const url = `${this.serverUrl}?room=${encodeURIComponent(this.roomId)}&user=${encodeURIComponent(this.userId)}`;
        this.ws = new WebSocket(url);
        this.ws.onmessage = (event) => {
            try {
                const message = JSON.parse(event.data);
                for (const handler of this.handlers)
                    handler(message);
            }
            catch (_a) {
                // skip invalid messages
            }
        };
        this.ws.onclose = () => {
            this.reconnectTimer = setTimeout(() => this.doConnect(), this.maxReconnectMs);
        };
    }
}
// ── Version History ─────────────────────────────────────────────
class VersionHistory {
    constructor() {
        this.snapshots = [];
    }
    /** Create a new version snapshot. */
    create(name, userId, data, thumbnail) {
        const snapshot = {
            id: `v-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
            name,
            timestamp: Date.now(),
            userId,
            data: structuredClone(data),
            thumbnail,
        };
        this.snapshots.push(snapshot);
        return snapshot;
    }
    /** Get all snapshots ordered by timestamp. */
    getAll() {
        return [...this.snapshots].sort((a, b) => b.timestamp - a.timestamp);
    }
    /** Get a specific snapshot. */
    get(id) {
        return this.snapshots.find((s) => s.id === id);
    }
    /** Remove a snapshot. */
    remove(id) {
        const idx = this.snapshots.findIndex((s) => s.id === id);
        if (idx < 0)
            return false;
        this.snapshots.splice(idx, 1);
        return true;
    }
    /** Import snapshots from JSON. */
    import(data) {
        this.snapshots = [...data];
    }
    /** Export snapshots to JSON. */
    export() {
        return structuredClone(this.snapshots);
    }
}
// ── Comment Manager ─────────────────────────────────────────────
class CommentManager {
    constructor() {
        this.comments = [];
    }
    add(userId, userName, text, x, y) {
        const comment = {
            id: `c-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
            userId, userName, text, x, y,
            timestamp: Date.now(),
            resolved: false,
            replies: [],
        };
        this.comments.push(comment);
        return comment;
    }
    reply(commentId, userId, userName, text) {
        const comment = this.comments.find((c) => c.id === commentId);
        if (!comment)
            return null;
        const reply = {
            id: `r-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
            userId, userName, text,
            timestamp: Date.now(),
        };
        comment.replies.push(reply);
        return reply;
    }
    resolve(commentId) {
        const c = this.comments.find((c) => c.id === commentId);
        if (!c)
            return false;
        c.resolved = true;
        return true;
    }
    getAll() {
        return [...this.comments];
    }
    getUnresolved() {
        return this.comments.filter((c) => !c.resolved);
    }
    remove(commentId) {
        const idx = this.comments.findIndex((c) => c.id === commentId);
        if (idx < 0)
            return false;
        this.comments.splice(idx, 1);
        return true;
    }
}
// ── React Hooks ─────────────────────────────────────────────────
/** Hook for real-time collaboration. */
function useCollaboration(transport, roomId, currentUser) {
    const [users, setUsers] = React.useState([]);
    const [connected, setConnected] = React.useState(false);
    const operationHandlers = React.useRef([]);
    React.useEffect(() => {
        transport.connect(roomId, currentUser.id);
        setConnected(true);
        transport.send({ type: "join", user: currentUser });
        const unsubscribe = transport.onMessage((msg) => {
            switch (msg.type) {
                case "join":
                    setUsers((prev) => {
                        if (prev.some((u) => u.id === msg.user.id))
                            return prev;
                        return [...prev, msg.user];
                    });
                    break;
                case "leave":
                    setUsers((prev) => prev.filter((u) => u.id !== msg.userId));
                    break;
                case "cursor":
                    setUsers((prev) => prev.map((u) => (u.id === msg.userId ? { ...u, cursor: { x: msg.x, y: msg.y } } : u)));
                    break;
                case "selection":
                    setUsers((prev) => prev.map((u) => u.id === msg.userId
                        ? { ...u, selection: { x: msg.x, y: msg.y, w: msg.w, h: msg.h } }
                        : u));
                    break;
                case "operation":
                    for (const handler of operationHandlers.current)
                        handler(msg.operation);
                    break;
            }
        });
        return () => {
            transport.send({ type: "leave", userId: currentUser.id });
            unsubscribe();
            transport.disconnect();
            setConnected(false);
        };
    }, [roomId, currentUser.id]);
    const sendCursor = React.useCallback((x, y) => transport.send({ type: "cursor", userId: currentUser.id, x, y }), [currentUser.id]);
    const sendOperation = React.useCallback((op) => transport.send({
        type: "operation",
        operation: { ...op, timestamp: Date.now(), userId: currentUser.id },
    }), [currentUser.id]);
    const onOperation = React.useCallback((handler) => {
        operationHandlers.current.push(handler);
        return () => {
            operationHandlers.current = operationHandlers.current.filter((h) => h !== handler);
        };
    }, []);
    return { users, connected, sendCursor, sendOperation, onOperation };
}
/** Hook for managing comments. */
function useComments() {
    const [comments, setComments] = React.useState([]);
    const manager = React.useRef(new CommentManager());
    const addComment = React.useCallback((userId, userName, text, x, y) => {
        const c = manager.current.add(userId, userName, text, x, y);
        setComments(manager.current.getAll());
        return c;
    }, []);
    const replyToComment = React.useCallback((commentId, userId, userName, text) => {
        const r = manager.current.reply(commentId, userId, userName, text);
        setComments(manager.current.getAll());
        return r;
    }, []);
    const resolveComment = React.useCallback((commentId) => {
        manager.current.resolve(commentId);
        setComments(manager.current.getAll());
    }, []);
    const removeComment = React.useCallback((commentId) => {
        manager.current.remove(commentId);
        setComments(manager.current.getAll());
    }, []);
    return { comments, addComment, replyToComment, resolveComment, removeComment };
}
/** Hook for version history. */
function useVersionHistory() {
    const [snapshots, setSnapshots] = React.useState([]);
    const history = React.useRef(new VersionHistory());
    const createSnapshot = React.useCallback((name, userId, data, thumbnail) => {
        const s = history.current.create(name, userId, data, thumbnail);
        setSnapshots(history.current.getAll());
        return s;
    }, []);
    const removeSnapshot = React.useCallback((id) => {
        history.current.remove(id);
        setSnapshots(history.current.getAll());
    }, []);
    const getSnapshot = React.useCallback((id) => history.current.get(id), []);
    return { snapshots, createSnapshot, removeSnapshot, getSnapshot };
}

exports.CommentManager = CommentManager;
exports.VersionHistory = VersionHistory;
exports.WebSocketTransport = WebSocketTransport;
exports.useCollaboration = useCollaboration;
exports.useComments = useComments;
exports.useVersionHistory = useVersionHistory;
//# sourceMappingURL=collaboration.js.map
