/* война.com · 4.8.0-wip. Управление локальным исполнением, не второе доказательное ядро. */
(function (root) {
  'use strict';
  const K = root.ВойнаМетодика || (typeof require === 'function' ? require('./доказательный-контур.js') : null);
  class Executor {
    constructor({factory=null,timeout=120000}={}){
      this.factory=factory;this.timeout=timeout;this.serial=0;this.active=null;
    }
    cancel(){if(this.active)this.active.fail(new DOMException('Локальная обработка отменена. Внешние вызовы этим не прекращаются.','AbortError'));}
    run(command,data,{signal,isCurrent=()=>true,onStage=()=>{},envelope=false}={}){
      if(this.active)return Promise.reject(Error('Другая операция ещё выполняется.'));
      if(signal?.aborted)return Promise.reject(new DOMException('Обработка отменена.','AbortError'));
      let bounds;try{bounds=K.resourceCheck(data,{envelope});if(!isCurrent())throw Error('Снимок уже неактуален.');}catch(e){return Promise.reject(e);}
      const id=++this.serial;
      return new Promise((resolve,reject)=>{
        let worker,timer,done=false;
        const finish=(error,value)=>{
          if(done)return;done=true;clearTimeout(timer);signal?.removeEventListener('abort',abort);
          this.active=null;
          // termination may finish asynchronously in Node. No later message can commit a result.
          try{const ended=worker?.terminate();ended?.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||m?.id!==id)return;
          try{
            if(m.type==='stage'){onStage(m.stage);return;}
            if(m.type==='error'){const e=Error(String(m.message||'Рабочий поток завершился с ошибкой.'));e.name=m.name||'Error';finish(e);return;}
            if(m.type==='result'){
              if(signal?.aborted)return abort();
              if(!isCurrent())return finish(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('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);
          // Structured clone takes its snapshot before this call returns. No buffer in the
          // previous working object is transferred or detached. Trust WeakMaps stay in their realm.
          worker.postMessage({id,command,data,bounds,protocol:'война/поток/1'});
        }catch(e){finish(e);}
      });
    }
  }
  root.ВойнаИсполнитель=Executor;
  if(typeof module!=='undefined'&&module.exports)module.exports=Executor;
})(globalThis);
