import { Option } from "effect";

/**
 * Extract the last Python exception line from a MarimoCommandError cause.
 *
 * The cause is typically a ResponseError from vscode-languageclient with:
 * - `.message`: often generic ("An error has occurred")
 * - `.data.traceback`: Python traceback frames (just file/line info)
 * - `.cause`: may contain an Error with the full RuntimeError message
 */
export function extractPythonError(cause: unknown): Option.Option<string> {
  if (!cause || typeof cause !== "object") {
    return Option.none();
  }

  // Check Error.cause chain (may contain "RuntimeError: Kernel failed to start: ...")
  if (hasErrorCause(cause)) {
    return extractLastException(cause.cause.message);
  }

  // Check .message directly (if not the generic pygls message)
  if (cause instanceof Error && cause.message) {
    if (cause.message !== "An error has occurred") {
      const detail = extractLastException(cause.message);
      if (Option.isSome(detail)) {
        return detail;
      }
      return Option.some(truncate(cause.message, 500));
    }
  }

  return Option.none();
}

function hasErrorCause(value: object): value is { cause: Error } {
  return "cause" in value && value.cause instanceof Error;
}

/** Find the last Python exception line (e.g. "ImportError: ...") in a traceback string. */
function extractLastException(text: string): Option.Option<string> {
  const lines = text.split("\n");
  for (let i = lines.length - 1; i >= 0; i--) {
    const line = lines[i].trim();
    if (line && /^\w+(\.\w+)*(Error|Exception):/.test(line)) {
      return Option.some(truncate(line, 500));
    }
  }
  return Option.none();
}

function truncate(text: string, maxLength: number): string {
  if (text.length <= maxLength) {
    return text;
  }
  return `${text.slice(0, maxLength)}…`;
}
