/**
 * Подготовка и активация базовых контекстов по виталам.
 *
 * Использует упрощённый доступ:
 * - BadVitalsValue[vitalId] — степень выхода витала из нормы (Kbad).
 * - VitalsContextsActivingArr[vitalId] — массив id базовых контекстов для активации.
 * - BasicContextsActived — массив ID активных базовых контекстов (0–3 числа); до первого вызова не определён.
 * Глобальные BadVitalsValue и BasicContextsActived обновляются только по завершении расчётов,
 * без промежуточных значений. VitalsContextsActivingArr только читается, не изменяется.
 *
 * После активации контекстов: при отсутствии активных — активируется Покой (id=14);
 * вычисляются IntergalConditionImportance, DiffZnacher, вызывается GetBadNormWell().
 * getDiffImportance() — произвольно вызываемая функция, возвращает normalizeValue(текущий DiffZnacher − сохранённый).
 * Перед нормализацией сырой diff (до normalizeValue) кладётся в LastDiffImportanceRaw — для «лупы» семантики в registerSemanticSample.
 */

(function (global) {
  'use strict';

  /** Длительность удержания состояния Хорошо (3) в пульсах после выхода из Плохо или по вызову SetWellForHolding(). */
  var state_retention_period = 15;

  /** Осталось пульсов удержания Хорошо (3). */
  var hold_well_remaining = 0;

  /** Осталось пульсов удержания Плохо (1). */
  var hold_bad_remaining = 0;

  /** Осталось пульсов удержания Норма (2). */
  var hold_norm_remaining = 0;

  /** Осталось пульсов удержания заданного набора базовых контекстов. */
  var hold_contexts_remaining = 0;

  /** Насильственно удерживаемые базовые контексты (их id). */
  var forced_basic_contexts = null;

  /** Значение BadNormWell, установленное в предыдущем пульсе (для определения перехода из Плохо). */
  var lastSetBadNormWell = 0;

  /** Снимок value по Id витала с предыдущего пульса — для относительного порога ухудшения. */
  var prevVitalValuesSnapshot = null;

  /** Рост value витала за один пульс, при котором запускается новый цикл ФО: (curr−prev)/prev ≥ порога. */
  var VITAL_RELATIVE_WORSEN_THRESHOLD = 0.3;

  function snapshotVitalValues(VitalsArr) {
    var o = {};
    if (!VitalsArr || !VitalsArr.length) return o;
    for (var i = 0; i < VitalsArr.length; i++) {
      var v = VitalsArr[i];
      if (v && typeof v.Id === 'number') {
        o[v.Id] = typeof v.value === 'number' ? v.value : 0;
      }
    }
    return o;
  }

  /**
   * Рост value более чем на VITAL_RELATIVE_WORSEN_THRESHOLD (от предыдущего значения), либо переход в Плохо.
   * При prev≈0: порог «не ниже 30 баллов» (30% шкалы 0…100), чтобы с нуля срабатывало осмысление.
   */
  function detectSharpVitalValueWorsening(VitalsArr, prevSnap) {
    if (!prevSnap || !VitalsArr || !VitalsArr.length) return false;
    for (var i = 0; i < VitalsArr.length; i++) {
      var v = VitalsArr[i];
      var vid = v.Id;
      var prevVal = prevSnap[vid];
      if (typeof prevVal !== 'number') prevVal = 0;
      var currVal = typeof v.value === 'number' ? v.value : 0;
      if (currVal <= prevVal) continue;
      if (prevVal > 0.001) {
        if ((currVal - prevVal) / prevVal >= VITAL_RELATIVE_WORSEN_THRESHOLD) return true;
      } else if (currVal >= 30) {
        return true;
      }
    }
    return false;
  }

  /**
   * Переход в Плохо или резкий рост value витала (>30% к предыдущему) — новый главный цикл осмысления.
   * Пульс 3 не трогаем: там отдельный стартовый вызов ФО в puls.js.
   */
  function tryTriggerConsciousnessOnVitalWorsening(suddenBadTransition, sharpValueWorsening) {
    if (!suddenBadTransition && !sharpValueWorsening) return;
    if (typeof global.cpuls === 'number' && global.cpuls === 3) return;
    if (typeof global.stimuls_consciousness !== 'function') return;

    var branchId = typeof global.PerceptionTree_getActiveTerminalNodeId === 'function'
      ? global.PerceptionTree_getActiveTerminalNodeId()
      : 0;
    var emotionId = 0;
    if (typeof global.Emotions_getOrCreateEmotionId === 'function' && Array.isArray(global.BasicContextsActived)) {
      emotionId = global.Emotions_getOrCreateEmotionId(global.BasicContextsActived) || 0;
    }
    var igr = typeof global.IntergalConditionImportance === 'number' ? global.IntergalConditionImportance : 0;
    var significance = Math.max(12, igr / 50);

    global.stimuls_consciousness({
      actual_stimul_ID: 0,
      branchId: branchId,
      well: global.BadNormWell || 0,
      emotionId: emotionId,
      activityID: 0,
      significance: significance,
      vitalEmergency: true
    });
  }

  /** Глобально: оставшееся число пульсов удержания Хорошо (для отображения обратного отсчёта в UI). */
  global.WellHoldRemainingPulses = 0;
  /** Глобально: оставшееся число пульсов удержания Плохо (для отображения обратного отсчёта в UI). */
  global.BadHoldRemainingPulses = 0;
  /** Глобально: оставшееся число пульсов удержания Норма (для UI). */
  global.NormHoldRemainingPulses = 0;
  /** Глобально: оставшееся число пульсов удержания базовых контекстов (для UI). */
  global.ContextHoldRemainingPulses = 0;

  /** Базовая величина эффекта по состоянию (можно менять вручную для оптимизации). */
  global.WellBadEffectManual = 5;

  function normalizeValue(value, k) {
    if (k === undefined) k = 0.5;
    var max = 10;
    return max * (1 - Math.exp(-k * Math.abs(value))) * (value >= 0 ? 1 : -1);
  }

  /**
   * Вычисляет window.BadNormWell: Плохо (1), Норма (2), Хорошо (3).
   * 1. Сначала определяется Норма или Плохо (по сумме BadVitalsValue).
   * 2. Если DiffZnacher > 2 — запускается период удержания Хорошо.
   * 3. В конце периода: если снова DiffZnacher > 2 — новый период Хорошо; иначе — Норма или Плохо.
   */
  function GetBadNormWell() {
    // Приоритетно удерживаем состояние Плохо, если запущено SetBadForHolding()
    if (hold_bad_remaining > 0) {
      hold_bad_remaining--;
      global.BadNormWell = 1;
      global.BadHoldRemainingPulses = hold_bad_remaining;
      global.WellHoldRemainingPulses = hold_well_remaining;
      global.NormHoldRemainingPulses = hold_norm_remaining;
      return;
    }
    if (hold_norm_remaining > 0) {
      hold_norm_remaining--;
      global.BadNormWell = 2;
      global.NormHoldRemainingPulses = hold_norm_remaining;
      global.BadHoldRemainingPulses = hold_bad_remaining;
      global.WellHoldRemainingPulses = hold_well_remaining;
      return;
    }
    var VitalsArr = global.VitalsArr || [];
    var sum = 0;
    for (var i = 0; i < VitalsArr.length; i++) {
      sum += global.BadVitalsValue[VitalsArr[i].Id] || 0;
    }
    var base = sum >= 2 ? 1 : 2; // 1 — Плохо, 2 — Норма

    var d = typeof global.DiffZnacher === 'number' ? global.DiffZnacher : 0;

    if (hold_well_remaining > 0) {
      hold_well_remaining--;
      global.BadNormWell = 3;
      if (hold_well_remaining === 0 && d > 2) {
        hold_well_remaining = state_retention_period;
      } else if (hold_well_remaining === 0) {
        global.BadNormWell = base;
      }
    } else {
      if (d > 2) {
        hold_well_remaining = state_retention_period;
        global.BadNormWell = 3;
      } else {
        global.BadNormWell = base;
      }
    }
    global.WellHoldRemainingPulses = hold_well_remaining;
    global.BadHoldRemainingPulses = hold_bad_remaining;
    global.NormHoldRemainingPulses = hold_norm_remaining;
  }

  /**
   * Запускает период удержания состояния Хорошо (3) на state_retention_period пульсов.
   */
  function SetWellForHolding() {
    hold_bad_remaining = 0;
    global.BadHoldRemainingPulses = 0;
    hold_norm_remaining = 0;
    global.NormHoldRemainingPulses = 0;
    hold_well_remaining = state_retention_period;
    global.WellHoldRemainingPulses = hold_well_remaining;
  }

  /**
   * Запускает период удержания состояния Плохо (1) на state_retention_period пульсов.
   */
  function SetBadForHolding() {
    hold_norm_remaining = 0;
    global.NormHoldRemainingPulses = 0;
    hold_well_remaining = 0;
    global.WellHoldRemainingPulses = 0;
    hold_bad_remaining = state_retention_period;
    global.BadHoldRemainingPulses = hold_bad_remaining;
    // Запуск удержания Плохо трактуем как ориентировочный сигнал «стало плохо»:
    // переводим осмысление в целевой режим и зажигаем лампочку осмысления.
    if (typeof global.Info_setMode === 'function') {
      global.Info_setMode('target');
    }
    if (typeof global.Info_thinkingFlash === 'function') {
      global.Info_thinkingFlash();
    }
  }

  /**
   * Запускает период удержания состояния Норма (2) на state_retention_period пульсов.
   */
  function SetNormForHolding() {
    hold_bad_remaining = 0;
    global.BadHoldRemainingPulses = 0;
    hold_well_remaining = 0;
    global.WellHoldRemainingPulses = 0;
    hold_norm_remaining = state_retention_period;
    global.NormHoldRemainingPulses = hold_norm_remaining;
  }

  /**
   * Запускает период удержания заданного набора базовых контекстов
   * на state_retention_period пульсов. В этот период выбор контекстов по виталам блокируется.
   * @param {number[]} contextIds - массив id из window.BasicContexts.
   */
  function ActivateBasicContextsForHolding(contextIds) {
    if (!Array.isArray(contextIds) || !contextIds.length) {
      return;
    }
    forced_basic_contexts = contextIds.slice(0);
    hold_contexts_remaining = state_retention_period;
    global.ContextHoldRemainingPulses = hold_contexts_remaining;
  }

  /** Глобально: предыдущее значение совокупного выхода из нормы (IntergalConditionImportance) для getDiffImportance. */
  global.PrevDiffZnacherForGetDiff = null;

  /**
   * Сырой diff для семантики: (getWellBadEffect() + prev − curr) / 100 до normalizeValue в последнем вызове getDiffImportance.
   * Используется в semantic_memory.registerSemanticSample («лупа» при почти нулевой meanImportance и норме виталов).
   */
  global.LastDiffImportanceRaw = null;

  /**
   * Смещение для diff в зависимости от удерживаемого базового состояния.
   * Модуль берётся из WellBadEffectManual (по умолчанию 5):
   *  - при Хорошо (BadNormWell=3) возвращает +5,
   *  - при Плохо (BadNormWell=1) возвращает -5,
   *  - в остальных случаях (Норма и пр.) — 0.
   */
  global.WellBadEffect = 0;

  function getWellBadEffect() {
    var base = (typeof global.WellBadEffectManual === 'number' && !isNaN(global.WellBadEffectManual))
      ? Math.abs(global.WellBadEffectManual)
      : 5;
    if (global.BadNormWell === 3) return base;
    if (global.BadNormWell === 1) return -base;
    return 0;
  }

  /**
   * Произвольно вызываемая функция: сравнивает текущий IntergalConditionImportance с сохранённым при предыдущем вызове.
   * @returns {number} normalizeValue(разница). Увеличение важности = хуже (отрицательный), уменьшение = лучше (положительный).
   */
  function getDiffImportance() {
    var curr = typeof global.IntergalConditionImportance === 'number' ? global.IntergalConditionImportance : 0;
    var prev = global.PrevDiffZnacherForGetDiff !== null && global.PrevDiffZnacherForGetDiff !== undefined
      ? global.PrevDiffZnacherForGetDiff
      : curr;
    global.PrevDiffZnacherForGetDiff = curr;
    var diff = (getWellBadEffect() + prev - curr) / 100;
    global.LastDiffImportanceRaw = diff;
    return normalizeValue(diff);
  }

  /**
   * Вычисляет степень выхода виталов из нормы (Kbad по Id витала).
   * Kbad = 0 при value <= 40; при value > 40: Kbad = 2^((value-40)*0.1).
   * @returns {Object} BadVitalsValue — объект vitalId -> Kbad. Пустой объект, если нет виталов.
   */
  function calculateBadVitalsValues() {
    var VitalsArr = global.VitalsArr;
    var BadVitalsValue = {};
    if (!VitalsArr || !VitalsArr.length) return BadVitalsValue;
    for (var i = 0; i < VitalsArr.length; i++) {
      var vitalId = VitalsArr[i].Id;
      var value = VitalsArr[i].value;
      var Kbad = value > 40 ? Math.pow(2, 0.1 * (value - 40)) : 0;
      BadVitalsValue[vitalId] = Kbad;
    }
    return BadVitalsValue;
  }

  /**
   * Выбирает массив ID активных базовых контекстов по рассчитанным BadVitalsValue.
   * Виталы учитываются в порядке убывания importance; не более 3 контекстов.
   * Учитывает удержание (forced_basic_contexts). При отсутствии контекстов — Покой (14).
   * Побочный эффект: при использовании удержания уменьшает hold_contexts_remaining.
   * @param {Object} badVitals — объект из calculateBadVitalsValues().
   * @returns {number[]} массив ID контекстов (BasicContextsActived).
   */
  function selectBasicContexts(badVitals) {
    var VitalsArr = global.VitalsArr;
    var VitalsContextsActivingArr = global.VitalsContextsActivingArr;
    var BasicContextsActived = [];

    if (!VitalsArr || !VitalsArr.length || !global.BasicContexts || !global.BasicContexts.length) {
      if (hold_contexts_remaining > 0 && Array.isArray(forced_basic_contexts) && forced_basic_contexts.length) {
        hold_contexts_remaining--;
        return forced_basic_contexts.slice(0);
      }
      return BasicContextsActived.length ? BasicContextsActived : [14];
    }

    var list = [];
    for (var i = 0; i < VitalsArr.length; i++) {
      list.push({
        vitalId: VitalsArr[i].Id,
        importance: VitalsArr[i].importance
      });
    }
    list.sort(function (a, b) { return b.importance - a.importance; });

    for (var s = 0; s < list.length; s++) {
      var vitalId = list[s].vitalId;
      var Kbad = badVitals[vitalId] || 0;
      if (Kbad > 0 && BasicContextsActived.length < 3 && VitalsContextsActivingArr) {
        var contextIds = VitalsContextsActivingArr[vitalId] || VitalsContextsActivingArr[String(vitalId)] || [];
        for (var c = 0; c < contextIds.length && BasicContextsActived.length < 3; c++) {
          var contextId = contextIds[c];
          if (BasicContextsActived.indexOf(contextId) === -1) BasicContextsActived.push(contextId);
        }
      }
    }

    if (hold_contexts_remaining > 0 && Array.isArray(forced_basic_contexts) && forced_basic_contexts.length) {
      hold_contexts_remaining--;
      BasicContextsActived = forced_basic_contexts.slice(0);
    }
    if (BasicContextsActived.length === 0) BasicContextsActived.push(14);

    return BasicContextsActived;
  }

  /**
   * Обновляет глобальные метрики важности и диффа: IntergalConditionImportance, DiffZnacher,
   * а также ContextHoldRemainingPulses для UI.
   * @param {Object} badVitals — объект из calculateBadVitalsValues().
   */
  function updateImportanceMetrics(badVitals) {
    var VitalsArr = global.VitalsArr || [];
    var IntergalConditionImportanceNew = 0;
    for (var v = 0; v < VitalsArr.length; v++) {
      var vid = VitalsArr[v].Id;
      var imp = VitalsArr[v].importance;
      IntergalConditionImportanceNew += (badVitals[vid] || 0) * imp;
    }
    var currentImportance = (typeof global.IntergalConditionImportance === 'number')
      ? global.IntergalConditionImportance
      : 0;

    global.IntergalConditionImportanceOld = currentImportance;
    global.IntergalConditionImportance = IntergalConditionImportanceNew;
    var rawDiff = (getWellBadEffect() + currentImportance - IntergalConditionImportanceNew) / 100;
    global.DiffZnacher = normalizeValue(rawDiff);
    // Детекторы badState/suddenChange обновляются в getActiveBasicContexts (см. ниже).
    global.ContextHoldRemainingPulses = hold_contexts_remaining;
  }

  /**
   * Активация базовых контекстов по текущим значениям виталов.
   * Сводится к вызову трёх шагов: расчёт BadVitalsValue, выбор контекстов, обновление метрик и GetBadNormWell.
   *
   * @param {number} [cpuls] — счётчик пульсов; при cpuls === 2 вызывается getDiffImportance() для инициализации PrevDiffZnacherForGetDiff.
   * @returns {Object} — объект BadVitalsValue (Kbad по ID витала).
   */
  function getActiveBasicContexts(cpuls) {
    var VitalsArr = global.VitalsArr;
    if (!VitalsArr || !VitalsArr.length || !global.BasicContexts || !global.BasicContexts.length) {
      return global.BadVitalsValue || {};
    }

    // Нужно для детекторов «badState» и «suddenChange»: сравниваем предыдущее и текущее базовое состояние.
    var prevBadNormWell = (typeof global.BadNormWell === 'number') ? global.BadNormWell : 0;

    var badVitals = calculateBadVitalsValues();
    var contextIds = selectBasicContexts(badVitals);
    // Режим сна: только базовый контекст «Сон», без смеси с Нормой/другими (в т.ч. при критических виталах).
    if (typeof global.SleepMode_isActive === 'function' && global.SleepMode_isActive()) {
      var sleepCtx = (typeof global.SleepMode_CONTEXT_ID === 'number') ? global.SleepMode_CONTEXT_ID : 15;
      contextIds = [sleepCtx];
    }

    global.BadVitalsValue = badVitals;
    global.BasicContextsActived = contextIds;

    updateImportanceMetrics(badVitals);
    GetBadNormWell();

    // Инфо-детекторы по базовым контекстам и виталам:
    // badState/suddenChange, критический витал, игровой режим, режим обучения / поисковый интерес, страх, агрессия, защита.
    if (typeof global.InfoDet_updateBadState === 'function') {
      global.InfoDet_updateBadState(prevBadNormWell, global.BadNormWell || 0);
    }
    if (typeof global.InfoDet_updateCriticalVital === 'function') {
      global.InfoDet_updateCriticalVital(global.VitalsArr || []);
    }
    if (typeof global.InfoDet_updatePlayMode === 'function') {
      global.InfoDet_updatePlayMode(global.BasicContextsActived || []);
    }
    if (typeof global.InfoDet_updateLearningAndSearch === 'function') {
      global.InfoDet_updateLearningAndSearch(global.BasicContextsActived || [], global.VitalsArr || []);
    }
    // Страх (id контекста 6) и агрессия (id контекста 7) – см. BasicContexts.js.
    var act = global.BasicContextsActived || [];
    var fearActive = act.indexOf(6) !== -1;
    var aggrActive = act.indexOf(7) !== -1;
    if (typeof global.InfoDet_updateFear === 'function') {
      global.InfoDet_updateFear(fearActive);
    }
    if (typeof global.InfoDet_updateAggression === 'function') {
      global.InfoDet_updateAggression(aggrActive);
    }
    if (typeof global.InfoDet_updateDefense === 'function') {
      global.InfoDet_updateDefense(fearActive || aggrActive);
    }

    var suddenBadTransition = (prevBadNormWell !== 1 && (global.BadNormWell || 0) === 1);
    var sharpValueWorsening = detectSharpVitalValueWorsening(VitalsArr, prevVitalValuesSnapshot);
    tryTriggerConsciousnessOnVitalWorsening(suddenBadTransition, sharpValueWorsening);
    prevVitalValuesSnapshot = snapshotVitalValues(VitalsArr);

    if (cpuls === 2) {
      getDiffImportance();
    }

    return badVitals;
  }

  global.getActiveBasicContexts = getActiveBasicContexts;
  global.GetBadNormWell = GetBadNormWell;
  global.getDiffImportance = getDiffImportance;
  global.normalizeValue = normalizeValue;
  global.SetWellForHolding = SetWellForHolding;
  global.SetBadForHolding = SetBadForHolding;
  global.SetNormForHolding = SetNormForHolding;
  global.ActivateBasicContextsForHolding = ActivateBasicContextsForHolding;
})(window);
