/**
 * Пассивное фантазирование: один шаг построения ассоциативной цепочки по кадрам эпизодической памяти (ЭП).
 *
 * Идея: в учительском кадре зафиксировано «оператор ответил стимулом X» → responseStimulusImageId = X.
 * Мы задаём X как текущий фокус (опорный стимул), находим такие кадры, выбираем самый «сильный» по |effect|,
 * из него берём образ действия (ОД) и делаем его следующим X для следующего пульса — получается сюжет «если
 * в жизни было так, то дальше…» без реального стимула среды (имитация через runActionImage + обновление инфокартины).
 *
 * Режим «пассивного» мышления задаётся полем цикла thinkingMode === 'passive' (не инфокартиной).
 * Главный цикл дописывает сюжет в InfoPicture.fantasmPlotRules; фоновый — только в cycle.fantasizmArr и
 * fantazmingPlot. Пока цикл пассивен, ур.3–4 не крутятся (Consciousness_runOneStep). См. about_passive_fantasizming.md.
 *
 * Зависимости: EpisodicMemory_getFramesByResponseStimulus, EpisodicMemory_pickBestFrameByAbsEffect,
 * runActionImage, Info_updateFromStimulus, Consciousness_cycleAppendLog, info_insight (порог FANTASMI_INSITE_COMPARE).
 */
