import chai from 'chai';
import '@babel/polyfill';
import __toString from 'lodash/toString';
import { format as __format } from '../../utils/format';

const __utils = (() => {
  const MAX_LOGS_SIZE = 64 * 1024;

  let logs = [];
  function flushLogs() {
    if (logs.length) {
      self.postMessage({
        type: 'LOG',
        data: logs.join('\n')
      });
      logs = [];
    }
  }

  const oldLog = self.console.log.bind(self.console);
  function proxyLog(...args) {
    logs.push(args.map(arg => __format(arg)).join(' '));
    if (logs.join('\n').length > MAX_LOGS_SIZE) {
      flushLogs();
    }
    return oldLog(...args);
  }

  // unless data.type is truthy, this sends data out to the testRunner
  function postResult(data) {
    flushLogs();
    self.postMessage(data);
  }

  function log(msg) {
    if (!(msg instanceof chai.AssertionError)) {
      // discards the stack trace via toString as it only useful to debug the
      // site, not a specific challenge.
      console.log(msg.toString());
    }
  }

  const toggleProxyLogger = on => {
    self.console.log = on ? proxyLog : oldLog;
  };

  return {
    postResult,
    log,
    toggleProxyLogger,
    flushLogs
  };
})();

/* Run the test if there is one.  If not just evaluate the user code */
self.onmessage = async e => {
  /* eslint-disable no-unused-vars */
  const { code = '' } = e.data;
  const assert = chai.assert;
  // Fake Deep Equal dependency
  const DeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);

  // Build errors should be reported, but only once:
  __utils.toggleProxyLogger(e.data.firstTest);
  /* eslint-enable no-unused-vars */
  try {
    let testResult;
    let __userCodeWasExecuted = false;
    /* eslint-disable no-eval */
    try {
      // Logging is proxyed after the build to catch console.log messages
      // generated during testing.
      testResult = eval(`${e.data.build}
__utils.flushLogs();
__userCodeWasExecuted = true;
__utils.toggleProxyLogger(true);
${e.data.testString}`);
    } catch (err) {
      if (__userCodeWasExecuted) {
        // rethrow error, since test failed.
        throw err;
      }
      // log build errors
      __utils.log(err);
      // the tests may not require working code, so they are evaluated even if
      // the user code does not get executed.
      testResult = eval(e.data.testString);
    }
    /* eslint-enable no-eval */
    if (typeof testResult === 'function') {
      await testResult(fileName => __toString(e.data.sources[fileName]));
    }
    __utils.postResult({
      pass: true
    });
  } catch (err) {
    // Errors from testing go to the browser console only.
    __utils.toggleProxyLogger(false);
    // Report execution errors in case user code has errors that are only
    // uncovered during testing.
    __utils.log(err);
    // postResult flushes the logs and must be called after logging is finished.
    __utils.postResult({
      err: {
        message: err.message,
        stack: err.stack
      }
    });
  }
};

self.postMessage({ type: 'contentLoaded' });

@@ -800,19 +800,23 @@
 ion log(
+...
 msg
+s
 ) %7B%0A    
@@ -823,13 +823,36 @@
 if (
+msgs && msgs%5B0%5D && 
 !(msg
+s%5B0%5D
  ins
@@ -1020,16 +1020,35 @@
 ole.log(
+...msgs.map(msg =%3E 
 msg.toSt
@@ -1054,16 +1054,17 @@
 tring())
+)
 ;%0A    %7D%0A
@@ -2205,17 +2205,317 @@
 d errors
-%0A
+ unless they're related to import/export/require (there%0A      // are challenges that use them and they should not trigger warnings)%0A      if (%0A        err.name !== 'ReferenceError' %7C%7C%0A        (err.message !== 'require is not defined' &&%0A          err.message !== 'exports is not defined')%0A      ) %7B%0A  
       __
@@ -2526,24 +2526,32 @@
 s.log(err);%0A
+      %7D%0A
       // the

improve challenge logs (#39395)