// /* eslint-disable @typescript-eslint/ban-ts-comment */
// /* eslint-disable no-async-promise-executor */
// /* eslint-disable @typescript-eslint/no-explicit-any */
// /* eslint-disable @typescript-eslint/no-unused-vars */
// import http from "http";
// import fs, { watch } from "fs";
// import path from "path";
// import puppeteer, { executablePath } from "puppeteer-core";
// import ansiC from "ansi-colors";
// import { IBuiltConfig, IRunTime, ISummary } from "../../Types.js";

// import {
//   createLogStreams,
//   isValidUrl,
//   pollForFile,
//   writeFileAndCreateDir,
//   puppeteerConfigs,
//   filesHash,
//   IOutputs,
// } from "../../PM/utils.js";

// import { getRunnables } from "./utils.js";
// import { PM_1 } from "./PM_1.js";
// import { makePrompt } from "./makePrompt.js";
// import { RawData, WebSocketServer } from "ws";
// import { ChildProcess } from "child_process";
// import { WebSocketMessage } from "../../PM/types.js";

// const changes: Record<string, string> = {};

// export abstract class PM_WithProcesses extends PM_1 {
//   // summary: ISummary = {};

//   // webMetafileWatcher: fs.FSWatcher;
//   // nodeMetafileWatcher: fs.FSWatcher;
//   // importMetafileWatcher: fs.FSWatcher;
//   // pitonoMetafileWatcher: fs.FSWatcher;
//   // golangMetafileWatcher: fs.FSWatcher;

//   // ports: Record<number, string>;
//   // queue: string[];
//   // logStreams: Record<string, ReturnType<typeof createLogStreams>> = {};
//   // launchers: Record<string, () => void>;

//   // configs: any;

//   // abstract launchNode(src: string, dest: string);
//   // abstract launchWeb(src: string, dest: string);
//   // abstract launchPure(src: string, dest: string);
//   // abstract launchPython(src: string, dest: string);
//   // abstract launchGolang(src: string, dest: string);
//   // abstract startBuildProcesses(): Promise<void>;

//   // runningProcesses: Map<string, ChildProcess | Promise<any>> = new Map();

//   // allProcesses: Map<
//   //   string,
//   //   {
//   //     child?: ChildProcess;
//   //     promise?: Promise<any>;
//   //     status: "running" | "exited" | "error" | "completed";
//   //     exitCode?: number;
//   //     error?: string;
//   //     command: string;
//   //     pid?: number;
//   //     timestamp: string;
//   //     type: "process" | "promise";
//   //     category: "aider" | "bdd-test" | "build-time" | "other";
//   //     testName?: string;
//   //     platform?: "node" | "web" | "pure" | "python" | "golang";
//   //   }
//   // > = new Map();
//   // processLogs: Map<string, string[]> = new Map();

//   // clients: Set<any> = new Set();

//   constructor(configs: IBuiltConfig, name, mode) {
//     super(configs, name, mode);

//     // this.configs.tests.forEach(([t, rt, tr, sidecars]) => {
//     //   this.ensureSummaryEntry(t);
//     //   sidecars.forEach(([sidecarName]) => {
//     //     this.ensureSummaryEntry(sidecarName, true);
//     //   });
//     // });

//     // this.launchers = {};
//     // this.ports = {};
//     // this.queue = [];

//     // this.configs.ports.forEach((element) => {
//     //   this.ports[element] = ""; // set ports as open
//     // });

//     // this.httpServer = http.createServer(this.handleHttpRequest.bind(this));

//     // this.wss = new WebSocketServer({ server: this.httpServer });

//     // this.wss.on("connection", (ws) => {
//     //   this.clients.add(ws);
//     //   console.log("Client connected");

//     //   ws.on("message", (data) => {
//     //     try {
//     //       this.websocket(data, ws);
//     //     } catch (error) {
//     //       console.error("Error handling WebSocket message:", error);
//     //     }
//     //   });

//     //   ws.on("close", () => {
//     //     this.clients.delete(ws);
//     //     console.log("Client disconnected");
//     //   });

//     //   ws.on("error", (error) => {
//     //     console.error("WebSocket error:", error);
//     //     this.clients.delete(ws);
//     //   });
//     // });

//     // const httpPort = Number(process.env.HTTP_PORT) || 3000;

//     // this.httpServer.on("error", (error) => {
//     //   console.error("HTTP server error:", error);
//     //   if ((error as any).code === "EADDRINUSE") {
//     //     console.error(
//     //       `Port ${httpPort} is already in use. Please use a different port.`
//     //     );
//     //   }
//     //   process.exit(-1);
//     // });

//     // this.httpServer.listen(httpPort, "0.0.0.0", () => {
//     //   console.log(`HTTP server running on http://localhost:${httpPort}`);
//     // });
//   }

