#!/usr/bin/env node
'use strict';
const fs=require('fs'),path=require('path'),A=require('assert/strict'),{Worker}=require('worker_threads'),os=require('os');
const R=path.resolve(__dirname,'..'),E=path.join(R,'доказательства/редакция-4.8.0'),K=require(path.join(R,'ресурсы/сценарии/доказательный-контур.js')),Executor=require(path.join(R,'ресурсы/сценарии/исполнитель-контура.js'));
const read=n=>JSON.parse(fs.readFileSync(path.join(R,n)));K.configureSchemas(Object.fromEntries(Object.entries(read('данные/профиль-схем-4.8.0.json').файлы).map(([k,n])=>[k,read(n.slice(1))])));
const amount=Number(process.argv[2]||4),trace=[],phases=[],input=[],files=[],names=[],schema=read('данные/схема-оценки-когнитивной-свободы-4.8.0.json'),workerPath=path.join(R,'ресурсы/сценарии/поток-доказательного-контура.js');
function sample(stage){phases.push({этап:stage,...process.memoryUsage(),пиковый_RSS_КиБ:process.resourceUsage().maxRSS});}
// Test-only wrapper samples a worker heap. It imports the unmodified shipping dispatcher.
const wrapper=`const {parentPort,workerData}=require('worker_threads');let id=0;parentPort.on('message',m=>{id=m.id;});setInterval(()=>parentPort.postMessage({id,type:'measurement',memory:process.memoryUsage()}),20);require(workerData);`;
const factory=()=>{const w=new Worker(wrapper,{eval:true,workerData:workerPath});w.on('message',m=>{if(m.type==='measurement')trace.push(m.memory);});return w;};
(async()=>{
 const template=read(read('данные/примеры-продольной-серии/индекс.json').файлы[0].replace(/^\//,'')),total=amount*1024*1024;sample('до входа');
 for(let i=0;i<25;i++){
  const a=structuredClone(template);a.редакция_схемы=K.RELEASE;a.идентификатор='ИС-ПОТОК-'+i;a.серия.идентификатор='ИС-ПОТОК-СЕРИЯ-'+i;
  const original=a.серия.наблюдения[0];a.серия.наблюдения=Array.from({length:100},(_,j)=>({...original,идентификатор:'ИС-ПОТОК-'+i+'-'+j,сеанс:'ИС-ПОТОК-'+i+'-'+j,ответ:'Искусственный ресурсный ответ',класс_доказательства:'ИС'}));
  a.ша256='0'.repeat(64);const target=Math.floor(total/25)+(i<total%25?1:0),remaining=target-Buffer.byteLength(JSON.stringify(a));A(remaining>=0);
  a.серия.наблюдения.forEach((o,j)=>o.ответ+='R'.repeat(Math.floor(remaining/100)+(j<remaining%100?1:0)));
  const bytes=Buffer.from(JSON.stringify(await K.seal(a)));A.equal(bytes.length,target);files.push(new Blob([bytes]));names.push('ИС-поток-'+i+'.json');input.push({имя:names.at(-1),байты:bytes.length,sha256:await K.hash(bytes)});
 }
 // A real previous panel remains held throughout; it is never replaced on failure.
 const previous=await K.ingest(fs.readFileSync(path.join(R,read('данные/примеры-продольной-серии/индекс.json').файлы[0])), 'ИС-прежняя.json',schema),panel=K.merge([previous],schema),before=await K.hashCanonical(panel);
 sample('вход и прежняя панель удерживаются');let accepted=false,error='',result;
 try{result=await new Executor({factory}).run('panel-import',{files,names,sources:panel.источники,decisions:[],revocations:[],migrations:[],restore:false},{onStage:sample});accepted=true;}catch(e){error=e.message;}
 sample('операция завершена');A.equal(await K.hashCanonical(panel),before);A.equal(files.reduce((n,f)=>n+f.size,0),total);
 const report={редакция:K.RELEASE,дата:new Date().toISOString(),команда:'node инструменты/измерить-поток-4-8.cjs '+amount,код_SHA256:await K.hash(fs.readFileSync(path.join(R,'ресурсы/сценарии/доказательный-контур.js'))),исполнитель_SHA256:await K.hash(fs.readFileSync(path.join(R,'ресурсы/сценарии/исполнитель-контура.js'))),диспетчер_SHA256:await K.hash(fs.readFileSync(workerPath)),измеритель_SHA256:await K.hash(fs.readFileSync(__filename)),среда:{ОС:os.type(),ядро:os.release(),CPU:os.cpus()[0]?.model,архитектура:process.arch,процесс:process.pid,Node:process.version,доступная_память:os.totalmem()},вход:{файлов:25,байты:total,наблюдений:2500,сохранённые_отпечатки:input,дополнительно_удерживается:'Прежняя панель из одного исходника, 25 Blob входа и снимок рабочего потока; намеренной сборки мусора нет.'},принят:accepted,ошибка:error,прежняя_панель_сохранена:true,пиковый_RSS_МиБ:process.resourceUsage().maxRSS/1024,основной_поток:phases,выборка_потока:trace,предел:'Node.js на одной машине. RSS общий для процесса и потоков; heap/external выборки относятся к измеряемому изоляту. 20 мс не гарантируют захвата мгновенного пика heap. Профиль включает прежний исходник: 26 суммарных исходников должны отклониться. Это не браузер, не сертификат устройства и не физический предел RSS.'};
 fs.writeFileSync(path.join(E,'ресурсы-поток-'+amount+'МиБ.json'),JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify({принят:accepted,ошибка:error,RSS:report.пиковый_RSS_МиБ}));
})().catch(e=>{console.error(e);process.exitCode=1;});
