/* prettier-ignore */
'use strict';

var AssertionError;
function loadAssertionError() {
  if (AssertionError === undefined) {
    AssertionError = require("internal/assert/assertion_error");
  }
}

// const { Buffer } = require('node:buffer');
// const {
//   isErrorStackTraceLimitWritable,
//   overrideStackTrace,
// } = require('internal/errors');
// const { openSync, closeSync, readSync } = require('node:fs');
// // const { EOL } = require('internal/constants');
// // const { BuiltinModule } = require('internal/bootstrap/realm');
// // const { isError } = require('internal/util');

// const errorCache = new SafeMap();
// // const { fileURLToPath } = require('internal/url');

// let parseExpressionAt;
// let findNodeAround;
// let tokenizer;
// let decoder;

// // Escape control characters but not \n and \t to keep the line breaks and
// // indentation intact.
// // eslint-disable-next-line no-control-regex
// const escapeSequencesRegExp = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
// const meta = [
//   '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
//   '\\u0005', '\\u0006', '\\u0007', '\\b', '',
//   '', '\\u000b', '\\f', '', '\\u000e',
//   '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013',
//   '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018',
//   '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d',
//   '\\u001e', '\\u001f',
// ];

// const escapeFn = (str) => meta[StringPrototypeCharCodeAt(str, 0)];

// function findColumn(fd, column: number, code: string) {
//   if (code.length > column + 100) {
//     try {
//       return parseCode(code, column);
//     } catch {
//       // End recursion in case no code could be parsed. The expression should
//       // have been found after 2500 characters, so stop trying.
//       if (code.length - column > 2500) {
//         // eslint-disable-next-line no-throw-literal
//         throw null;
//       }
//     }
//   }
//   // Read up to 2500 bytes more than necessary in columns. That way we address
//   // multi byte characters and read enough data to parse the code.
//   const bytesToRead = column - code.length + 2500;
//   const buffer = Buffer.allocUnsafe(bytesToRead);
//   const bytesRead = readSync(fd, buffer, 0, bytesToRead);
//   code += decoder.write(buffer.slice(0, bytesRead));
//   // EOF: fast path.
//   if (bytesRead < bytesToRead) {
//     return parseCode(code, column);
//   }
//   // Read potentially missing code.
//   return findColumn(fd, column, code);
// }

// function getCode(fd, line: number, column: number) {
//   let bytesRead = 0;
//   if (line === 0) {
//     // Special handle line number one. This is more efficient and simplifies the
//     // rest of the algorithm. Read more than the regular column number in bytes
//     // to prevent multiple reads in case multi byte characters are used.
//     return findColumn(fd, column, '');
//   }
//   let lines = 0;
//   // Prevent blocking the event loop by limiting the maximum amount of
//   // data that may be read.
//   let maxReads = 32; // bytesPerRead * maxReads = 512 KiB
//   const bytesPerRead = 16384;
//   // Use a single buffer up front that is reused until the call site is found.
//   let buffer = Buffer.allocUnsafe(bytesPerRead);
//   while (maxReads-- !== 0) {
//     // Only allocate a new buffer in case the needed line is found. All data
//     // before that can be discarded.
//     buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead);
//     bytesRead = readSync(fd, buffer, 0, bytesPerRead);
//     // Read the buffer until the required code line is found.
//     for (let i = 0; i < bytesRead; i++) {
//       if (buffer[i] === 10 && ++lines === line) {
//         // If the end of file is reached, directly parse the code and return.
//         if (bytesRead < bytesPerRead) {
//           return parseCode(buffer.toString('utf8', i + 1, bytesRead), column);
//         }
//         // Check if the read code is sufficient or read more until the whole
//         // expression is read. Make sure multi byte characters are preserved
//         // properly by using the decoder.
//         const code = decoder.write(buffer.slice(i + 1, bytesRead));
//         return findColumn(fd, column, code);
//       }
//     }
//   }
// }

// TODO: parse source to get assertion message
// function parseCode(code, offset) {
//   // Lazy load acorn.
//   if (parseExpressionAt === undefined) {
//     const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
//     ({ findNodeAround } = require('internal/deps/acorn/acorn-walk/dist/walk'));

//     parseExpressionAt = FunctionPrototypeBind(Parser.parseExpressionAt, Parser);
//     tokenizer = FunctionPrototypeBind(Parser.tokenizer, Parser);
//   }
//   let node;
//   let start;
//   // Parse the read code until the correct expression is found.
//   for (const token of tokenizer(code, { ecmaVersion: 'latest' })) {
//     start = token.start;
//     if (start > offset) {
//       // No matching expression found. This could happen if the assert
//       // expression is bigger than the provided buffer.
//       break;
//     }
//     try {
//       node = parseExpressionAt(code, start, { ecmaVersion: 'latest' });
//       // Find the CallExpression in the tree.
//       node = findNodeAround(node, offset, 'CallExpression');
//       if (node?.node.end >= offset) {
//         return [
//           node.node.start,
//           StringPrototypeReplace(StringPrototypeSlice(code,
//                                                       node.node.start, node.node.end),
//                                  escapeSequencesRegExp, escapeFn),
//         ];
//       }
//     // eslint-disable-next-line no-unused-vars
//     } catch (err) {
//       continue;
//     }
//   }
//   // eslint-disable-next-line no-throw-literal
//   throw null;
// }

