"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("./wrap/lodash");
const callback_1 = require("./callback");
const is_callback_1 = require("./matchers/is-callback");
const calls_1 = require("./store/calls");
const log_1 = require("./log");
const stubbings_1 = require("./store/stubbings");
const config_1 = require("./config");
const clone_deep_if_possible_1 = require("./clone-deep-if-possible");
const symbols_1 = require("./symbols");
function when(__userDoesRehearsalInvocationHere__, config = {}) {
    return ({
        thenReturn(...stubbedValues) {
            return addStubbing(stubbedValues, config, 'thenReturn');
        },
        thenCallback(...stubbedValues) {
            return addStubbing(stubbedValues, config, 'thenCallback');
        },
        thenDo(...stubbedValues) {
            return addStubbing(stubbedValues, config, 'thenDo');
        },
        thenThrow(...stubbedValues) {
            return addStubbing(stubbedValues, config, 'thenThrow');
        },
        thenResolve(...stubbedValues) {
            warnIfPromiseless();
            return addStubbing(stubbedValues, config, 'thenResolve');
        },
        thenReject(...stubbedValues) {
            warnIfPromiseless();
            return addStubbing(stubbedValues, config, 'thenReject');
        }
    });
}
exports.default = when;
function addStubbing(stubbedValues, config, plan) {
    const last = calls_1.default.pop();
    ensureRehearsalOccurred(last);
    ensureCloneableIfCloneArgs(last, config);
    lodash_1.default.assign(config, { plan });
    stubbings_1.default.add(last.testDouble, concatImpliedCallback(last.args, config), stubbedValues, config);
    return last.testDouble;
}
function ensureRehearsalOccurred(last) {
    if (!last) {
        return log_1.default.error('td.when', `\
No test double invocation call detected for \`when()\`.

  Usage:
    when(myTestDouble('foo')).thenReturn('bar')\
`);
    }
}
function ensureCloneableIfCloneArgs(last, config) {
    if (config.cloneArgs && clone_deep_if_possible_1.default(last.args) === symbols_1.default.uncloneable) {
        return log_1.default.error('td.when', `\
Failed to deep-clone arguments. Ensure lodash _.cloneDeep works on them
`);
    }
}
function concatImpliedCallback(args, config) {
    if (config.plan !== 'thenCallback') {
        return args;
    }
    else if (!lodash_1.default.some(args, is_callback_1.default)) {
        return args.concat(callback_1.default);
    }
    else {
        return args;
    }
}
function warnIfPromiseless() {
    if (config_1.default().promiseConstructor == null) {
        log_1.default.warn('td.when', `\
no promise constructor is set, so this \`thenResolve\` or \`thenReject\` stubbing
will fail if it's satisfied by an invocation on the test double. You can tell
testdouble.js which promise constructor to use with \`td.config\`, like so:

  td.config({
    promiseConstructor: require('bluebird')
  })\
`);
    }
}
