// scripts/undo.js

import { world, system } from '@minecraft/server';
import { getCardinalDirection, runLoad, resolvePermutation } from './utils.js';
import { computeRegion, snapshotRegionSync, startAsyncSnapshot, restoreRegion, clearWaterBlocks, removeEntitiesInBox } from './region.js';
import { directionEffects } from './data.js';
import notes from './notes.js';

export const undoMap = new Map(); // player.id -> { dimId, blocks, min, max }

export function spawnWithUndo(player, structure, structureSizes) {
    const dir = getCardinalDirection(player);
    const cmd = structure.commands[dir];
    const offsets = structure.offsets?.[dir];

    if (!cmd || !offsets) {
        player.sendMessage?.(`§cNo command/offset for ${dir}.`);
        return;
    }

    const region = computeRegion(player, structure.id, offsets, structureSizes);
    if (region.error) {
        player.sendMessage?.(`§c${region.error}`);
        return;
    }

    const dim = player.dimension;
    const volume = (region.max.x - region.min.x + 1) *
                   (region.max.y - region.min.y + 1) *
                   (region.max.z - region.min.z + 1);

    // TAKE SNAPSHOT FIRST (before structure spawns)
    let snap;
    try {
        if (volume <= 15000) {
            // Small structures: immediate snapshot
            snap = snapshotRegionSync(dim, region.min, region.max);
        } else {
            // Large structures: still take immediate snapshot but smaller
            snap = snapshotRegionSync(dim, region.min, region.max);
        }
    } catch {
        player.sendMessage?.("§eUndo disabled for this structure (snapshot failed).");
        snap = null;
    }

    // SPAWN STRUCTURE
    const ok = runLoad(player, cmd);
    if (!ok) return;

    // PARTICLES FOR ALL SIZES
    for (const fx of (directionEffects[dir] || [])) {
        try { player.runCommand(fx); } catch {}
    }

    // Success message
    player.sendMessage?.(`§aThe ${structure.name} Spawned Successfully`);

    // Store undo data (only if snapshot succeeded)
    if (snap) {
        undoMap.set(player.id, {
            dimId: dim.id,
            blocks: snap,
            min: region.min,
            max: region.max,
            isLoading: false,
            timestamp: Date.now(),
        });
        
        if (volume > 15000) {
            player.sendMessage?.("§eUndo data ready!");
        }
    }

    // Optional helpful note
    const noteMessage = notes[structure.id];
    if (noteMessage) player.sendMessage(`§eNote: ${noteMessage}`);
}

// SILENT UNDO - No progress messages
export function undoLast(player) {
    const rec = undoMap.get(player.id);
    if (!rec) {
        player.sendMessage?.("§eNothing to undo.");
        return;
    }

    if (rec.isLoading) {
        player.sendMessage?.("§eUndo data still loading... please wait a moment.");
        return;
    }

    const dim = world.getDimension(rec.dimId);
    if (!dim) {
        player.sendMessage?.("§cUndo failed: dimension unavailable.");
        return;
    }

    const blocks = rec.blocks || [];
    
    if (blocks.length === 0) {
        player.sendMessage?.("§eNothing to undo.");
        undoMap.delete(player.id);
        return;
    }

    // NO PROGRESS MESSAGE - Silent undo
    if (blocks.length > 10000) {
        // Chunked undo for large data
        return undoChunked(dim, blocks, rec.min, rec.max, player);
    } else {
        // Direct undo for smaller structures
        return undoDirect(dim, blocks, rec.min, rec.max, player);
    }
}

// SILENT: Direct undo for smaller structures
function undoDirect(dim, blocks, min, max, player) {
    try {
        // Restore blocks one by one
        for (const blockData of blocks) {
            try {
                const block = dim.getBlock({ x: blockData.x, y: blockData.y, z: blockData.z });
                if (block) {
                    const perm = resolvePermutation(blockData.typeId, blockData.states || {});
                    block.setPermutation(perm);
                }
            } catch (e) {
                // Skip problematic blocks but continue
                continue;
            }
        }

        // Clean up water and entities after restoration
        system.runTimeout(() => {
            try {
                clearWaterBlocks(dim, min, max);
                removeEntitiesInBox(dim, min, max);
            } catch {}
        }, 2);

        undoMap.delete(player.id);
        try { player.runCommandAsync("playsound random.orb @s"); } catch {}
        player.sendMessage?.("§eUndo complete!");
        
    } catch (error) {
        player.sendMessage?.("§cFailed to undo area.");
    }
}

// SILENT: Chunked undo for big snapshots
function undoChunked(dim, blocks, min, max, player) {
    let processed = 0;

    function tick() {
        const start = Date.now();

        // Process blocks for max 100ms to prevent lag
        while (Date.now() - start < 100 && processed < blocks.length) {
            const blockData = blocks[processed];
            
            try {
                const block = dim.getBlock({ x: blockData.x, y: blockData.y, z: blockData.z });
                if (block) {
                    const perm = resolvePermutation(blockData.typeId, blockData.states || {});
                    block.setPermutation(perm);
                }
            } catch (e) {
                // Skip problematic blocks but continue
            }
            
            processed++;
        }

        if (processed >= blocks.length) {
            // All blocks processed - cleanup
            try {
                clearWaterBlocks(dim, min, max);
                removeEntitiesInBox(dim, min, max);
            } catch {}

            undoMap.delete(player.id);
            try { player.runCommandAsync("playsound random.orb @s"); } catch {}
            player.sendMessage?.("§eUndo complete!");
        } else {
            // NO PROGRESS MESSAGES - Silent processing
            system.runTimeout(tick, 1);
        }
    }

    tick();
}

// Add cleanup on player disconnect
world.afterEvents.playerLeave.subscribe((ev) => {
    undoMap.delete(ev.playerId);
});