//   writeBigBoard = () => {
//     // note: this path is different from the one used by front end
//     const summaryPath = `./testeranto/reports/${this.projectName}/summary.json`;
//     const summaryData = JSON.stringify(this.summary, null, 2);
//     fs.writeFileSync(summaryPath, summaryData);

//     // Broadcast the update
//     this.webSocketBroadcastMessage({
//       type: "summaryUpdate",
//       data: this.summary,
//     });
//   };

//   addPromiseProcess(
//     processId: string,
//     promise: Promise<any>,
//     command: string,
//     category: "aider" | "bdd-test" | "build-time" | "other" = "other",
//     testName?: string,
//     platform?: "node" | "web" | "pure" | "python" | "golang",
//     onResolve?: (result: any) => void,
//     onReject?: (error: any) => void
//   ) {
//     this.runningProcesses.set(processId, promise);
//     this.allProcesses.set(processId, {
//       promise,
//       status: "running",
//       command,
//       timestamp: new Date().toISOString(),
//       type: "promise",
//       category,
//       testName,
//       platform,
//     });

//     // Initialize logs for this process
//     this.processLogs.set(processId, []);

//     const startMessage = `Starting: ${command}`;
//     const logs = this.processLogs.get(processId) || [];
//     logs.push(startMessage);
//     this.processLogs.set(processId, logs);

//     this.webSocketBroadcastMessage({
//       type: "processStarted",
//       processId,
//       command,
//       timestamp: new Date().toISOString(),
//       logs: [startMessage],
//     });

//     promise
//       .then((result) => {
//         this.runningProcesses.delete(processId);
//         const processInfo = this.allProcesses.get(processId);
//         if (processInfo) {
//           this.allProcesses.set(processId, {
//             ...processInfo,
//             status: "completed",
//             exitCode: 0,
//           });
//         }

//         // Add log entry for process completion
//         const successMessage = `Completed successfully with result: ${JSON.stringify(
//           result
//         )}`;
//         const currentLogs = this.processLogs.get(processId) || [];
//         currentLogs.push(successMessage);
//         this.processLogs.set(processId, currentLogs);

//         this.webSocketBroadcastMessage({
//           type: "processExited",
//           processId,
//           exitCode: 0,
//           timestamp: new Date().toISOString(),
//           logs: [successMessage],
//         });
//         if (onResolve) onResolve(result);
//       })
//       .catch((error) => {
//         this.runningProcesses.delete(processId);

//         const processInfo = this.allProcesses.get(processId);
//         if (processInfo) {
//           this.allProcesses.set(processId, {
//             ...processInfo,
//             status: "error",
//             error: error.message,
//           });
//         }

//         const errorMessage = `Failed with error: ${error.message}`;
//         const currentLogs = this.processLogs.get(processId) || [];
//         currentLogs.push(errorMessage);
//         this.processLogs.set(processId, currentLogs);

//         this.webSocketBroadcastMessage({
//           type: "processError",
//           processId,
//           error: error.message,
//           timestamp: new Date().toISOString(),
//           logs: [errorMessage],
//         });
//         if (onReject) onReject(error);
//       });

//     return processId;
//   }

//   getProcessesByCategory(
//     category: "aider" | "bdd-test" | "build-time" | "other"
//   ) {
//     return Array.from(this.allProcesses.entries())
//       .filter(([id, procInfo]) => procInfo.category === category)
//       .map(([id, procInfo]) => ({
//         processId: id,
//         command: procInfo.command,
//         pid: procInfo.pid,
//         status: procInfo.status,
//         exitCode: procInfo.exitCode,
//         error: procInfo.error,
//         timestamp: procInfo.timestamp,
//         category: procInfo.category,
//         testName: procInfo.testName,
//         platform: procInfo.platform,
//         logs: this.processLogs.get(id) || [],
//       }));
//   }

//   getBDDTestProcesses() {
//     return this.getProcessesByCategory("bdd-test");
//   }

//   getBuildTimeProcesses() {
//     return this.getProcessesByCategory("build-time");
//   }

//   getAiderProcesses() {
//     return this.getProcessesByCategory("aider");
//   }

//   getProcessesByTestName(testName: string) {
//     return Array.from(this.allProcesses.entries())
//       .filter(([id, procInfo]) => procInfo.testName === testName)
//       .map(([id, procInfo]) => ({
//         processId: id,
//         command: procInfo.command,
//         pid: procInfo.pid,
//         status: procInfo.status,
//         exitCode: procInfo.exitCode,
//         error: procInfo.error,
//         timestamp: procInfo.timestamp,
//         category: procInfo.category,
//         testName: procInfo.testName,
//         platform: procInfo.platform,
//         logs: this.processLogs.get(id) || [],
//       }));
//   }

