import { world, ItemStack } from "@minecraft/server";

// ======= Global State =======
// Stores per player { baseLoc, loc, origType }
const playerPrevLightState = new Map();
// Track which players have toggled this press
const toggledThisUse = new Set();

// Global queue for failed resets
// Each entry: { dimensionId, loc: {x,y,z}, origType }
// const pendingReplacements = [];

const PENDING_KEY = "flashlight.pending";
let pendingReplacements = [];

function loadPending() {
  const saved = world.getDynamicProperty(PENDING_KEY);
  if (saved) {
    try {
      pendingReplacements = JSON.parse(saved);
    } catch {
      pendingReplacements = [];
    }
  }
}

loadPending();

const MAX_DIST_WITH_RAYCAST_HIT = 18;
const MAX_DIST_NO_RAYCAST_HIT = 10;

// Offset per face
const faceOffsets = {
  Up: { x: 0, y: 1, z: 0 },
  Down: { x: 0, y: -1, z: 0 },
  North: { x: 0, y: 0, z: -1 },
  South: { x: 0, y: 0, z: 1 },
  West: { x: -1, y: 0, z: 0 },
  East: { x: 1, y: 0, z: 0 },
};

function positionsEqual(a, b) {
  return a.x === b.x && a.y === b.y && a.z === b.z;
}

// ======= Main Flashlight Logic (Player Location Version) =======
export function flashlight(player) {
  const saved = world.getDynamicProperty(PENDING_KEY);
  if (saved) {
    try {
      pendingReplacements = JSON.parse(saved);
    } catch {}
  }

  // On each new player join:
  world.afterEvents.playerSpawn.subscribe((ev) => {
    processPendingReplacementsForPlayer(ev.player);
  });

  const dimension = world.getDimension(player.dimension.id);

  // 1) Get the player block location
  const baseLoc = {
    x: Math.floor(player.location.x),
    y: Math.floor(player.location.y),
    z: Math.floor(player.location.z),
  };

  // 2) Check previous state
  const prevState = playerPrevLightState.get(player.id);
  let candidateLoc, origType;
  if (prevState && positionsEqual(prevState.baseLoc, baseLoc)) {
    // Reuse previous location
    candidateLoc = prevState.loc;
    origType = prevState.origType;
  } else {
    // 3) Search nearby for empty block to place light
    const checkPoints = generateCheckPoints();
    let found = false;
    for (const off of checkPoints) {
      const loc = { x: baseLoc.x + off.x, y: baseLoc.y + off.y, z: baseLoc.z + off.z };
      const blk = dimension.getBlock(loc);
      if (!blk) break; // Chunk not loaded

      if (blk.typeId === "minecraft:air") {
        candidateLoc = loc;
        origType = "minecraft:air";
        found = true;
        break;
      }
    }

    if (!found) {
      if (prevState) resetOrQueue(prevState, player.id, dimension.id);
      return;
    }
  }

  // 4) Reset old light block if needed
  if (prevState && !positionsEqual(prevState.loc, candidateLoc)) {
    resetOrQueue(prevState, player.id, dimension.id);
  }

  // 5) Place new light block
  const target = dimension.getBlock(candidateLoc);
  if (target && target.typeId !== "minecraft:light_block_11") {
    target.setType("minecraft:light_block_11");

    // Remove old pending entry
    if (prevState) {
      for (let i = pendingReplacements.length - 1; i >= 0; i--) {
        const p = pendingReplacements[i];
        if (p.dimensionId === player.dimension.id && sameLoc(p.loc, prevState.loc)) {
          pendingReplacements.splice(i, 1);
        }
      }
    }

    // Add new pending entry
    pendingReplacements.push({
      playerId: player.id,
      dimensionId: player.dimension.id,
      loc: candidateLoc,
      origType: origType,
    });

    savePending();
  }

  // 6) Save current state
  playerPrevLightState.set(player.id, {
    baseLoc,
    loc: candidateLoc,
    origType,
  });
}

// Helper function: generates your 3×3 area (+ vertical etc.)
function generateCheckPoints() {
  return [
    { x: 0, y: 0, z: 0 },
    { x: 0, y: 1, z: 0 },
    { x: 0, y: 2, z: 0 },
    { x: 0, y: -1, z: 0 },
    { x: 0, y: -2, z: 0 },
    { x: 1, y: 0, z: 0 },
    { x: -1, y: 0, z: 0 },
    { x: 0, y: 0, z: 1 },
    { x: 0, y: 0, z: -1 },
    { x: 0, y: 1, z: 1 },
    { x: 0, y: 1, z: -1 },
    { x: 1, y: 1, z: 0 },
    { x: -1, y: 1, z: 0 },
    { x: 1, y: 1, z: 1 },
    { x: -1, y: 1, z: 1 },
    { x: 1, y: 1, z: -1 },
    { x: -1, y: 1, z: -1 },
    { x: 2, y: 0, z: 0 },
    { x: -2, y: 0, z: 0 },
    { x: 0, y: 0, z: 2 },
    { x: 0, y: 0, z: -2 },
    { x: 2, y: 1, z: 0 },
    { x: -2, y: 1, z: 0 },
    { x: 0, y: 1, z: 2 },
    { x: 0, y: 1, z: -2 },
    { x: 1, y: 2, z: 1 },
    { x: -1, y: 2, z: 1 },
    { x: 1, y: 2, z: -1 },
    { x: -1, y: 2, z: -1 },
    { x: 2, y: 0, z: 2 },
    { x: -2, y: 0, z: 2 },
    { x: 2, y: 0, z: -2 },
    { x: -2, y: 0, z: -2 },
    { x: 2, y: 2, z: 0 },
    { x: -2, y: 2, z: 0 },
    { x: 0, y: 2, z: 2 },
    { x: 0, y: 2, z: -2 },
    { x: 2, y: 2, z: 2 },
    { x: -2, y: 2, z: 2 },
    { x: 2, y: 2, z: -2 },
    { x: -2, y: 2, z: -2 },
  ];
}

