/* война.com · 4.9.0-wip. Транспорт одного ядра; не удостоверение владельца. */
(function(root){
  'use strict';
  const K=root.ВойнаМетодика||(typeof require==='function'?require('./доказательный-контур.js'):null);
  let pageLease=null; // Один активный исполнитель этого контекста; не межвкладочный замок.
  class Executor{
    constructor({factory=null,timeout=120000}={}){
      if(!Number.isFinite(timeout)||timeout<1||timeout>120000)throw Error('Предел времени должен быть от 1 до 120000 мс.');
      this.factory=factory;this.timeout=timeout;this.serial=0;this.active=null;
    }
    cancel(){this.active?.fail(new DOMException('Локальная обработка отменена. Внешние вызовы этим не прекращаются.','AbortError'));}
    run(command,data,{signal,isCurrent=()=>true,onStage=()=>{},envelope=false}={}){
      if(this.active||pageLease)return Promise.reject(Error('Другая операция ещё выполняется в этой странице.'));
      if(!K.WORKER_COMMANDS.includes(command))return Promise.reject(Error('Неизвестная операция рабочего потока.'));
      if(signal?.aborted)return Promise.reject(new DOMException('Обработка отменена.','AbortError'));
      try{K.resourceCheck(data,{envelope});if(!isCurrent())throw Error('Снимок уже неактуален.');}catch(e){return Promise.reject(e);}
      const id=++this.serial,lease={};pageLease=lease;
      return new Promise((resolve,reject)=>{
        let worker,timer,done=false,next=1;
        const finish=(error,value)=>{
          if(done)return;done=true;clearTimeout(timer);signal?.removeEventListener('abort',abort);this.active=null;
          if(pageLease===lease)pageLease=null;
          try{const end=worker?.terminate();end?.catch?.(()=>{});}catch{}
          if(error)reject(error);else resolve(value);
        };
        const abort=()=>finish(new DOMException('Локальная обработка отменена; прежняя работа сохранена.','AbortError'));
        this.active={id,fail:e=>finish(e)};
        const message=m=>{
          if(done)return;
          try{
            if(m&&Number.isSafeInteger(m.id)&&m.id>0&&m.id<id)return; // старое сообщение не принимается
            if(!m||typeof m!=='object'||Array.isArray(m)||m.id!==id||m.protocol!==K.WORKER_PROTOCOL||m.release!==K.RELEASE||m.command!==command||m.seq!==next)throw Error('Нарушен протокол, редакция или порядок сообщений рабочего потока.');
            const fields=['id','protocol','release','command','seq','type',...(m.type==='stage'?['stage']:m.type==='error'?['name','message']:m.type==='result'?['value']:[])].sort();
            if(Object.keys(m).sort().join('\0')!==fields.join('\0'))throw Error('Недопустимые поля сообщения рабочего потока.');
            next++;
            if(m.type==='stage'){if(typeof m.stage!=='string'||!m.stage.trim()||m.stage.length>160)throw Error('Неверное имя этапа.');onStage(m.stage);return;}
            if(m.type==='error'){if(typeof m.message!=='string'||m.message.length>4000||typeof m.name!=='string'||m.name.length>64)throw Error('Неверная форма отказа рабочего потока.');const e=Error(m.message);e.name=m.name;finish(e);return;}
            if(m.type!=='result')throw Error('Неизвестный вид сообщения рабочего потока.');
            K.workerResultCheck(command,m.value);
            if(signal?.aborted)return abort();
            if(!isCurrent())throw Error('Данные изменились; устаревший результат не принят.');
            finish(null,m.value);
          }catch(e){finish(e);}
        };
        try{
          if(this.factory)worker=this.factory();
          else if(typeof Worker==='function')worker=new Worker(new URL('ресурсы/сценарии/поток-доказательного-контура.js?редакция='+encodeURIComponent(K.RELEASE),document.baseURI));
          else throw Error('Рабочий поток недоступен. Синхронная подмена не выполняется; прежняя работа сохранена.');
          if(typeof worker.on==='function'){
            worker.on('message',message);worker.on('messageerror',()=>finish(Error('Сообщение рабочего потока не удалось прочитать.')));worker.on('error',e=>finish(e));worker.on('exit',code=>{if(!done)finish(Error('Рабочий поток завершился до результата: '+code));});
          }else{worker.onmessage=e=>message(e.data);worker.onerror=e=>finish(Error(e.message||'Сбой рабочего потока.'));worker.onmessageerror=()=>finish(Error('Результат рабочего потока не удалось прочитать.'));}
          signal?.addEventListener('abort',abort,{once:true});if(signal?.aborted)return abort();
          timer=setTimeout(()=>finish(Error('Истекло время локальной обработки. Результат не принят.')),this.timeout);
          // Ни один исходный ArrayBuffer не передаётся с отсоединением.
          worker.postMessage({id,command,data,protocol:K.WORKER_PROTOCOL,release:K.RELEASE});
        }catch(e){finish(e);}
      });
    }
  }
  root.ВойнаИсполнитель=Executor;if(typeof module!=='undefined'&&module.exports)module.exports=Executor;
})(globalThis);