//   getProcessesByPlatform(
//     platform: "node" | "web" | "pure" | "pitono" | "golang"
//   ) {
//     return Array.from(this.allProcesses.entries())
//       .filter(([id, procInfo]) => procInfo.platform === platform)
//       .map(([id, procInfo]) => ({
//         processId: id,
//         command: procInfo.command,
//         pid: procInfo.pid,
//         status: procInfo.status,
//         exitCode: procInfo.exitCode,
//         error: procInfo.error,
//         timestamp: procInfo.timestamp,
//         category: procInfo.category,
//         testName: procInfo.testName,
//         platform: procInfo.platform,
//         logs: this.processLogs.get(id) || [],
//       }));
//   }

//   bddTestIsRunning(src: string) {
//     // @ts-ignore
//     this.summary[src] = {
//       prompt: "?",
//       runTimeErrors: "?",
//       staticErrors: "?",
//       typeErrors: "?",
//       failingFeatures: {},
//     };
//   }

//   abstract tscCheck({
//     entrypoint,
//     addableFiles,
//     platform,
//   }: {
//     entrypoint: string;
//     addableFiles: string[];
//     platform: IRunTime;
//   });

//   abstract eslintCheck({
//     entrypoint,
//     addableFiles,
//     platform,
//   }: {
//     entrypoint: string;
//     addableFiles: string[];
//     platform: IRunTime;
//   });

//   async metafileOutputs(platform: IRunTime) {
//     let metafilePath: string;
//     if (platform === "python") {
//       metafilePath = `./testeranto/metafiles/python/core.json`;
//     } else {
//       metafilePath = `./testeranto/metafiles/${platform}/${this.projectName}.json`;
//     }

//     // Ensure the metafile exists
//     if (!fs.existsSync(metafilePath)) {
//       console.log(
//         ansiC.yellow(`Metafile not found at ${metafilePath}, skipping`)
//       );
//       return;
//     }

//     let metafile;
//     try {
//       const fileContent = fs.readFileSync(metafilePath).toString();
//       const parsedData = JSON.parse(fileContent);
//       // Handle different metafile structures
//       if (platform === "python") {
//         // Pitono metafile might be the entire content or have a different structure
//         metafile = parsedData.metafile || parsedData;
//       } else {
//         metafile = parsedData.metafile;
//       }
//       if (!metafile) {
//         console.log(
//           ansiC.yellow(ansiC.inverse(`No metafile found in ${metafilePath}`))
//         );
//         return;
//       }
//       // console.log(
//       //   ansiC.blue(
//       //     `Found metafile for ${platform} with ${
//       //       Object.keys(metafile.outputs || {}).length
//       //     } outputs`
//       //   )
//       // );
//     } catch (error) {
//       console.error(`Error reading metafile at ${metafilePath}:`, error);
//       return;
//     }

//     const outputs: IOutputs = metafile.outputs;

//     // Check if outputs exists and is an object
//     // if (!outputs || typeof outputs !== "object") {
//     //   console.log(
//     //     ansiC.yellow(
//     //       ansiC.inverse(`No outputs found in metafile at ${metafilePath}`)
//     //     )
//     //   );
//     //   return;
//     // }

//     // @ts-ignore
//     Object.keys(outputs).forEach(async (k) => {
//       const pattern = `testeranto/bundles/${platform}/${this.projectName}/${this.configs.src}`;
//       if (!k.startsWith(pattern)) {
//         return;
//       }

//       // // @ts-ignore
//       const output = outputs[k];
//       // Check if the output entry exists and has inputs
//       // if (!output || !output.inputs) {
//       //   return;
//       // }

//       // @ts-ignore
//       const addableFiles = Object.keys(output.inputs).filter((i) => {
//         if (!fs.existsSync(i)) return false;
//         if (i.startsWith("node_modules")) return false;
//         if (i.startsWith("./node_modules")) return false;

//         return true;
//       });

//       const f = `${k.split(".").slice(0, -1).join(".")}/`;

//       if (!fs.existsSync(f)) {
//         fs.mkdirSync(f, { recursive: true });
//       }

//       // @ts-ignore
//       let entrypoint = output.entryPoint;

//       if (entrypoint) {
//         // Normalize the entrypoint path to ensure consistent comparison
//         entrypoint = path.normalize(entrypoint);

//         const changeDigest = await filesHash(addableFiles);

//         if (changeDigest === changes[entrypoint]) {
//           // skip
//         } else {
//           changes[entrypoint] = changeDigest;

