// ============================================================================
// Данные экрана «Цех» — план цеха + двух-фазный учёт перемещений ПФ между
// участками. Прототип, синхронизация с реальными PROCESSES / NOMENCLATURE
// будет позже (Этап B).
//
// Источник истины процессов:
//   ~/.claude/projects/.../memory/manufacturing_path.md — сводная карта
//   .tmp/manufacturing_flow.md — версия для git'а (после переезда в docs/)
//   .tmp/manufacturing_path_questions.md — открытые вопросы
// ============================================================================

// Все участки и подэтапы цеха, в которых может «жить» позиция.
const CECH_STAGES = {
  tpa:     { name: 'ТПА',            short: 'ТПА',          tone: 'mold'   },
  lak:     { name: 'Лакировка',      short: 'лак',          tone: 'coat'   },
  metal:   { name: 'Металлизация',   short: 'металл.',      tone: 'metal'  },
  al_cast: { name: 'Литьё AL',       short: 'AL · литьё',   tone: 'cast'   },
  al_mech: { name: 'Мех. обработка', short: 'AL · мех.',    tone: 'cast'   },
  al_gult: { name: 'Голтовка',       short: 'AL · голт.',   tone: 'cast'   },
  buffer:  { name: 'Буфер производства', short: 'буфер',    tone: 'buffer' },
  asm:     { name: 'Сборка',         short: 'сборка',       tone: 'asm'    },
  // Сторонняя обработка (round-trip): деталь уезжает к подрядчику с одного
  // из обработочных этапов (лак/металл/голтовка) и ВОЗВРАЩАЕТСЯ обратно в
  // цех (буфер/сборка/склад). Не финальная зона, не обработочный этап
  // завода — промежуточный mid-route stage. Граф разрешённых переходов
  // (туда и обратно) — в cech_route_editor.jsx::RE_DEFAULT_GRAPH.
  outsource: { name: 'Сторонняя обработка', short: 'аутсорс', tone: 'outsource' },
  sklad2:  { name: 'Склад №2',       short: 'склад №2',     tone: 'w2'     },
  sklad3:  { name: 'Склад №3',       short: 'склад №3',     tone: 'w2'     },
};

// Маппинг слов маршрута из спеки (поле BomRow.route, например
// «ТПА → Лакировка → Металлизация → Склад №2») в ключи CECH_STAGES.
// Ключи нормализуются: lower-case + схлопывание пробелов, чтобы
// «Литьё AL» / «литьё al» / «Литье AL» давали один и тот же результат.
// «Закупка» намеренно НЕ маппится — покупное в цех не идёт (см. routeToStages).
const ROUTE_WORD_TO_STAGE = {
  'тпа':            'tpa',
  'лакировка':      'lak',
  'лак':            'lak',
  'металлизация':   'metal',
  'металл':         'metal',
  'метал':          'metal',
  // Канон этапа металлизации — «Металлизация» (выше). Синонимы «ВАК/вакуум…»
  // убраны 2026-06-25 как мёртвые: ни один маршрут спек их не использовал
  // (проверено на живой БД — 0 узлов), а сам термин путал отчёты. Если на
  // заводе вернётся написание «ВАК» в маршруте — добавить алиас обратно сюда.
  'литье al':       'al_cast',
  'литьё al':       'al_cast',
  'al литье':       'al_cast',
  'al литьё':       'al_cast',
  'литье':          'al_cast',
  'литьё':          'al_cast',
  'мех обр':        'al_mech',
  'мех обработка':  'al_mech',
  'мех':            'al_mech',
  'механообработка':'al_mech',
  'голтовка':       'al_gult',
  'галтовка':       'al_gult',
  'буфер':          'buffer',
  'буфер производства': 'buffer',
  'сборка':         'asm',
  'аутсорс':           'outsource',
  'outsource':         'outsource',
  'сторонняя обработка':'outsource',
  'сторонняя':         'outsource',
  'кооперация':        'outsource',
  'субподряд':         'outsource',
  'склад 2':        'sklad2',
  'склад №2':       'sklad2',
  'склад 3':        'sklad3',
  'склад №3':       'sklad3',
};

// Нормализация одного слова маршрута: lower-case, убрать точки,
// схлопнуть пробелы. «Мех.обр» → «мех обр»; «Склад №2» оставляем №
// (он есть в ключах), но «Склад N2»/«склад  2» тоже сводятся.
function _normRouteWord(word) {
  if (!word) return '';
  return String(word)
    .toLowerCase()
    .replace(/\./g, ' ')      // «мех.обр» → «мех обр»
    .replace(/\s+/g, ' ')     // схлопнуть пробелы
    .trim();
}

