const ANSI_PATTERN =
  /\u001b\[[0-9;]*m|\u001b\]8;;.*?\u001b\\|\u001b\][0-9];.*?\u0007/g;

const ANSI_COLOR_MAP: Record<string, string> = {
  "30": "text-zinc-400",
  "31": "text-red-400",
  "32": "text-green-400",
  "33": "text-yellow-400",
  "34": "text-blue-400",
  "35": "text-purple-400",
  "36": "text-cyan-400",
  "37": "text-zinc-200",
  "90": "text-zinc-500",
  "91": "text-red-300",
  "92": "text-green-300",
  "93": "text-yellow-300",
  "94": "text-blue-300",
  "95": "text-purple-300",
  "96": "text-cyan-300",
  "97": "text-zinc-100",
};

export function stripAnsi(text: string): string {
  return text.replace(ANSI_PATTERN, "");
}

export interface AnsiSegment {
  text: string;
  className?: string;
}

export function parseAnsiLine(line: string): AnsiSegment[] {
  const segments: AnsiSegment[] = [];
  let currentClass: string | undefined;
  let buffer = "";
  const regex = /\u001b\[([0-9;]*)m/g;
  let lastIndex = 0;
  let match: RegExpExecArray | null;

  while ((match = regex.exec(line)) !== null) {
    if (match.index > lastIndex) {
      const text = line.slice(lastIndex, match.index);
      if (text) {
        segments.push({ text, className: currentClass });
      }
    }

    const codes = match[1].split(";").filter(Boolean);
    if (codes.length === 0 || codes.includes("0")) {
      currentClass = undefined;
    } else {
      const colorCode = codes.find((code) => ANSI_COLOR_MAP[code]);
      currentClass = colorCode ? ANSI_COLOR_MAP[colorCode] : currentClass;
    }

    lastIndex = regex.lastIndex;
  }

  buffer = line.slice(lastIndex).replace(ANSI_PATTERN, "");
  if (buffer) {
    segments.push({ text: buffer, className: currentClass });
  }

  if (segments.length === 0) {
    return [{ text: stripAnsi(line) }];
  }

  return segments;
}