//           // Run appropriate static analysis based on platform
//           if (
//             platform === "node" ||
//             platform === "web" ||
//             platform === "pure"
//           ) {
//             this.tscCheck({ entrypoint, addableFiles, platform });
//             this.eslintCheck({ entrypoint, addableFiles, platform });
//           } else if (platform === "python") {
//             this.pythonLintCheck(entrypoint, addableFiles);
//             this.pythonTypeCheck(entrypoint, addableFiles);
//           }

//           makePrompt(
//             this.summary,
//             this.projectName,
//             entrypoint,
//             addableFiles,
//             platform
//           );

//           const testName = this.findTestNameByEntrypoint(entrypoint, platform);
//           if (testName) {
//             console.log(
//               ansiC.green(
//                 ansiC.inverse(
//                   `Source files changed, re-queueing test: ${testName}`
//                 )
//               )
//             );
//             this.addToQueue(testName, platform);
//           } else {
//             console.error(
//               `Could not find test for entrypoint: ${entrypoint} (${platform})`
//             );
//             process.exit(-1);
//           }
//         }
//       }
//     });
//   }

//   private findTestNameByEntrypoint(
//     entrypoint: string,
//     platform: IRunTime
//   ): string | null {
//     const runnables = getRunnables(this.configs.tests, this.projectName);

//     let entryPointsMap: Record<string, string>;

//     switch (platform) {
//       case "node":
//         entryPointsMap = runnables.nodeEntryPoints;
//         break;
//       case "web":
//         entryPointsMap = runnables.webEntryPoints;
//         break;
//       case "pure":
//         entryPointsMap = runnables.pureEntryPoints;
//         break;
//       case "python":
//         entryPointsMap = runnables.pythonEntryPoints;
//         break;
//       case "golang":
//         entryPointsMap = runnables.golangEntryPoints;
//         break;
//       default:
//         throw "wtf";
//     }

//     if (!entryPointsMap) {
//       console.error("idk");
//     }

//     if (!entryPointsMap[entrypoint]) {
//       console.error(`${entrypoint} not found`);
//     }

//     return entryPointsMap[entrypoint];
//   }

//   async pythonLintCheck(entrypoint: string, addableFiles: string[]) {
//     const reportDest = `testeranto/reports/${this.projectName}/${entrypoint
//       .split(".")
//       .slice(0, -1)
//       .join(".")}/python`;

//     if (!fs.existsSync(reportDest)) {
//       fs.mkdirSync(reportDest, { recursive: true });
//     }

//     const lintErrorsPath = `${reportDest}/lint_errors.txt`;

//     try {
//       // Use flake8 for Python linting
//       const { spawn } = await import("child_process");
//       const child = spawn("flake8", [entrypoint, "--max-line-length=88"], {
//         stdio: ["pipe", "pipe", "pipe"],
//       });

//       let stderr = "";
//       child.stderr.on("data", (data) => {
//         stderr += data.toString();
//       });

//       let stdout = "";
//       child.stdout.on("data", (data) => {
//         stdout += data.toString();
//       });

//       return new Promise<void>((resolve) => {
//         child.on("close", () => {
//           const output = stdout + stderr;
//           if (output.trim()) {
//             fs.writeFileSync(lintErrorsPath, output);
//             this.summary[entrypoint].staticErrors = output.split("\n").length;
//           } else {
//             if (fs.existsSync(lintErrorsPath)) {
//               fs.unlinkSync(lintErrorsPath);
//             }
//             this.summary[entrypoint].staticErrors = 0;
//           }
//           resolve();
//         });
//       });
//     } catch (error) {
//       console.error(`Error running flake8 on ${entrypoint}:`, error);
//       fs.writeFileSync(
//         lintErrorsPath,
//         `Error running flake8: ${error.message}`
//       );
//       this.summary[entrypoint].staticErrors = -1;
//     }
//   }

//   async pythonTypeCheck(entrypoint: string, addableFiles: string[]) {
//     const reportDest = `testeranto/reports/${this.projectName}/${entrypoint
//       .split(".")
//       .slice(0, -1)
//       .join(".")}/python`;

//     if (!fs.existsSync(reportDest)) {
//       fs.mkdirSync(reportDest, { recursive: true });
//     }

//     const typeErrorsPath = `${reportDest}/type_errors.txt`;

//     try {
//       // Use mypy for Python type checking
//       const { spawn } = await import("child_process");
//       const child = spawn("mypy", [entrypoint], {
//         stdio: ["pipe", "pipe", "pipe"],
//       });

//       let stderr = "";
//       child.stderr.on("data", (data) => {
//         stderr += data.toString();
//       });

//       let stdout = "";
//       child.stdout.on("data", (data) => {
//         stdout += data.toString();
//       });

