(function () {
    const MB = window.MagicBot;

    // Store the raw full state to apply patches against
    let rawState = null;

    MB.on('packet_received', (msg) => {
        // Log to discover flow
        if (msg.type !== 'Ping' && msg.type !== 'Pong' && msg.type !== 'PartialState') {
            console.log(`[MagicBot] RX: ${msg.type}`, Object.keys(msg));
        }

        // --- DATA COLLECTION STREAM ---
        if (msg.type !== 'Ping' && msg.type !== 'Pong') { // Stream everything non-heartbeat
            fetch('http://localhost:5454', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ type: msg.type, payload: msg })
            }).catch(e => console.warn('[MagicBot] Data Stream Error:', e));
        }
        // ------------------------------

        if (msg.type === 'Welcome') {
            handleWelcome(msg);
        } else if (msg.type === 'State') {
            handleState(msg);
        } else if (msg.type === 'PartialState') {
            handlePartialState(msg);
        }
    });

    function handleWelcome(msg) {
        console.log("[MagicBot] Parsing Welcome...");
        rawState = msg.fullState.data; // Store raw root

        // Fix: Explicitly attach child (Quinoa/Game Scope) because 'data' doesn't contain it
        if (msg.fullState.child) {
            rawState.child = msg.fullState.child;
        }

        // Initial parse
        parseRawState();
    }

    function handleState(msg) {
        console.log("[MagicBot] Handling State message...", msg);
        // Replace or merge? Usually 'State' is a full dump of a specific scope.
        // For simplicity, we might just re-process. 
        // CAUTION: If it's just 'Quinoa' data, we attach it to rawState.child.data?
        // Let's assume it updates the relevant part of rawState if we knew where it fits.
        // For now, let's just try to process it directly.

        const data = msg.data || msg.state || msg;
        if (msg.scope === 'Quinoa' || (data.scope === 'Quinoa')) {
            // It's the game state!
            if (!rawState.child) rawState.child = {};
            rawState.child.data = data.data || data;
            parseRawState();
        }
    }

    function handlePartialState(msg) {
        if (!rawState) return;

        // Apply patches to rawState
        if (msg.patches && Array.isArray(msg.patches)) {
            // console.log(`[MagicBot] Applying ${msg.patches.length} patches...`);
            applyPatches(rawState, msg.patches);

            // Re-eval the simplified state
            parseRawState();
        }
    }

    function parseRawState() {
        if (!rawState) return;

        const root = rawState;

        // 1. Players
        if (root.players) MB.state.players = root.players;

        // 2. PlayerID
        if (!MB.state.playerId && root.hostPlayerId) {
            MB.state.playerId = root.hostPlayerId;
        }

        // 3. Game Data
        let gameData = null;
        if (root.child && root.child.data) {
            gameData = root.child.data;
        }

        if (gameData) {
            processGameData(gameData);
        } else if (!MB.state.players) {
            // Only warn if we have NOTHING
            console.warn("[MagicBot] State looks empty.");
        }

        MB.emit('state_updated', MB.state);
    }

    function processGameData(gameData) {
        if (gameData.shops) MB.state.shops = gameData.shops;
        if (gameData.timer) MB.state.timer = gameData.timer;

        if (gameData.userSlots && Array.isArray(gameData.userSlots)) {
            const userSlot = gameData.userSlots.find(s => s.playerId === MB.state.playerId);
            if (userSlot) {
                MB.state.coins = userSlot.data.coinsCount;
                MB.state.garden = userSlot.data.garden;
                MB.state.inventory = userSlot.data.inventory;
                MB.state.shopPurchases = userSlot.data.shopPurchases;
                MB.state.self = userSlot;
            } else {
                console.warn("[MagicBot] State: Could not find user slot for self:", MB.state.playerId);
            }
        } else {
            console.warn("[MagicBot] State: userSlots missing or invalid in gameData");
        }
    }

    // --- JSON PATCH UTILS ---
    function applyPatches(doc, patches) {
        patches.forEach(patch => {
            try {
                applyOperation(doc, patch);
            } catch (e) {
                console.warn("[MagicBot] Patch failed:", e, patch);
            }
        });
    }

    function applyOperation(doc, patch) {
        const path = patch.path.split('/').filter(p => p.length > 0);
        let current = doc;

        // Navigate to parent
        for (let i = 0; i < path.length - 1; i++) {
            const key = path[i];
            if (current[key] === undefined) {
                if (patch.op === 'add') current[key] = {}; // Auto-create path for add?
                else return; // Path doesn't exist
            }
            current = current[key];
        }

        const lastKey = path[path.length - 1];

        switch (patch.op) {
            case 'add':
            case 'replace':
                if (Array.isArray(current) && !isNaN(lastKey)) {
                    // Array set/insert
                    const idx = parseInt(lastKey);
                    if (patch.op === 'add') current.splice(idx, 0, patch.value);
                    else current[idx] = patch.value;
                } else {
                    current[lastKey] = patch.value;
                }
                break;
            case 'remove':
                if (Array.isArray(current)) {
                    current.splice(parseInt(lastKey), 1);
                } else {
                    delete current[lastKey];
                }
                break;
        }
    }
    console.log('[Magic Bot Extension] State module loaded.');
})();