// Helper function: try to reset, otherwise queue + remove prevState
function resetOrQueue(state, playerId, dimensionId) {
  const dim = world.getDimension(dimensionId);
  try {
    const block = dim.getBlock(state.loc);
    if (!block) throw new Error("Chunk/Block not loaded");
    block.setType(state.origType);
  } catch (e) {
    pendingReplacements.push({
      playerId: playerId, // ← new
      dimensionId,
      loc: state.loc,
      origType: state.origType,
    });
    savePending();
  }
  playerPrevLightState.delete(playerId);
}

// ======= Player-specific queue processing =======
function processPendingReplacementsForPlayer(player) {
  const dim = world.getDimension(player.dimension.id);
  const onlineIds = new Set(world.getPlayers().map((p) => p.id));

  // ── 1) Handle anyone who is now offline ──
  for (let i = pendingReplacements.length - 1; i >= 0; i--) {
    const p = pendingReplacements[i];
    if (!onlineIds.has(p.playerId)) {
      // That player has disconnected—attempt to reset their light block now:
      let didReset = false;
      try {
        const deadDim = world.getDimension(p.dimensionId);
        const blk = deadDim.getBlock(p.loc);
        if (blk && blk.typeId === "minecraft:light_block_11") {
          blk.setType(p.origType);
        }
        // If we reached this line without throwing, the chunk was loaded,
        // so we consider the reset “successful” (even if the block type
        // wasn’t exactly what we expected).
        didReset = true;
      } catch {
        // Chunk was unloaded or something else went wrong. Do NOT splice here;
        // leave the entry in pendingReplacements so we can retry later.
      }

      if (didReset) {
        pendingReplacements.splice(i, 1);
      }
    }
  }

  // ── 2) Now handle any remaining entries that belong to _this_ player in this dimension ──
  for (let i = pendingReplacements.length - 1; i >= 0; i--) {
    const p = pendingReplacements[i];
    if (p.playerId !== player.id || p.dimensionId !== player.dimension.id) continue;

    const blk = dim.getBlock(p.loc);
    if (blk && blk.typeId === "minecraft:light_block_11") {
      try {
        blk.setType(p.origType);
      } catch {
        // Chunk got unloaded mid‐reset. In that case, skip splicing and let it retry next time.
        continue;
      }
      pendingReplacements.splice(i, 1);
    } else if (blk && blk.typeId !== "minecraft:light_block_11") {
      // Someone else changed that block already—just drop our pending entry.
      pendingReplacements.splice(i, 1);
    }
  }

  savePending();
}

// (2) The standalone “offline” function from above:
function processPendingReplacementsForOfflinePlayers() {
  const onlineIds = new Set(world.getPlayers().map((p) => p.id));

  for (let i = pendingReplacements.length - 1; i >= 0; i--) {
    const entry = pendingReplacements[i];
    if (!onlineIds.has(entry.playerId)) {
      let didReset = false;
      try {
        const deadDim = world.getDimension(entry.dimensionId);
        const blk = deadDim.getBlock(entry.loc);
        if (blk && blk.typeId === "minecraft:light_block_11") {
          blk.setType(entry.origType);
        }
        didReset = true;
      } catch {
        // Chunk unloaded → don’t splice yet
      }
      if (didReset) {
        pendingReplacements.splice(i, 1);
      }
    }
  }

  savePending();
}

export function flashlightHandler(player) {
  // 1) Get the equippable component
  const equip = player.getComponent("equippable");
  if (!equip) return;
  const mainhandItem = equip.getEquipment("Mainhand");
  // 3) If the correct item is in hand → toggle light on/off
  if (player.hasTag("hb_cb:trigger_light")) {
  // if (mainhandItem?.typeId === "minecraft:apple") {
    processPendingReplacementsForOfflinePlayers();
    flashlight(player);
  } else {
    // 4) Item put away: reset old state / queue
    const prevState = playerPrevLightState.get(player.id);
    if (prevState) {
      resetOrQueue(prevState, player.id, player.dimension.id);
      playerPrevLightState.delete(player.id);
    }
    // 5) Process all remaining pending resets
    processPendingReplacementsForPlayer(player);
  }
}

function savePending() {
  world.setDynamicProperty(PENDING_KEY, JSON.stringify(pendingReplacements));
}

function sameLoc(a, b) {
  return a.x === b.x && a.y === b.y && a.z === b.z;
}