//       return new Promise<void>((resolve) => {
//         child.on("close", () => {
//           const output = stdout + stderr;
//           if (output.trim()) {
//             fs.writeFileSync(typeErrorsPath, output);
//             this.summary[entrypoint].typeErrors = output.split("\n").length;
//           } else {
//             if (fs.existsSync(typeErrorsPath)) {
//               fs.unlinkSync(typeErrorsPath);
//             }
//             this.summary[entrypoint].typeErrors = 0;
//           }
//           resolve();
//         });
//       });
//     } catch (error) {
//       console.error(`Error running mypy on ${entrypoint}:`, error);
//       fs.writeFileSync(typeErrorsPath, `Error running mypy: ${error.message}`);
//       this.summary[entrypoint].typeErrors = -1;
//     }
//   }

//   abstract onBuildDone(): void;

//   async start() {
//     // Wait for build processes to complete first
//     try {
//       await this.startBuildProcesses();

//       // Generate Python metafile if there are Python tests
//       const pythonTests = this.configs.tests.filter(
//         (test) => test[1] === "python"
//       );
//       if (pythonTests.length > 0) {
//         const { generatePitonoMetafile, writePitonoMetafile } = await import(
//           "../../utils/pitonoMetafile.js"
//         );
//         const entryPoints = pythonTests.map((test) => test[0]);
//         const metafile = await generatePitonoMetafile(
//           this.projectName,
//           entryPoints
//         );
//         writePitonoMetafile(this.projectName, metafile);
//       }

//       this.onBuildDone();
//     } catch (error) {
//       console.error("Build processes failed:", error);
//       return;
//     }

//     // Continue with the rest of the setup after builds are done
//     this.mapping().forEach(async ([command, func]) => {
//       globalThis[command] = func;
//     });

//     if (!fs.existsSync(`testeranto/reports/${this.projectName}`)) {
//       fs.mkdirSync(`testeranto/reports/${this.projectName}`);
//     }

//     try {
//       this.browser = await puppeteer.launch(puppeteerConfigs);
//     } catch (e) {
//       console.error(e);
//       console.error(
//         "could not start chrome via puppeter. Check this path: ",
//         executablePath
//       );
//     }

//     const runnables = getRunnables(this.configs.tests, this.projectName);
//     const {
//       nodeEntryPoints,
//       webEntryPoints,
//       pureEntryPoints,
//       pythonEntryPoints,
//       golangEntryPoints,
//     } = runnables;

//     // Add all tests to the queue
//     [
//       ["node", nodeEntryPoints],
//       ["web", webEntryPoints],
//       ["pure", pureEntryPoints],
//       ["python", pythonEntryPoints],
//       ["golang", golangEntryPoints],
//     ].forEach(([runtime, entryPoints]: [IRunTime, Record<string, string>]) => {
//       Object.keys(entryPoints).forEach((entryPoint) => {
//         // Create the report directory
//         const reportDest = `testeranto/reports/${this.projectName}/${entryPoint
//           .split(".")
//           .slice(0, -1)
//           .join(".")}/${runtime}`;
//         if (!fs.existsSync(reportDest)) {
//           fs.mkdirSync(reportDest, { recursive: true });
//         }

//         // Add to the processing queue
//         this.addToQueue(entryPoint, runtime);
//       });
//     });

//     // Set up metafile watchers for each runtime
//     const runtimeConfigs = [
//       ["node", nodeEntryPoints],
//       ["web", webEntryPoints],
//       ["pure", pureEntryPoints],
//       ["python", pythonEntryPoints],
//       ["golang", golangEntryPoints],
//     ];

//     for (const [runtime, entryPoints] of runtimeConfigs) {
//       if (Object.keys(entryPoints).length === 0) continue;

//       // For python, the metafile path is different
//       let metafile: string;
//       if (runtime === "python") {
//         metafile = `./testeranto/metafiles/${runtime}/core.json`;
//       } else {
//         metafile = `./testeranto/metafiles/${runtime}/${this.projectName}.json`;
//       }

//       // Ensure the directory exists
//       const metafileDir = metafile.split("/").slice(0, -1).join("/");
//       if (!fs.existsSync(metafileDir)) {
//         fs.mkdirSync(metafileDir, { recursive: true });
//       }

//       try {
//         // For python, we may need to generate the metafile first
//         if (runtime === "python" && !fs.existsSync(metafile)) {
//           const { generatePitonoMetafile, writePitonoMetafile } = await import(
//             "../../utils/pitonoMetafile.js"
//           );
//           const entryPointList = Object.keys(entryPoints);
//           if (entryPointList.length > 0) {
//             const metafileData = await generatePitonoMetafile(
//               this.projectName,
//               entryPointList
//             );
//             writePitonoMetafile(this.projectName, metafileData);
//           }
//         }

//         await pollForFile(metafile);
//         // console.log("Found metafile for", runtime, metafile);

