// A network gate: forwards real HTTP/WebSocket traffic and holds only list replies.
import { createServer, request } from "node:http";
import { readdirSync } from "node:fs";
import { createRequire } from "node:module";
import { APP_DIR } from "../e2e.config.ts";
const directory=readdirSync(`${APP_DIR}/node_modules/.pnpm`).find(n=>n.startsWith("ws@8."));
const {WebSocket,WebSocketServer}=createRequire(import.meta.url)(`${APP_DIR}/node_modules/.pnpm/${directory}/node_modules/ws`);
export async function networkGate(serverPort:number){
 const sockets=new Set<any>();
 const proof={started:0,interrupted:0,completed:0};
 let holding=false;
 const releaseQueue:Array<()=>void>=[];
 const http=createServer((req,res)=>{
  const upstream=request({hostname:"127.0.0.1",port:serverPort,path:req.url,method:req.method,headers:req.headers},r=>{res.writeHead(r.statusCode??502,r.headers);r.pipe(res);});
  upstream.on("error",()=>res.end());req.pipe(upstream);
 });
 const ws=new WebSocketServer({noServer:true});
 http.on("upgrade",(req,socket,head)=>ws.handleUpgrade(req,socket,head,(client:any)=>{
  const upstream=new WebSocket(`ws://127.0.0.1:${serverPort}${req.url}`,{headers:{...req.headers,host:`127.0.0.1:${serverPort}`}});
  sockets.add(client);sockets.add(upstream);
  const ids=new Set<string>();const queue:any[]=[];
  client.on("message",(data:any,isBinary:boolean)=>{
   const frames=String(data).split("\n").flatMap(line=>{try{const p=JSON.parse(line);return Array.isArray(p)?p:[p];}catch{return [];}});
   for(const f of frames){if(f.tag==="peerLinks.list"){ids.add(String(f.id));proof.started++;}if(f._tag==="Interrupt"&&ids.has(String(f.requestId)))proof.interrupted++;}
   if(upstream.readyState===WebSocket.OPEN)upstream.send(data,{binary:isBinary});else queue.push([data,isBinary]);
  });
  upstream.on("open",()=>{for(const [data,binary] of queue)upstream.send(data,{binary});});
  upstream.on("message",(data:any,isBinary:boolean)=>{
   const relevant=String(data).split("\n").some(line=>{try{const p=JSON.parse(line);return ids.has(String(p.requestId));}catch{return false;}});
   const forward=()=>{if(client.readyState===WebSocket.OPEN){client.send(data,{binary:isBinary});if(relevant)proof.completed++;}};
   if(holding&&relevant)releaseQueue.push(forward);else forward();
  });
  upstream.on("error",()=>client.close());client.on("error",()=>upstream.close());
  client.on("close",()=>upstream.close());upstream.on("close",()=>client.close());
 }));
 await new Promise<void>(r=>http.listen(0,"0.0.0.0",r));
 return {port:(http.address() as any).port,proof,hold:()=>{holding=true;},release:()=>{holding=false;for(const send of releaseQueue.splice(0))send();},stop:async()=>{for(const s of sockets)s.terminate();await new Promise<void>(r=>http.close(()=>r()));}};
}
