import { Readable } from "stream";
import split2 from "split2";
import type * as vscode from "vscode";
import type { LogEntry } from "@codeflash/types";
import { LSPMessageType } from "@codeflash/types";
import { Logger } from "./logger";
import { randomUUID } from "crypto";
import type { LogsEventEmitter } from "./logsEventEmitter";

const delimiter = "\\u241F";

export function createInterceptingOutputChannel(
  innerChannel: vscode.OutputChannel,
  optimizationEventEmitter?: LogsEventEmitter,
): vscode.OutputChannel & { lastTracebackMessage: string | null } {
  class WrappedChannel implements vscode.OutputChannel {
    constructor(
      private readonly inner: vscode.OutputChannel,
      private readonly optimizationEventEmitter?: LogsEventEmitter,
    ) {
      this.logger = new Logger("LSP-MESSAGE-INTERCEPTOR");

      this.input = new Readable({
        read() {}, // no need for the read implementation, since we push data manually
      });

      this.input
        .pipe(split2(delimiter, { maxLength: 2 * 1024 * 1024 })) // 2mb
        .on("data", (chunk: string) => {
          if (!chunk.trim()) {
            return;
          }
          try {
            const parsed = JSON.parse(chunk.trim());
            if (Object.values(LSPMessageType).includes(parsed?.type)) {
              const uuid = randomUUID();
              const message = parsed as LogEntry;
              this.optimizationEventEmitter?.fire({
                ...message,
                id: uuid,
              });
            }
          } catch {
            // ignore JSON parse errors
          }
        });
    }

    name: string = innerChannel.name;
    private logger: Logger;
    private input: Readable;

    public lastTracebackMessage: string | null = null;

    append(value: string): void {
      this.storeLastTracebackMessage(value);
      this.logger.debug("LSP Message: " + value);
      this.input.push(value);
    }

    appendLine(value: string): void {
      this.storeLastTracebackMessage(value);
      this.logger.debug("LSP Message: " + value);
      this.input.push(value);
    }

    // private storeLastTracebackMessage = (message: string) => {
    //   if (message.trim().startsWith("Traceback (most recent call last):")) {
    //     this.lastTracebackMessage = message;
    //   }
    // };

    private storeLastTracebackMessage(message: string) {
      const lines = message.trim().split("\n");
      // If it looks like the final exception line, stop collecting
      for (const line of lines) {
        if (/^[A-Za-z]+Error:/.test(line)) {
          this.lastTracebackMessage = line;
          break;
        }
      }
    }

    clear() {}
    show() {}
    hide() {}
    dispose() {
      this.input.destroy();
      this.inner.dispose();
      this.logger.dispose();
    }
    replace() {}
  }

  return new WrappedChannel(innerChannel, optimizationEventEmitter);
}