//         // Set up watcher for the metafile with debouncing
//         let timeoutId: NodeJS.Timeout;
//         const watcher = watch(metafile, async (e, filename) => {
//           // Debounce to avoid multiple rapid triggers
//           clearTimeout(timeoutId);
//           timeoutId = setTimeout(async () => {
//             console.log(
//               ansiC.yellow(ansiC.inverse(`< ${e} ${filename} (${runtime})`))
//             );
//             try {
//               await this.metafileOutputs(runtime as IRunTime);
//               // After processing metafile changes, check the queue to run tests
//               console.log(
//                 ansiC.blue(
//                   `Metafile processed, checking queue for tests to run`
//                 )
//               );
//               this.checkQueue();
//             } catch (error) {
//               console.error(`Error processing metafile changes:`, error);
//             }
//           }, 300); // 300ms debounce
//         });

//         // Store the watcher based on runtime
//         switch (runtime) {
//           case "node":
//             this.nodeMetafileWatcher = watcher;
//             break;
//           case "web":
//             this.webMetafileWatcher = watcher;
//             break;
//           case "pure":
//             this.importMetafileWatcher = watcher;
//             break;
//           case "python":
//             this.pitonoMetafileWatcher = watcher;
//             break;
//           case "golang":
//             this.golangMetafileWatcher = watcher;
//             break;
//         }

//         // Read the metafile immediately
//         await this.metafileOutputs(runtime as IRunTime);
//       } catch (error) {
//         console.error(`Error setting up watcher for ${runtime}:`, error);
//       }
//     }
//   }

//   async stop() {
//     console.log(ansiC.inverse("Testeranto-Run is shutting down gracefully..."));
//     this.mode = "once";
//     this.nodeMetafileWatcher.close();
//     this.webMetafileWatcher.close();
//     this.importMetafileWatcher.close();
//     if (this.pitonoMetafileWatcher) {
//       this.pitonoMetafileWatcher.close();
//     }

//     // if (this.gitWatcher) {
//     //   this.gitWatcher.close();
//     // }

//     // if (this.gitWatchTimeout) {
//     //   clearTimeout(this.gitWatchTimeout);
//     // }

//     Object.values(this.logStreams || {}).forEach((logs) => logs.closeAll());

//     if (this.wss) {
//       this.wss.close(() => {
//         console.log("WebSocket server closed");
//       });
//     }

//     this.clients.forEach((client) => {
//       client.terminate();
//     });
//     this.clients.clear();

//     if (this.httpServer) {
//       this.httpServer.close(() => {
//         console.log("HTTP server closed");
//       });
//     }
//     this.checkForShutdown();
//   }

//   receiveFeaturesV2 = (
//     reportDest: string,
//     srcTest: string,
//     platform: IRunTime
//   ) => {
//     const featureDestination = path.resolve(
//       process.cwd(),
//       "reports",
//       "features",
//       "strings",
//       srcTest.split(".").slice(0, -1).join(".") + ".features.txt"
//     );

//     const testReportPath = `${reportDest}/tests.json`;
//     if (!fs.existsSync(testReportPath)) {
//       console.error(`tests.json not found at: ${testReportPath}`);
//       return;
//     }

//     const testReport = JSON.parse(fs.readFileSync(testReportPath, "utf8"));

//     // Add full path information to each test
//     if (testReport.tests) {
//       testReport.tests.forEach((test) => {
//         // Add the full path to each test
//         test.fullPath = path.resolve(process.cwd(), srcTest);
//       });
//     }

//     // Add full path to the report itself
//     testReport.fullPath = path.resolve(process.cwd(), srcTest);

//     // Write the modified report back
//     fs.writeFileSync(testReportPath, JSON.stringify(testReport, null, 2));

//     testReport.features
//       .reduce(async (mm, featureStringKey) => {
//         const accum = await mm;

//         const isUrl = isValidUrl(featureStringKey);

//         if (isUrl) {
//           const u = new URL(featureStringKey);

//           if (u.protocol === "file:") {
//             accum.files.push(u.pathname);
//           } else if (u.protocol === "http:" || u.protocol === "https:") {
//             const newPath = `${process.cwd()}/testeranto/features/external/${
//               u.hostname
//             }${u.pathname}`;

//             const body = await this.configs.featureIngestor(featureStringKey);

//             writeFileAndCreateDir(newPath, body);
//             accum.files.push(newPath);
//           }
//         } else {
//           await fs.promises.mkdir(path.dirname(featureDestination), {
//             recursive: true,
//           });

//           accum.strings.push(featureStringKey);
//         }

//         return accum;
//       }, Promise.resolve({ files: [] as string[], strings: [] as string[] }))

//       .then(({ files }: { files: string[]; strings: string[] }) => {
//         // Markdown files must be referenced in the prompt but string style features are already present in the tests.json file

