/**
 * Обработка генетически заданных моторных рефлексов и вывод активности Beast.
 *
 * Логика:
 * - При активации стимулов (мотивирующих, позитивных, негативных) по картам
 *   *_GeneticReflexes выбираются базовые действия (window.BasicActions).
 * - Для выбранного действия в блоке "Активность Beast" показывается его название
 *   (например, "Плачет", "Хватает" и т.п.).
 * - Для специальных действий с Id == 100 раз в 5 пульсов выводится случайный
 *   "физиологический звук" из window.BasicVerbal: "Произносит: ма-ма-ма".
 * - Для действий из window.ActionProlongator дополнительно по каждому пульсу
 *   уменьшается указанный витал на shift, пока он не достигнет 0.
 *
 * Ожидается вызов processGeneticReflexes() из центрального пульса (puls.js)
 * один раз на каждый пульс.
 */
(function (global) {
  'use strict';

  var prevMotivation = [];
  var prevPositiv = [];
  var prevNegative = [];

  var prolongedActions = {}; // actionId -> { vitalID, shift }
  var verbalReflexActive = false;
  var verbalPulseCounter = 0;

  /**
   * Отложенность выполнения рефлекса: ОР не даёт выполнить рефлекс сразу; он ставится в очередь
   * delayedReflexes из ОР (addBlockedReflexToQueue) с branchId активной ветки. Если в stimuls_consciousness
   * автоматизм запущен, по этой ветке рефлексы удаляются из очереди (removeDelayedReflexesForBranch).
   */
  var delayedReflexes = [];
  var OR_BLOCK_PULSES = 2;

  function getCurrentFullFinalImageId() {
    if (typeof global.FinalImages_getOrCreateFinalImageId !== 'function') return 0;
    // Глобальные id стимулов (type*1000+localId), чтобы один числовой id в разных типах не коллидировал.
    var ids = typeof global.StimulusGlobalId_getActiveGlobalIds === 'function'
      ? global.StimulusGlobalId_getActiveGlobalIds()
      : [];
    return ids.length ? global.FinalImages_getOrCreateFinalImageId(ids) : 0;
  }

  /**
   * Обработка отложенных рефлексов (задержанных ОР на 2 пульса).
   * Если автоматизм был запущен в stimuls_consciousness, рефлекс по этой ветке уже удалён из очереди
   * (removeDelayedReflexesForBranch), сравнение по действию не нужно — только по branchId.
   */
  function runDelayedReflexes() {
    var pulse = typeof global.Cur_puls_val === 'number' ? global.Cur_puls_val : 0;
    for (var i = delayedReflexes.length - 1; i >= 0; i--) {
      var d = delayedReflexes[i];
      if (d.untilPulse > pulse) continue;
      delayedReflexes.splice(i, 1);
      startAction(d.actionId, d.sourceType, d.stimId, d.noConditionalReflex ? { noConditionalReflex: true } : undefined);
    }
  }

  /**
   * Удалить из очереди отложенных рефлексов все записи по данной ветке дерева.
   * Вызывается из stimuls_consciousness при запуске автоматизма — рефлекс для этой ветки аннулируется без сравнения действий.
   */
  function removeDelayedReflexesForBranch(branchId) {
    if (branchId == null) return;
    for (var i = delayedReflexes.length - 1; i >= 0; i--) {
      if (delayedReflexes[i].branchId === branchId) {
        delayedReflexes.splice(i, 1);
      }
    }
  }

  /**
   * Первый «новый» генетический рефлекс по текущим активным стимулам (для постановки в очередь из ОР).
   */
  function getBlockedGeneticReflexEvent() {
    var activeMotiv = Array.isArray(global.ActiveMotivationStimuls) ? global.ActiveMotivationStimuls.slice() : [];
    var activePositiv = Array.isArray(global.ActivePositivStimuls) ? global.ActivePositivStimuls.slice() : [];
    var activeNegative = Array.isArray(global.ActiveNegativeStimuls) ? global.ActiveNegativeStimuls.slice() : [];
    var events = [];
    events = events.concat(
      collectNewActionEvents(activeMotiv, prevMotivation, global.StimulMotivarion_GeneticReflexes, 'motivation'),
      collectNewActionEvents(activePositiv, prevPositiv, global.StimulPositiv_GeneticReflexes, 'positive'),
      collectNewActionEvents(activeNegative, prevNegative, global.StimulNegative_GeneticReflexes, 'negative')
    );
    return events.length ? events[0] : null;
  }

  /**
   * Поставить заблокированный ОР генетический рефлекс в очередь (вызов из ОР при setBlockedReflex).
   * Рефлекс привязан к ветке branchId; при запуске автоматизма по этой ветке очередь чистится через removeDelayedReflexesForBranch.
   */
  function addBlockedReflexToQueue(branchId) {
    if (branchId == null) return;
    var ev = getBlockedGeneticReflexEvent();
    if (!ev) return;
    var pulse = typeof global.Cur_puls_val === 'number' ? global.Cur_puls_val : 0;
    delayedReflexes.push({
      actionId: ev.actionId,
      sourceType: ev.sourceType,
      stimId: ev.stimId,
      untilPulse: pulse + OR_BLOCK_PULSES,
      noConditionalReflex: true,
      branchId: branchId
    });
  }

  function getBasicActionName(actionId) {
    if (!global.BasicActions || !Array.isArray(global.BasicActions)) {
      return '';
    }
    for (var i = 0; i < global.BasicActions.length; i++) {
      if (global.BasicActions[i].Id === actionId) {
        return global.BasicActions[i].name || '';
      }
    }
    return '';
  }

  var activityHighlightTimeout = null;

  function setBeastActivityText(text) {
    if (!global.document) return;
    var block = global.document.getElementById('beastActivityBlock');
    if (!block) return;

    // Сбрасываем предыдущий таймер и возвращаем базовый стиль
    if (activityHighlightTimeout !== null) {
      global.clearTimeout(activityHighlightTimeout);
      activityHighlightTimeout = null;
    }

    block.textContent = text || '';

    // Кратковременное визуальное выделение новой реакции (1 секунда)
    if (text) {
      // Во сне, пока крутится фантазм по пустому кадру ЭП — не яркая подсветка, а бледно-серый текст активности.
      var paleDream = global.SleepDreams_paleActivityBeast === true;
      if (paleDream) {
        block.style.color = '#9e9e9e';
        block.style.fontWeight = 'normal';
        block.style.fontSize = '13pt';
        activityHighlightTimeout = global.setTimeout(function () {
          block.style.color = '#b0b0b0';
          block.style.fontWeight = '';
          block.style.fontSize = '12pt';
          activityHighlightTimeout = null;
        }, 800);
      } else {
        block.style.color = '#1565C0';       // синий цвет
        block.style.fontWeight = 'bold';
        block.style.fontSize = '18pt';

        activityHighlightTimeout = global.setTimeout(function () {
          // Возврат к стандартному виду (наследуемому из CSS)
          block.style.color = '';
          block.style.fontWeight = '';
          block.style.fontSize = '';
          activityHighlightTimeout = null;
        }, 1000);
      }
    }
  }

  function registerProlongatedAction(actionId) {
    if (!global.ActionProlongator || !Array.isArray(global.ActionProlongator)) {
      return;
    }
    for (var i = 0; i < global.ActionProlongator.length; i++) {
      var cfg = global.ActionProlongator[i];
      if (cfg && cfg.actionID === actionId) {
        prolongedActions[String(actionId)] = {
          vitalID: cfg.vitalID,
          shift: cfg.shift
        };
        break;
      }
    }
  }

  function getStimulusName(sourceType, stimId) {
    var arr = null;
    if (sourceType === 'motivation') {
      arr = global.StimulMotivarion;
    } else if (sourceType === 'positive') {
      arr = global.StimulPositiv;
    } else if (sourceType === 'negative') {
      arr = global.StimulNegative;
    }
    if (!arr || !Array.isArray(arr)) {
      return '';
    }
    for (var i = 0; i < arr.length; i++) {
      if (arr[i] && arr[i].Id === stimId) {
        return arr[i].name || '';
      }
    }
    return '';
  }

  function getSemanticImportanceValue() {
    if (typeof global.getDiffImportance === 'function') {
      var z = global.getDiffImportance();
      if (typeof z === 'number' && !isNaN(z)) {
        return z;
      }
    }
    return null;
  }

  /**
   * Не вызывать из startAction и нигде больше.
   * Учёт семантики выполняется один раз за активацию в orientation_reflex;
   * повторный вызов привёл бы к двойному увеличению счётчика обучений.
   * Заглушка оставлена, чтобы случайный вызов не давал двойного учёта.
   */
  function registerSemanticExperience(sourceType, stimId, z) {
    return;
  }

  function startAction(actionId, sourceType, stimId, options) {
    if (actionId === 100) {
      verbalReflexActive = true;
      return;
    }
    var name = getBasicActionName(actionId);
    var stimulusName = getStimulusName(sourceType, stimId);

    // Семантическая значимость учитывается один раз за активацию в ОР (orientation_reflex),
    // чтобы счётчик обучений увеличивался на 1 и усреднение значимости было верным.
    // Здесь только берём z для отображения в «Активность Beast», без повторной регистрации.
    var z = null;
    if (sourceType !== 'synonym') {
      z = getSemanticImportanceValue();
    }
    var typeLabel = '';
    if (sourceType === 'automatism') {
      typeLabel = 'автоматизм';
    } else if (sourceType === 'synonym') {
      typeLabel = 'условный рефлекс';
    } else if (sourceType === 'motivation' || sourceType === 'positive' || sourceType === 'negative') {
      typeLabel = 'генетический рефлекс';
    }
    var line1 = name ? (name + (typeLabel ? ' (' + typeLabel + ')' : '')) : typeLabel;
    var line2 = '';
    if (stimulusName) {
      line2 = 'Семантическая значимость ' + stimulusName;
      if (z !== null) {
        var zStr = String(Math.round(z * 100) / 100);
        line2 += ' - ' + zStr;
      }
    }
    var line = line2 ? (line1 + '\n' + line2) : line1;
    setBeastActivityText(line || name || '');
    // Любая выполненная реакция (генетическая, условная, автоматизм) переводит осмысление в целевой режим.
    if (typeof global.Info_setMode === 'function') {
      global.Info_setMode('target');
    }
    registerProlongatedAction(actionId);

    var fi = getCurrentFullFinalImageId();
    if (fi && typeof global.ActionsImage_registerReaction === 'function') {
      try {
        global.ActionsImage_registerReaction(fi, actionId);
      } catch (e) {}
    }
    if (typeof global.EpisodicMemory_setLastFrameActionFromActionId === 'function') {
      try {
        global.EpisodicMemory_setLastFrameActionFromActionId(actionId);
      } catch (e) {}
    }

    // Информируем подсистему условных (синонимических) рефлексов
    // ТОЛЬКО для "врождённых" путей (motivation / positive / negative).
    // Для sourceType === 'synonym' (запуск по условному рефлексу) — повторное обучение не выполняем.
    // Если стимул привлёк ОР (рефлекс был отложен ОР) — процесс образования условного рефлекса не запускаем.
    // Если идёт сборка кадра ЭП (период ожидания ответа) — условный рефлекс не создаём.
    // Если кадр только что закрыт ответным стимулом — рефлекс по нему не должен образовывать УР (флаг сбрасывается после пульса).
    // Условный рефлекс — это «сочетание» стимулов: при одном активном стимуле шаг сочетания не показываем и не создаём УР.
    var noConditionalReflex = options && options.noConditionalReflex === true;
    var inEpWaiting = typeof global.EpisodicMemory_isInWaitingPeriod === 'function' && global.EpisodicMemory_isInWaitingPeriod();
    var skipBecauseResponseStimulus = global.EpisodicMemory_skipConditionalReflexFormation === true;
    var activeIdsForUR = typeof global.StimulusGlobalId_getActiveGlobalIds === 'function'
      ? global.StimulusGlobalId_getActiveGlobalIds() : [];
    var singleStimulusNoPairing = !activeIdsForUR || activeIdsForUR.length < 2;
    if (sourceType !== 'synonym' && !noConditionalReflex && !inEpWaiting && !skipBecauseResponseStimulus && !singleStimulusNoPairing &&
        typeof global.synonyms_onGeneticActionFired === 'function') {
      try {
        global.synonyms_onGeneticActionFired(stimId, sourceType, actionId);
      } catch (e) {}
    }
  }

  /**
   * Выполнить образ действия (ОД): массив actionIds и/или последовательность других ОД (odSequence).
   * Используется при срабатывании автоматизма в stimuls_consciousness.
   * @param {number} actionsImageId — ID образа действия
   */
  function runActionImage(actionsImageId) {
    if (!actionsImageId || typeof global.ActionImages_getActionImage !== 'function') return;
    var od = global.ActionImages_getActionImage(actionsImageId);
    if (!od) return;
    var ids = od.actionIds || [];
    for (var i = 0; i < ids.length; i++) {
      startAction(ids[i], 'automatism', 0);
    }
    var seq = (od.odSequence || '').split(',');
    for (var j = 0; j < seq.length; j++) {
      var nextId = parseInt(seq[j].trim(), 10);
      if (nextId) runActionImage(nextId);
    }
  }

  /**
   * Внешний хук для запуска действия из условного (синонимического) рефлекса.
   * Оборачивает внутренний startAction, чтобы сохранялась единая логика
   * семантического учёта и пролонгированных действий.
   */
  function runGeneticActionFromSynonymReflex(actionId, sourceType, stimId) {
    var pulse = typeof global.Cur_puls_val === 'number' ? global.Cur_puls_val : 0;
    if (typeof global.OrientationReflex_isReflexBlockedByOR === 'function' &&
        global.OrientationReflex_isReflexBlockedByOR(stimId, actionId)) {
      var branchId = (typeof global.PerceptionTree_getActiveTerminalNodeId === 'function')
        ? global.PerceptionTree_getActiveTerminalNodeId() : 0;
      delayedReflexes.push({
        actionId: actionId,
        sourceType: sourceType || 'synonym',
        stimId: stimId,
        untilPulse: pulse + OR_BLOCK_PULSES,
        noConditionalReflex: true,
        branchId: branchId
      });
      return;
    }
    startAction(actionId, sourceType, stimId);
  }

  function updateProlongatedActions() {
    var hasChanges = false;
    if (!global.VitalsArr || !Array.isArray(global.VitalsArr)) {
      return;
    }
    for (var key in prolongedActions) {
      if (!Object.prototype.hasOwnProperty.call(prolongedActions, key)) continue;
      var cfg = prolongedActions[key];
      var vital = null;
      for (var i = 0; i < global.VitalsArr.length; i++) {
        if (global.VitalsArr[i].Id === cfg.vitalID) {
          vital = global.VitalsArr[i];
          break;
        }
      }
      if (!vital) {
        delete prolongedActions[key];
        continue;
      }
      if (vital.value <= 0) {
        delete prolongedActions[key];
        continue;
      }
      var newVal = vital.value - cfg.shift;
      if (newVal < 0) newVal = 0;
      if (newVal !== vital.value) {
        vital.value = newVal;
        hasChanges = true;
      }
      if (vital.value <= 0) {
        delete prolongedActions[key];
      }
    }
    if (hasChanges) {
      if (typeof global.update_vitals_view === 'function') {
        global.update_vitals_view();
      }
      if (typeof global.saveVitalsToStorage === 'function') {
        global.saveVitalsToStorage();
      }
    }
  }

  function collectNewActionEvents(activeIds, prevIds, mapping, sourceType) {
    var events = [];
    if (!activeIds || !activeIds.length || !mapping) {
      return events;
    }
    for (var i = 0; i < activeIds.length; i++) {
      var id = activeIds[i];
      if (prevIds.indexOf(id) !== -1) continue;
      var acts = mapping[id];
      if (!acts || !acts.length) continue;
      for (var j = 0; j < acts.length; j++) {
        var actId = acts[j];
        events.push({
          actionId: actId,
          sourceType: sourceType,
          stimId: id
        });
      }
    }
    return events;
  }

  function updateVerbalReflexFlag() {
    verbalReflexActive = false;
    function checkMapping(activeIds, mapping) {
      if (!activeIds || !activeIds.length || !mapping) return;
      for (var i = 0; i < activeIds.length; i++) {
        var id = activeIds[i];
        var acts = mapping[id];
        if (!acts || !acts.length) continue;
        for (var j = 0; j < acts.length; j++) {
          if (acts[j] === 100) {
            verbalReflexActive = true;
            return;
          }
        }
      }
    }
    checkMapping(global.ActiveMotivationStimuls, global.StimulMotivarion_GeneticReflexes);
    checkMapping(global.ActivePositivStimuls, global.StimulPositiv_GeneticReflexes);
    checkMapping(global.ActiveNegativeStimuls, global.StimulNegative_GeneticReflexes);
  }

  function handleVerbalReflex() {
    if (!verbalReflexActive) {
      verbalPulseCounter = 0;
      return;
    }
    verbalPulseCounter++;
    if (verbalPulseCounter % 5 !== 0) {
      return;
    }
    if (!global.BasicVerbal || !Array.isArray(global.BasicVerbal) || !global.BasicVerbal.length) {
      return;
    }
    var idx = Math.floor(Math.random() * global.BasicVerbal.length);
    if (idx < 0 || idx >= global.BasicVerbal.length) {
      return;
    }
    var phrase = global.BasicVerbal[idx];
    if (!phrase) return;
    var name = phrase.name || '';
    if (!name) return;
    setBeastActivityText('Произносит: ' + name);
  }

  function processGeneticReflexes() {
    var pulse = typeof global.Cur_puls_val === 'number' ? global.Cur_puls_val : 0;

    runDelayedReflexes();

    if (typeof global.synonyms_tryRunReflexes === 'function') {
      try {
        if (global.synonyms_tryRunReflexes()) {
          updateProlongatedActions();
          updateVerbalReflexFlag();
          handleVerbalReflex();
          return;
        }
      } catch (e) {}
    }

    var activeMotiv = Array.isArray(global.ActiveMotivationStimuls) ? global.ActiveMotivationStimuls.slice() : [];
    var activePositiv = Array.isArray(global.ActivePositivStimuls) ? global.ActivePositivStimuls.slice() : [];
    var activeNegative = Array.isArray(global.ActiveNegativeStimuls) ? global.ActiveNegativeStimuls.slice() : [];

    var events = [];
    events = events.concat(
      collectNewActionEvents(activeMotiv, prevMotivation, global.StimulMotivarion_GeneticReflexes, 'motivation'),
      collectNewActionEvents(activePositiv, prevPositiv, global.StimulPositiv_GeneticReflexes, 'positive'),
      collectNewActionEvents(activeNegative, prevNegative, global.StimulNegative_GeneticReflexes, 'negative')
    );

    if (events.length) {
      var ev = events[0];
      var currentBranchId = (typeof global.PerceptionTree_getActiveTerminalNodeId === 'function')
        ? global.PerceptionTree_getActiveTerminalNodeId() : 0;
      // Сбрасываем флаг «автоматизм сработал» только для других веток — при смене стимула/контекста генрефлекс снова разрешён.
      if (global.automatismRanForBranchIds && currentBranchId != null) {
        for (var bid in global.automatismRanForBranchIds) {
          if (Number(bid) !== Number(currentBranchId)) {
            delete global.automatismRanForBranchIds[bid];
          }
        }
      }
      var fullFinalImageId = getCurrentFullFinalImageId();
      var blocked = typeof global.OrientationReflex_isReflexBlockedByOR === 'function' &&
        global.OrientationReflex_isReflexBlockedByOR(fullFinalImageId, ev.actionId);
      var automatismRanForThisBranch = global.automatismRanForBranchIds && global.automatismRanForBranchIds[currentBranchId];
      if (blocked) {
        // Рефлекс уже поставлен в очередь из ОР (addBlockedReflexToQueue); здесь не дублируем.
      } else if (automatismRanForThisBranch) {
        // По этой ветке уже выполнен автоматизм в stimuls_consciousness — генрефлекс не запускаем (кардинально исключаем повтор).
      } else {
        startAction(ev.actionId, ev.sourceType, ev.stimId);
      }
    }

    prevMotivation = activeMotiv;
    prevPositiv = activePositiv;
    prevNegative = activeNegative;

    updateProlongatedActions();
    updateVerbalReflexFlag();
    handleVerbalReflex();
  }

  global.setBeastActivityText = setBeastActivityText;

  global.processGeneticReflexes = processGeneticReflexes;
  global.runGeneticActionFromSynonymReflex = runGeneticActionFromSynonymReflex;
  global.runActionImage = runActionImage;
  global.addBlockedReflexToQueue = addBlockedReflexToQueue;
  global.removeDelayedReflexesForBranch = removeDelayedReflexesForBranch;

  function copyTextToClipboard(text) {
    var t = (text || '').trim();
    if (!t) return;
    if (global.navigator && global.navigator.clipboard && typeof global.navigator.clipboard.writeText === 'function') {
      global.navigator.clipboard.writeText(t).catch(function () {
        fallbackCopy(t);
      });
      return;
    }
    fallbackCopy(t);
  }

  function fallbackCopy(text) {
    var doc = global.document;
    if (!doc || !doc.body) return;
    var ta = doc.createElement('textarea');
    ta.value = text;
    ta.style.position = 'fixed';
    ta.style.left = '-9999px';
    ta.style.top = '0';
    doc.body.appendChild(ta);
    ta.focus();
    ta.select();
    try {
      doc.execCommand('copy');
    } catch (e) {}
    doc.body.removeChild(ta);
  }

  function showCopySuccessAlert() {
    if (typeof global.show_alert === 'function') {
      global.show_alert('Скопировано в буфер обмена');
      if (typeof global.close_alert === 'function') {
        global.setTimeout(function () {
          try {
            global.close_alert();
          } catch (e) {}
        }, 1500);
      }
    }
  }

  function bindCopyButtons() {
    var doc = global.document;
    if (!doc) return;
    var btnBranch = doc.getElementById('btnCopyActiveBranch');
    if (btnBranch && typeof btnBranch.addEventListener === 'function') {
      btnBranch.addEventListener('click', function () {
        var el = doc.getElementById('perceptionTreeActiveBranchText');
        if (el) {
          copyTextToClipboard(el.textContent);
          showCopySuccessAlert();
        }
      });
    }
    var btnActivity = doc.getElementById('btnCopyActivityBlock');
    if (btnActivity && typeof btnActivity.addEventListener === 'function') {
      btnActivity.addEventListener('click', function () {
        var block = doc.getElementById('beastActivityBlock');
        if (block) {
          copyTextToClipboard(block.textContent);
          showCopySuccessAlert();
        }
      });
    }
  }

  if (global.document.readyState === 'loading') {
    global.document.addEventListener('DOMContentLoaded', bindCopyButtons);
  } else {
    bindCopyButtons();
  }
})(window);

