"use strict";

var objectAssign = require("object-assign");
var arrayFrom = require("array-from");

function expect(actual) {
  var expectation = objectAssign(Object.create(expect.expectation), {
    actual: actual,
    assertMode: true,
  });

  expectation.not = objectAssign(Object.create(expectation), {
    assertMode: false,
  });

  return expectation;
}

expect.expectation = Object.create(null);

expect.wrapAssertion = function wrapAssertion(assertion, expectation, referee) {
  expect.expectation[expectation] = function () {
    var args = [this.actual].concat(arrayFrom(arguments));
    var type = this.assertMode ? "assert" : "refute";
    var callFunc;

    if (assertion === "assert") {
      callFunc = this.assertMode ? referee.assert : referee.refute;
    } else if (assertion === "refute") {
      callFunc = this.assertMode ? referee.refute : referee.assert;
    } else {
      callFunc = referee[type][assertion];
    }

    try {
      return callFunc.apply(referee.expect, args);
    } catch (e) {
      e.message = e.message.replace(
        "[" + type + "." + assertion + "]",
        "[expect." + (this.assertMode ? "" : "not.") + expectation + "]"
      );
      throw e;
    }
  };
};

expect.init = function init(referee) {
  Object.keys(referee.assert).forEach(function (name) {
    var expectationName = referee.assert[name].expectationName;
    if (expectationName) {
      expect.wrapAssertion(name, expectationName, referee);
    }
  });

  expect.wrapAssertion("assert", "toBeTruthy", referee);
  expect.wrapAssertion("refute", "toBeFalsy", referee);
};

module.exports = expect;