//         fs.writeFileSync(
//           `testeranto/reports/${this.projectName}/${srcTest
//             .split(".")
//             .slice(0, -1)
//             .join(".")}/${platform}/featurePrompt.txt`,
//           files
//             .map((f) => {
//               return `/read ${f}`;
//             })
//             .join("\n")
//         );
//       });

//     testReport.givens.forEach((g) => {
//       if (g.failed === true) {
//         // @ts-ignore
//         this.summary[srcTest].failingFeatures[g.key] = g.features;
//       }
//     });

//     this.writeBigBoard();
//   };

//   addToQueue(src: string, runtime: IRunTime) {
//     // Ensure we're using the original test source path, not a bundle path
//     // The src parameter might be a bundle path from metafile changes
//     // We need to find the corresponding test source path

//     // First, check if this looks like a bundle path (contains 'testeranto/bundles')
//     if (src.includes("testeranto/bundles")) {
//       // Try to find the original test name that corresponds to this bundle
//       const runnables = getRunnables(this.configs.tests, this.projectName);
//       const allEntryPoints = [
//         ...Object.entries(runnables.nodeEntryPoints),
//         ...Object.entries(runnables.webEntryPoints),
//         ...Object.entries(runnables.pureEntryPoints),
//         ...Object.entries(runnables.pythonEntryPoints),
//         ...Object.entries(runnables.golangEntryPoints),
//       ];

//       // Normalize the source path for comparison
//       const normalizedSrc = path.normalize(src);

//       for (const [testName, bundlePath] of allEntryPoints) {
//         const normalizedBundlePath = path.normalize(bundlePath as string);
//         // Check if the source path ends with the bundle path
//         if (normalizedSrc.endsWith(normalizedBundlePath)) {
//           // Use the original test name instead of the bundle path
//           src = testName;
//           break;
//         }
//       }
//     }

//     // First, clean up any existing processes for this test
//     this.cleanupTestProcesses(src);

//     // Add the test to the queue (using the original test source path)
//     // Make sure we don't add duplicates
//     if (!this.queue.includes(src)) {
//       this.queue.push(src);
//       console.log(
//         ansiC.green(
//           ansiC.inverse(`Added ${src} (${runtime}) to the processing queue`)
//         )
//       );
//       // Try to process the queue
//       this.checkQueue();
//     } else {
//       console.log(
//         ansiC.yellow(
//           ansiC.inverse(`Test ${src} is already in the queue, skipping`)
//         )
//       );
//     }
//   }

//   private cleanupTestProcesses(testName: string) {
//     // Find and clean up any running processes for this test
//     const processesToCleanup: string[] = [];

//     // Find all process IDs that match this test name
//     for (const [processId, processInfo] of this.allProcesses.entries()) {
//       if (
//         processInfo.testName === testName &&
//         processInfo.status === "running"
//       ) {
//         processesToCleanup.push(processId);
//       }
//     }

//     // Clean up each process
//     processesToCleanup.forEach((processId) => {
//       const processInfo = this.allProcesses.get(processId);
//       if (processInfo) {
//         // Kill child process if it exists
//         if (processInfo.child) {
//           try {
//             processInfo.child.kill();
//           } catch (error) {
//             console.error(`Error killing process ${processId}:`, error);
//           }
//         }

//         // Update process status
//         this.allProcesses.set(processId, {
//           ...processInfo,
//           status: "exited",
//           exitCode: -1,
//           error: "Killed due to source file change",
//         });

//         // Remove from running processes
//         this.runningProcesses.delete(processId);

//         // Broadcast process exit
//         this.webSocketBroadcastMessage({
//           type: "processExited",
//           processId,
//           exitCode: -1,
//           timestamp: new Date().toISOString(),
//           logs: ["Process killed due to source file change"],
//         });
//       }
//     });
//   }

//   checkQueue() {
//     // Process all items in the queue
//     while (this.queue.length > 0) {
//       const x = this.queue.pop();
//       if (!x) continue;

//       // Check if this test is already running
//       let isRunning = false;
//       for (const processInfo of this.allProcesses.values()) {
//         if (processInfo.testName === x && processInfo.status === "running") {
//           isRunning = true;
//           break;
//         }
//       }

//       if (isRunning) {
//         console.log(
//           ansiC.yellow(
//             `Skipping ${x} - already running, will be re-queued when current run completes`
//           )
//         );
//         continue;
//       }

//       const test = this.configs.tests.find((t) => t[0] === x);
//       if (!test) {
//         console.error(`test is undefined ${x}`);
//         continue;
//       }

//       // Get the appropriate launcher based on the runtime type
//       const runtime = test[1];

