;(() => {
  // =========================================================
  // 配置区：把所有 FTB progress/task ID 集中放这里，便于维护
  // =========================================================
  const IDS = {
    READ_MANUAL: '08D7EA44A0456E45',          // 阅读手册（进度ID）
    STAND_ON_CASING: '55AD35F8774FE667',      // 站上列车机壳（进度ID）
    HELM_ASSEMBLY: '44902F4824ECA5C0',        // Shift+右键船舵（组装引导）（进度ID）
    ON_SHIP_AFTER_HELM: '22461918823DDE4F',   // 船上检测完成（进度ID）
    SET_SPAWN: '262B19178941C86A',            // 设置重生点（进度ID）
    SAIL: '0A0520B6CD610530'                  // 启航（进度ID）
  }

  // =========================================================
  // 配置区：物品/方块/检测参数
  // =========================================================
  const MANUAL = {
    id: 'minecraft:written_book',
    title: '[飞艇操作手册]',
    author: 'xiaobaiYWX',
    cooldownTicks: 20
  }

  const STAND_CHECK = {
    targetBlockId: 'create:railway_casing',
    intervalTicks: 10
  }

  const SHIP_CHECK = {
    intervalTicks: 20,
    delayAfterHelmTicks: 20 // 避免同tick推进冲突（你之前验证过需要延迟）
  }

  // =========================================================
  // 内部 key：统一命名，避免各脚本互相踩 persistentData
  // =========================================================
  const KEY = {
    // book cooldown
    bookCd: `vs_cd_book_${IDS.READ_MANUAL}`,

    // stage switches
    enableStand: `vs_enable_stand_${IDS.STAND_ON_CASING}`,
    enableShipAfterHelm: `vs_enable_ship_${IDS.ON_SHIP_AFTER_HELM}`,
    shipDueTick: `vs_ship_due_${IDS.ON_SHIP_AFTER_HELM}`,

    // delayed command queue
    // 用玩家 persistentData 存一个“最早执行tick”和“目标ID”，做成单槽队列（简单稳定）
    queueDue: 'vs_queue_due',
    queueId: 'vs_queue_id'
  }

  // =========================================================
  // 工具：排队执行 change_progress（统一从 server 执行，避免 @s 上下文坑）
  // - 单槽队列：如果同一玩家连续触发多个目标，会覆盖为“更晚/更新”的那个
  //   （你现在任务链基本串行，这种策略最稳、最少bug）
  // =========================================================
  function scheduleProgress(server, player, progressId, delayTicks) {
    const due = server.tickCount + (delayTicks ?? 1)

    // 如果已经有队列任务：保留“更早”的 or 直接覆盖？
    // 这里选择：如果新任务 due 更早 -> 用更早；否则覆盖为更新的目标
    // （也可以改成更复杂的数组队列，但单槽通常够用）
    const oldDue = player.persistentData.getLong(KEY.queueDue)
    const oldId = String(player.persistentData.getString(KEY.queueId) ?? '')

    if (oldId && oldDue > 0) {
      // 如果队列里已有任务且时间更早，避免被覆盖导致“丢任务”
      if (due >= oldDue) return
    }

    player.persistentData.putLong(KEY.queueDue, due)
    player.persistentData.putString(KEY.queueId, String(progressId))
  }

  function runQueued(server, player) {
    const due = player.persistentData.getLong(KEY.queueDue)
    if (!due || due <= 0) return
    if (server.tickCount < due) return

    const id = String(player.persistentData.getString(KEY.queueId) ?? '')
    if (!id) {
      player.persistentData.putLong(KEY.queueDue, 0)
      return
    }

    // 统一执行方式：server + username
    server.runCommandSilent(`ftbquests change_progress ${player.username} complete ${id}`)

    // 清空队列
    player.persistentData.putLong(KEY.queueDue, 0)
    player.persistentData.putString(KEY.queueId, '')
  }

  // =========================================================
  // 触发器 1：阅读手册（右键成书） -> 完成 READ_MANUAL，并开启脚下方块检测
  // =========================================================
  ItemEvents.rightClicked(event => {
    if (!event.server) return
    if (event.hand && String(event.hand) !== 'MAIN_HAND') return

    const player = event.player
    const item = event.item
    if (!item || item.isEmpty()) return
    if (item.id !== MANUAL.id) return

    const nbt = item.nbt
    if (!nbt) return

    const title = String(nbt.title ?? nbt.filtered_title ?? '')
    const author = String(nbt.author ?? '')

    if (title !== MANUAL.title) return
    if (author !== MANUAL.author) return

    // 推进任务（延迟 1 tick，规避书本 use() 时序干扰）
    scheduleProgress(event.server, player, IDS.READ_MANUAL, 1)
    //player.tell('已检测到阅读《飞艇操作手册》')

    // 直接开启下一阶段检测（绕开 FTB completed 事件）
    player.persistentData.putBoolean(KEY.enableStand, true)
    //player.tell('检测已开启：请站到指定方块上。')
  })

  // =========================================================
  // 触发器 2：Shift+右键船舵（组装引导） -> 完成 HELM_ASSEMBLY 并开启“在船上”检测
  // =========================================================
  BlockEvents.rightClicked(event => {
    if (!event.server) return
    if (event.hand && String(event.hand) !== 'MAIN_HAND') return

    const player = event.player
    if (!player.isCrouching()) return
    if (event.block.id !== 'vs_eureka:oak_ship_helm') return

    // 推进组装引导（延迟 1 tick，减少同tick冲突）
    scheduleProgress(event.server, player, IDS.HELM_ASSEMBLY, 1)

    // 开启在船上检测，并设置最早完成时间（避免同tick推进冲突）
    player.persistentData.putBoolean(KEY.enableShipAfterHelm, true)
    player.persistentData.putLong(KEY.shipDueTick, event.server.tickCount + SHIP_CHECK.delayAfterHelmTicks)

    //player.tell('船舵任务完成：请上船以继续。')
  })

  // =========================================================
  // 触发器 3：设置重生点（床/重生锚） -> 完成 SET_SPAWN
  // =========================================================
  BlockEvents.rightClicked(event => {
    if (!event.server) return
    if (event.hand && String(event.hand) !== 'MAIN_HAND') return

    const player = event.player
    const id = event.block.id
    if (!(id.endsWith('_bed') || id === 'minecraft:respawn_anchor')) return

    scheduleProgress(event.server, player, IDS.SET_SPAWN, 1)
    player.tell('你设置了重生点')
  })

  // =========================================================
  // 触发器 4：启航（右键船舵）+ 必须在船上 -> 完成 SAIL
  // 注意：和 Shift+右键区分：这里不要求 crouch
  // =========================================================
  BlockEvents.rightClicked(event => {
    if (!event.server) return
    if (event.hand && String(event.hand) !== 'MAIN_HAND') return

    const player = event.player
    if (event.block.id !== 'vs_eureka:oak_ship_helm') return

    // 如果玩家按住 Shift，这次右键会同时满足“组装引导”触发器和这里。
    // 为避免混乱：Shift 时不走“启航”
    if (player.isCrouching()) return

    const onShip = VS.js().isPlayerOnShipStrict(player)
    if (!onShip) {
      //player.tell('你当前不在飞艇上，无法启航。')
      return
    }

    scheduleProgress(event.server, player, IDS.SAIL, 1)
    //player.tell('启航！')
  })

  // =========================================================
  // 唯一 tick：执行队列 + 两种阶段检测
  // =========================================================
  ServerEvents.tick(event => {
    const server = event.server
    const tick = server.tickCount

    // 1) 执行所有玩家的“延迟命令队列”
    server.players.forEach(p => runQueued(server, p))

    // 2) 脚下方块检测（只对 enableStand=true 的玩家跑）
    if (tick % STAND_CHECK.intervalTicks === 0) {
      server.players.forEach(player => {
        if (!player.persistentData.getBoolean(KEY.enableStand)) return

        const world = player.level
        const x = Math.floor(player.x)
        const y = Math.floor(player.y - 1)
        const z = Math.floor(player.z)

        const block = world.getBlock(x, y, z)
        if (block.id !== STAND_CHECK.targetBlockId) return

        scheduleProgress(server, player, IDS.STAND_ON_CASING, 1)
        //player.tell('登上飞艇')

        player.persistentData.putBoolean(KEY.enableStand, false)
      })
    }

    // 3) 在船上检测（只对 enableShipAfterHelm=true 的玩家跑）
    if (tick % SHIP_CHECK.intervalTicks === 0) {
      server.players.forEach(player => {
        if (!player.persistentData.getBoolean(KEY.enableShipAfterHelm)) return

        const due = player.persistentData.getLong(KEY.shipDueTick)
        if (due > 0 && tick < due) return

        const onShip = VS.js().isPlayerOnShipStrict(player)
        if (!onShip) return

        scheduleProgress(server, player, IDS.ON_SHIP_AFTER_HELM, 1)
        //player.tell('已检测到在船上，后续任务完成！')

        player.persistentData.putBoolean(KEY.enableShipAfterHelm, false)
        player.persistentData.putLong(KEY.shipDueTick, 0)
      })
    }
  })
})()
