import { createServer, request } from "node:http";
import { createRequire } from "node:module";
const { WebSocket, WebSocketServer } = createRequire(import.meta.resolve("@effect/platform-node/NodeHttpServer"))("ws");

export async function holdFilePatches(port: number, upstreamPort: number, paths: string[]) {
  let holding = true;
  const held: Array<() => void> = [];
  const sockets = new Set<any>();
  const counts={http:0,webSockets:0,frames:0,review:0};
  const server = createServer((req, res) => {
    counts.http++;
    const upstream = request({hostname:"127.0.0.1",port:upstreamPort,path:req.url,method:req.method,headers:req.headers}, reply => {
      res.writeHead(reply.statusCode ?? 502, reply.headers); reply.pipe(res);
    });
    upstream.on("error",()=>res.end()); req.pipe(upstream);
  });
  const wss = new WebSocketServer({noServer:true});
  server.on("upgrade",(req,socket,head)=>wss.handleUpgrade(req,socket,head,(downstream:any)=>{
    counts.webSockets++;
    const headers={...req.headers};
    delete headers["sec-websocket-key"]; delete headers["sec-websocket-version"]; delete headers.upgrade; delete headers.connection; delete headers["sec-websocket-extensions"];
    const upstream = new WebSocket(`ws://127.0.0.1:${upstreamPort}${req.url}`, {headers});
    sockets.add(upstream); sockets.add(downstream);
    const pending: Array<()=>void> = [];
    let connected=false;
    upstream.on("open",()=>{connected=true; for(const send of pending)send();});
    downstream.on("message",(data:any,binary:boolean)=>{
      const send=()=>{if(upstream.readyState===1)upstream.send(data,{binary});};
      const deliver=()=>connected?send():pending.push(send);
      counts.frames++;
      const text=data.toString();
      if(text.includes("review.getDiffPreview")) counts.review++;
      if(holding && text.includes("review.getDiffPreview") && paths.some(path=>text.includes(path))) held.push(deliver);
      else deliver();
    });
    upstream.on("message",(data:any,binary:boolean)=>{if(downstream.readyState===1)downstream.send(data,{binary});});
    for(const [one,other] of [[upstream,downstream],[downstream,upstream]]) {
      one.on("close",()=>{sockets.delete(one);other.terminate();});
      one.on("error",()=>other.terminate());
    }
  }));
  await new Promise<void>(resolve=>server.listen(port,"0.0.0.0",resolve));
  return {
    heldCount:()=>held.length,
    stats:()=>({...counts,held:held.length}),
    release:()=>{holding=false;for(const send of held.splice(0))send();},
    close:async()=>{for(const socket of sockets)socket.terminate();wss.close();await new Promise<void>(resolve=>server.close(()=>resolve()));}
  };
}