//       const runnables = getRunnables(this.configs.tests, this.projectName);
//       let dest: string;

//       switch (runtime) {
//         case "node":
//           dest = runnables.nodeEntryPoints[x];
//           if (dest) {
//             this.launchNode(x, dest);
//           } else {
//             console.error(`No destination found for node test: ${x}`);
//           }
//           break;
//         case "web":
//           dest = runnables.webEntryPoints[x];
//           if (dest) {
//             this.launchWeb(x, dest);
//           } else {
//             console.error(`No destination found for web test: ${x}`);
//           }
//           break;
//         case "pure":
//           dest = runnables.pureEntryPoints[x];
//           if (dest) {
//             this.launchPure(x, dest);
//           } else {
//             console.error(`No destination found for pure test: ${x}`);
//           }
//           break;
//         case "python":
//           dest = runnables.pythonEntryPoints[x];
//           if (dest) {
//             this.launchPython(x, dest);
//           } else {
//             console.error(`No destination found for python test: ${x}`);
//           }
//           break;
//         case "golang":
//           dest = runnables.golangEntryPoints[x];
//           if (dest) {
//             // For Golang, we need to build and run the executable
//             // The dest is the path to the generated wrapper file
//             this.launchGolang(x, dest);
//           } else {
//             console.error(`No destination found for golang test: ${x}`);
//           }
//           break;
//         default:
//           console.error(`Unknown runtime: ${runtime} for test ${x}`);
//           break;
//       }
//     }

//     if (this.queue.length === 0) {
//       console.log(ansiC.inverse(`The queue is empty`));
//     }
//   }

//   checkForShutdown = () => {
//     this.checkQueue();

//     console.log(
//       ansiC.inverse(
//         `The following jobs are awaiting resources: ${JSON.stringify(
//           this.queue
//         )}`
//       )
//     );
//     console.log(
//       ansiC.inverse(`The status of ports: ${JSON.stringify(this.ports)}`)
//     );

//     this.writeBigBoard();

//     if (this.mode === "dev") return;

//     let inflight = false;

//     Object.keys(this.summary).forEach((k) => {
//       if (this.summary[k].prompt === "?") {
//         console.log(ansiC.blue(ansiC.inverse(`🕕 prompt ${k}`)));
//         inflight = true;
//       }
//     });

//     Object.keys(this.summary).forEach((k) => {
//       if (this.summary[k].runTimeErrors === "?") {
//         console.log(ansiC.blue(ansiC.inverse(`🕕 runTimeError ${k}`)));
//         inflight = true;
//       }
//     });

//     Object.keys(this.summary).forEach((k) => {
//       if (this.summary[k].staticErrors === "?") {
//         console.log(ansiC.blue(ansiC.inverse(`🕕 staticErrors ${k}`)));
//         inflight = true;
//       }
//     });

//     Object.keys(this.summary).forEach((k) => {
//       if (this.summary[k].typeErrors === "?") {
//         console.log(ansiC.blue(ansiC.inverse(`🕕 typeErrors ${k}`)));
//         inflight = true;
//       }
//     });

//     this.writeBigBoard();

//     if (!inflight) {
//       if (this.browser) {
//         if (this.browser) {
//           this.browser.disconnect().then(() => {
//             console.log(
//               ansiC.inverse(`${this.projectName} has been tested. Goodbye.`)
//             );
//             process.exit();
//           });
//         }
//       }
//     }
//   };

//   private ensureSummaryEntry(src: string, isSidecar = false) {
//     if (!this.summary[src]) {
//       // @ts-ignore
//       this.summary[src] = {
//         typeErrors: undefined,
//         staticErrors: undefined,
//         runTimeErrors: undefined,
//         prompt: undefined,
//         failingFeatures: {},
//       };
//       if (isSidecar) {
//         // Sidecars don't need all fields
//         // delete this.summary[src].runTimeError;
//         // delete this.summary[src].prompt;
//       }
//     }
//     return this.summary[src];
//   }

//   websocket(data: RawData) {
//     const message: WebSocketMessage = JSON.parse(data.toString());
//     if (message.type === "chatMessage") {
//       // Always record the message, even if aider is not available
//       console.log(`Received chat message: ${message.content}`);

//       // Pass to PM_WithHelpo if available
//       if ((this as any).handleChatMessage) {
//         (this as any).handleChatMessage(message.content);
//       } else {
//         console.log("PM_WithHelpo not available - message not processed");
//       }
//       return;
//     }

//     super.websocket(data);
//   }
// }

// // onBuildDone(): void {
// //   console.log("Build processes completed");
// //   // The builds are done, which means the files are ready to be watched
// //   // This matches the original behavior where builds completed before PM_Main started

// //   // Start Git watcher for development mode
// //   this.startGitWatcher();
// // }
