import http from 'node:http';
import { Pool } from 'undici';
import { safeLog } from './client.mjs';
import { randomUUID } from 'node:crypto';
const origin=new URL(process.env.UPSTREAM_ORIGIN||'http://127.0.0.1:8561');
if(!['http:','https:'].includes(origin.protocol)||origin.username||origin.password||origin.pathname!=='/'||origin.search||origin.hash)throw new Error('UPSTREAM_ORIGIN must contain only an http(s) origin');
const pool=new Pool(origin.origin,{connections:4,pipelining:1,connectTimeout:1500});let active=0,stopping=false;
const server=http.createServer(async(req,res)=>{
 const id=randomUUID(),started=performance.now();const respond=(status,data)=>{if(!res.destroyed){res.writeHead(status,{'content-type':'application/json; charset=utf-8','x-request-id':id});res.end(JSON.stringify(data));}};
 if(req.method!=='GET'||req.url!=='/api/catalog')return respond(404,{error:'not_found'});
 if(stopping||active>=16)return respond(503,{error:'busy'});
 active++;const controller=new AbortController();const timer=setTimeout(()=>controller.abort(),3000);
 const disconnected=()=>{if(!res.writableFinished)controller.abort();};res.on('close',disconnected);
 try{
   // Fixed origin and path: there is no URL supplied by a visitor.
   const upstream=await pool.request({path:'/catalog',method:'GET',signal:controller.signal,headersTimeout:1500,bodyTimeout:1500,headers:{'x-request-id':id}});
   let size=0;const chunks=[];
   for await(const chunk of upstream.body){size+=chunk.length;if(size>262144){upstream.body.destroy();throw Object.assign(new Error('oversized upstream'),{code:'MAX_BYTES'});}chunks.push(chunk);}
   if(upstream.statusCode!==200){respond(502,{error:'upstream_http',upstreamStatus:upstream.statusCode});console.log(JSON.stringify(safeLog({id,path:'/api/catalog',status:502,ms:performance.now()-started})));return;}
   const data=JSON.parse(Buffer.concat(chunks).toString('utf8'));
   if(!Array.isArray(data.items)||!data.items.every(x=>Number.isInteger(x.id)&&typeof x.name==='string'&&Number.isFinite(x.price)))throw new Error('invalid upstream shape');
   respond(200,{items:data.items});console.log(JSON.stringify(safeLog({id,path:'/api/catalog',status:200,ms:performance.now()-started})));
 }catch(error){const status=controller.signal.aborted?504:502;respond(status,{error:'upstream_unavailable'});console.log(JSON.stringify(safeLog({id,path:'/api/catalog',status,ms:performance.now()-started,error})));}
 finally{clearTimeout(timer);res.off('close',disconnected);active--;}
});
server.listen(Number(process.env.PORT||8562),'127.0.0.1',()=>console.log(`Gateway: http://127.0.0.1:${server.address().port}`));
for(const signal of ['SIGINT','SIGTERM'])process.once(signal,()=>{
 stopping=true;const force=setTimeout(()=>{server.closeAllConnections();pool.destroy();},5000);force.unref();
 server.close(async()=>{await pool.close();clearTimeout(force);});server.closeIdleConnections();
});