function getErrMessage(_message: string, _value: unknown, _fn: Function): string | undefined {
  // const tmpLimit = Error.stackTraceLimit;
  // const errorStackTraceLimitIsWritable = isErrorStackTraceLimitWritable();
  // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it
  // does to much work.
  // if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 1;
  // We only need the stack trace. To minimize the overhead use an object
  // instead of an error.
  // const err = {};
  // ErrorCaptureStackTrace(err, fn);
  // if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
  // overrideStackTrace.set(err, (_, stack) => stack);
  // const call = err.stack[0];
  //
  // if (fn.name === "ok") {
  //   return `The expression evaluated to a falsy value:\n\n  assert.ok(${value})\n`;
  // }
  //   let filename = call.getFileName();
  //   const line = call.getLineNumber() - 1;
  //   let column = call.getColumnNumber() - 1;
  //   let identifier;
  //   let code;
  //   if (filename) {
  //     identifier = `${filename}${line}${column}`;
  //     // Skip Node.js modules!
  //     if (StringPrototypeStartsWith(filename, 'node:') &&
  //         BuiltinModule.exists(StringPrototypeSlice(filename, 5))) {
  //       errorCache.set(identifier, undefined);
  //       return;
  //     }
  //   } else {
  //     return message;
  //   }
  //   if (errorCache.has(identifier)) {
  //     return errorCache.get(identifier);
  //   }
  //   let fd;
  //   try {
  //     // Set the stack trace limit to zero. This makes sure unexpected token
  //     // errors are handled faster.
  //     if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 0;
  //     if (filename) {
  //       if (decoder === undefined) {
  //         const { StringDecoder } = require('string_decoder');
  //         decoder = new StringDecoder('utf8');
  //       }
  //       // ESM file prop is a file proto. Convert that to path.
  //       // This ensure opensync will not throw ENOENT for ESM files.
  //       const fileProtoPrefix = 'file://';
  //       if (StringPrototypeStartsWith(filename, fileProtoPrefix)) {
  //         filename = Bun.fileURLToPath(filename);
  //       }
  //       fd = openSync(filename, 'r', 0o666);
  //       // Reset column and message.
  //       ({ 0: column, 1: message } = getCode(fd, line, column));
  //       // Flush unfinished multi byte characters.
  //       decoder.end();
  //     } else {
  //       for (let i = 0; i < line; i++) {
  //         code = StringPrototypeSlice(code,
  //                                     StringPrototypeIndexOf(code, '\n') + 1);
  //       }
  //       // ({ 0: column, 1: message } = parseCode(code, column));
  //       throw new Error("todo: parseCode");
  //     }
  //     // Always normalize indentation, otherwise the message could look weird.
  //     if (StringPrototypeIncludes(message, '\n')) {
  //       if (process.platform === 'win32') {
  //         message = RegExpPrototypeSymbolReplace(/\r\n/g, message, '\n');
  //       }
  //       const frames = StringPrototypeSplit(message, '\n');
  //       message = ArrayPrototypeShift(frames);
  //       for (const frame of frames) {
  //         let pos = 0;
  //         while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) {
  //           pos++;
  //         }
  //         message += `\n  ${StringPrototypeSlice(frame, pos)}`;
  //       }
  //     }
  //     message = `The expression evaluated to a falsy value:\n\n  ${message}\n`;
  //     // Make sure to always set the cache! No matter if the message is
  //     // undefined or not
  //     errorCache.set(identifier, message);
  //     return message;
  //   } catch {
  //     // Invalidate cache to prevent trying to read this part again.
  //     errorCache.set(identifier, undefined);
  //   } finally {
  //     // Reset limit.
  //     if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
  //     if (fd !== undefined)
  //       closeSync(fd);
  //   }
}

export function innerOk(fn, argLen, value, message) {
  if (!value) {
    let generatedMessage = false;

    if (argLen === 0) {
      generatedMessage = true;
      message = "No value argument passed to `assert.ok()`";
    } else if (message == null) {
      generatedMessage = true;
      message = getErrMessage(message, value, fn);
      // TODO: message
    } else if (Error.isError(message)) {
      throw message;
    }

    if (AssertionError === undefined) loadAssertionError();
    const err = new AssertionError({
      actual: value,
      expected: true,
      message,
      operator: "==",
      stackStartFn: fn,
    });
    err.generatedMessage = generatedMessage;
    throw err;
  }
}