(function (global) {
  'use strict';

  /** Порог |effect| по умолчанию: выше — считаем кадр настолько значимым, что нужен инсайт (переход в target). */
  var DEFAULT_INSITE_COMPARE = 5;

  function puls() {
    return typeof global.cpuls === 'number' ? global.cpuls : 0;
  }

  /** Лог цикла ФО: фоновые циклы — с force, чтобы сообщение не терялось. */
  function appendCycleLog(cycle, text, opts) {
    if (typeof global.Consciousness_cycleAppendLog !== 'function') return;
    var o = opts && typeof opts === 'object' ? opts : {};
    if (o.force === undefined) o.force = !cycle.isMainCycle;
    global.Consciousness_cycleAppendLog(cycle, text, puls(), o);
  }

  /** Инициализация структуры сюжета на цикле: цепочка звеньев и последний ОД как «якорь» для следующего поиска по ЭП. */
  function ensureFantazmingPlot(cycle) {
    if (!cycle) return { chain: [], lastGeneratedActionId: 0 };
    if (!cycle.fantazmingPlot || typeof cycle.fantazmingPlot !== 'object') {
      cycle.fantazmingPlot = { chain: [], lastGeneratedActionId: 0 };
    }
    if (!Array.isArray(cycle.fantazmingPlot.chain)) cycle.fantazmingPlot.chain = [];
    if (cycle.fantazmingPlot.lastGeneratedActionId == null) cycle.fantazmingPlot.lastGeneratedActionId = 0;
    return cycle.fantazmingPlot;
  }

  /**
   * Добавить в инфокартину фрагмент «Сюжет фантазма» (ячейка fantasmPlotRules) для отображения оператору.
   * Обрезка хвоста — чтобы массив не разрастался бесконечно при длинной цепочке.
   */
  function appendFantasmSnapshotToInfoPicture(frameLike) {
    var info = typeof global.Info_initIfNeeded === 'function' ? global.Info_initIfNeeded() : global.InfoPicture;
    if (!info) return;
    if (!Array.isArray(info.fantasmPlotRules)) info.fantasmPlotRules = [];
    info.fantasmPlotRules.push({
      perceptionNodeId: frameLike.perceptionNodeId || 0,
      stimulusImageId: frameLike.stimulusImageId || 0,
      situationId: frameLike.situationId || 0,
      actionImageId: frameLike.actionImageId != null ? frameLike.actionImageId : null,
      effect: frameLike.effect != null ? frameLike.effect : null,
      responseStimulusImageId: frameLike.responseStimulusImageId != null ? frameLike.responseStimulusImageId : null,
      episodeIndex: frameLike._episodeIndex != null ? frameLike._episodeIndex : null
    });
    while (info.fantasmPlotRules.length > 80) info.fantasmPlotRules.shift();
  }

  /** Дублирование снимка кадра в cycle.fantasizmArr — оперативная лента для отладки/будущей связи с другими циклами. */
  function syncFantasizmArr(cycle, frameLike) {
    if (!Array.isArray(cycle.fantasizmArr)) cycle.fantasizmArr = [];
    cycle.fantasizmArr.push({
      perceptionNodeId: frameLike.perceptionNodeId || 0,
      stimulusImageId: frameLike.stimulusImageId || 0,
      situationId: frameLike.situationId || 0,
      actionImageId: frameLike.actionImageId != null ? frameLike.actionImageId : null,
      effect: frameLike.effect != null ? frameLike.effect : null,
      responseStimulusImageId: frameLike.responseStimulusImageId != null ? frameLike.responseStimulusImageId : null
    });
  }

  /**
   * Один шаг итерации пассивного режима для данного цикла.
   *
   * Возврат true означает, что цепочка логически продолжена (в т.ч. ветка инсайта без мотора).
   * false — нет опорного стимула, нет кадров в ЭП или нет подходящих функций памяти.
   *
   * @param {Object} cycle — активный цикл сознания (status === 'running')
   * @returns {boolean}
   */
  function info_fantasizming(cycle) {
    if (!cycle || cycle.status !== 'running') return false;
    if (cycle.thinkingMode !== 'passive') return false;

    var inf = global.InfoPicture;
    var isMain = cycle.isMainCycle === true;

    var plot = ensureFantazmingPlot(cycle);
    // --- Шаг 1: опорный стимул (FinalImageId), с которым ищем «учительские» кадры по responseStimulusImageId.
    // Приоритет: продолжение цепочки (последний ОД шага как новый «ответный» идентификатор) → контекст создания цикла
    // → текущая цель осмысления в инфокартине (часто 0 в пассиве).
    var currentStimulusId = 0;
    if (plot.lastGeneratedActionId) {
      currentStimulusId = parseInt(plot.lastGeneratedActionId, 10) || 0;
    }
    if (!currentStimulusId) {
      var cx = cycle.context || {};
      if (cx.actual_stimul_ID != null) currentStimulusId = parseInt(cx.actual_stimul_ID, 10) || 0;
    }
    if (!currentStimulusId && isMain && inf && typeof inf.currentGoalId === 'number' && inf.currentGoalId > 0) {
      currentStimulusId = inf.currentGoalId;
    }
    if (!currentStimulusId) {
      appendCycleLog(cycle, 'Фантазм: нет опорного стимула (currentStimulusId) — шаг пропущен.');
      return false;
    }

    // --- Шаг 2: все кадры ЭП, где операторский ответ совпал с нашим currentStimulusId; лучший по модулю эффекта.
    var getFrames = global.EpisodicMemory_getFramesByResponseStimulus;
    var pickBest = global.EpisodicMemory_pickBestFrameByAbsEffect;
    if (typeof getFrames !== 'function' || typeof pickBest !== 'function') {
      appendCycleLog(cycle, 'Фантазм: нет EpisodicMemory_getFramesByResponseStimulus / pickBestFrameByAbsEffect — сюжет не обновлён.');
      return false;
    }

    var candidates = getFrames(currentStimulusId);
    var bestFrame = pickBest(candidates);
    if (!bestFrame || !bestFrame.actionImageId) {
      appendCycleLog(cycle, 'Фантазм: нет кадра с responseStimulusImageId=' + currentStimulusId + ' — цепочка прервана.');
      return false;
    }

    var actionId = parseInt(bestFrame.actionImageId, 10) || 0;
    if (!actionId) {
      appendCycleLog(cycle, 'Фантазм: кадр ЭП без валидного actionImageId — сюжет не обновлён.');
      return false;
    }

    var eff = typeof bestFrame.effect === 'number' ? bestFrame.effect : 0;
    var threshold = typeof global.FANTASMI_INSITE_COMPARE === 'number' ? global.FANTASMI_INSITE_COMPARE : DEFAULT_INSITE_COMPARE;

    // --- Шаг 3: зафиксировать звено сюжета; lastGeneratedActionId = ОД — на следующем пульсе поиск пойдёт
    // от «как будто оператор ответил этим действием-образом» (сопоставление с полем responseStimulusImageId в данных).
    plot.chain.push({
      fromStimulusId: currentStimulusId,
      actionImageId: actionId,
      episodeIndex: bestFrame._episodeIndex != null ? bestFrame._episodeIndex : null,
      effect: eff
    });
    plot.lastGeneratedActionId = actionId;
    cycle.currentFantazmingStimulus = actionId;

    syncFantasizmArr(cycle, bestFrame);
    if (isMain) {
      appendFantasmSnapshotToInfoPicture(bestFrame);
    }

    // Каждое успешное обновление сюжета (цепочка + fantasizmArr [+ fantasmPlotRules у главного]) — отдельная запись в лог цикла.
    var storyLog = 'Фантазм [сюжет]: ответный стимул=' + currentStimulusId + ' → ОД=' + actionId +
      ', effect=' + eff + ', эпизод #' + (bestFrame._episodeIndex != null ? bestFrame._episodeIndex : '—') +
      (isMain ? ', инфо-ячейка «Сюжет фантазма» обновлена.' : ', контекст в fantasizmArr / fantazmingPlot.');
    appendCycleLog(cycle, storyLog);

    // --- Шаг 4а: экстремальный эффект → инсайт: целевой режим цикла, цель «значение».
    // Намеренно не вызываем runActionImage: смысловой сдвиг важнее имитации мотора в этом тике.
    if (Math.abs(eff) >= threshold && typeof global.info_insight === 'function') {
      global.info_insight(cycle, { fantasyInsight: true, effect: eff });
      if (isMain && global.InfoPicture) {
        global.InfoPicture.thinkingSource = 'fantasy';
      }
      if (isMain && typeof global.Info_incrementStep === 'function') {
        global.Info_incrementStep();
      }
      appendCycleLog(cycle, 'Фантазм: инсайт |effect|≥' + String(threshold) + ' — цель «значение», переход в целевой режим.', { force: true });
      return true;
    }

    // --- Шаг 4б: главный цикл — имитация ответа и обновление глобальной инфокартины; фоновый — только контекст в цикле.
    if (isMain) {
      if (typeof global.runActionImage === 'function') {
        global.runActionImage(actionId);
      }
      if (typeof global.Info_updateFromStimulus === 'function') {
        var showStim = bestFrame.stimulusImageId || actionId;
        global.Info_updateFromStimulus(showStim, 'imagination');
      }
      if (global.InfoPicture) {
        global.InfoPicture.thinkingSource = 'fantasy';
      }
      if (typeof global.Info_incrementStep === 'function') {
        global.Info_incrementStep();
      }
    }

    return true;
  }

  global.info_fantasizming = info_fantasizming;
  /** Глобальный порог инсайта из фантазирования; можно переопределить до первого шага. */
  global.FANTASMI_INSITE_COMPARE = DEFAULT_INSITE_COMPARE;
})(window);