// routeToStages(routeStr): парсит строковый маршрут спеки в УПОРЯДОЧЕННЫЙ
// массив ключей этапов цеха. Разделители — стрелки (→, ->, =>) либо «/».
//   'ТПА → Склад №2'                               → ['tpa','sklad2']
//   'Литьё AL → Мех.обр → Голтовка → Лак → Метал → Склад №2'
//                                                  → ['al_cast','al_mech','al_gult','lak','metal','sklad2']
//   'Закупка'                                      → []   (покупное, не цех)
// Неизвестные слова пропускаются (не ломают парс). Дубли подряд схлопываются.
// Возвращает [] для пустого/нераспознанного маршрута — вызывающая сторона
// трактует это как «маршрут не задан» и сохраняет старое поведение.
function routeToStages(routeStr) {
  if (!routeStr || typeof routeStr !== 'string') return [];
  const parts = routeStr.split(/→|->|=>|\/|⟶|➝|➔/);
  const out = [];
  for (const raw of parts) {
    const key = _normRouteWord(raw);
    if (!key) continue;
    if (key === 'закупка' || key === 'покупное' || key === 'покупка') continue;
    const stage = ROUTE_WORD_TO_STAGE[key];
    if (!stage) continue;                 // незнакомое слово — пропускаем
    if (out.length > 0 && out[out.length - 1] === stage) continue; // дубль подряд
    out.push(stage);
  }
  return out;
}

// resolveItemRoute(opts): достаёт строковый маршрут для запускаемой партии
// из доступных источников, в порядке приоритета. Возвращает строку route
// (или null если нигде не нашлось). Парсинг в массив этапов — отдельно,
// через routeToStages. Источники (первый непустой выигрывает):
//   1) opts.routeStr            — явная строка (если вызывающий уже её знает)
//   2) opts.bomRow.route        — поле строки BOM (основной кейс: FG-PF, где
//                                 каждая internal-строка несёт свой маршрут)
//   3) opts.item.route          — поле самой NOMENCLATURE-позиции ПФ
//   4) spec[sku].route          — маршрут на уровне объекта-спеки ПФ
//   5) первая internal-строка spec[sku].bom с непустым route
// Чисто read-only по отношению к данным.
function resolveItemRoute(opts) {
  opts = opts || {};
  if (typeof opts.routeStr === 'string' && opts.routeStr.trim()) return opts.routeStr;
  if (opts.bomRow && typeof opts.bomRow.route === 'string' && opts.bomRow.route.trim()) {
    return opts.bomRow.route;
  }
  if (opts.item && typeof opts.item.route === 'string' && opts.item.route.trim()) {
    return opts.item.route;
  }
  const sku = opts.sku || (opts.item && opts.item.sku) || null;
  if (sku && typeof window !== 'undefined') {
    const spec = (window.SPECIFICATIONS || {})[sku];
    if (spec && typeof spec === 'object' && !Array.isArray(spec)) {
      if (typeof spec.route === 'string' && spec.route.trim()) return spec.route;
    }
    // первая internal-строка спеки с маршрутом
    const tree = (typeof window.getSpecBom === 'function') ? window.getSpecBom(sku)
               : (Array.isArray(spec) ? spec : (spec && spec.bom) || []);
    const flat = [];
    const walk = (rows) => {
      for (const r of (rows || [])) {
        if (!r) continue;
        flat.push(r);
        if (Array.isArray(r.children)) walk(r.children);
      }
    };
    walk(tree);
    const withRoute = flat.find(r => typeof r.route === 'string' && r.route.trim());
    if (withRoute) return withRoute.route;
  }
  return null;
}

// Стартовый реестр позиций в Цехе — пустой. Партии материализуются здесь
// только когда пользователь явно запускает их через «Запуск в производство»
// (см. v1/screens/launch_flow.jsx → window.CECH_STATE.items.push). До этого
// Цех пустой, никаких demo-табличек.
const CECH_INITIAL_ITEMS = [];

// Размер партии по умолчанию для перемещения между участками.
const CECH_BATCH = 500;

// Стартовое состояние Цеха — пустое. items[] наполняется только запусками
// партий через launch_flow.jsx (window.CECH_STATE.items.push + диспатч
// 'cech:state-changed') и/или backend-snapshot (manufacturingBatches +
// cechTransfers, см. data_server_sync.jsx::_applyServerSnapshot).
function makeCechInitialState() {
  return { items: [], shipmentSeq: 200 };
}

// routeProgress(item, currentStage): где находится партия на своём
// маршруте. Возвращает { idx, total, next, done } или null если у item
// нет привязанного маршрута (item.route пустой/отсутствует).
//   idx   — индекс currentStage в маршруте (0-based), -1 если не на маршруте
//   total — длина маршрута
//   next  — ключ следующего этапа по маршруту (null если currentStage финальный
//           или не на маршруте)
//   done  — true если currentStage — последний этап маршрута
// Чисто read-only, ничего не мутирует. Цех использует для подсветки
// «следующего ожидаемого этапа» и индикатора прогресса.
function routeProgress(item, currentStage) {
  const route = item && Array.isArray(item.route) ? item.route : null;
  if (!route || route.length === 0) return null;
  const idx = route.indexOf(currentStage);
  const total = route.length;
  if (idx < 0) return { idx: -1, total, next: null, done: false };
  const next = idx < total - 1 ? route[idx + 1] : null;
  return { idx, total, next, done: idx === total - 1 };
}

// nextRouteStage(item, fromStage): следующий по маршруту этап ИЗ fromStage,
// или null. Тонкая обёртка над routeProgress — удобно для подсветки drop-зоны.
function nextRouteStage(item, fromStage) {
  const p = routeProgress(item, fromStage);
  return p ? p.next : null;
}

Object.assign(window, {
  CECH_STAGES,
  CECH_INITIAL_ITEMS,
  CECH_BATCH,
  makeCechInitialState,
  ROUTE_WORD_TO_STAGE,
  routeToStages,
  routeProgress,
  nextRouteStage,
  resolveItemRoute,
});
