'use strict';

import 'should';

/**
 * @typedef SpyCallInfo Information about spy call
 * @property {any} returnValue Call return value
 * @property {any[]} args Call arguments
 */

/**
 * Retrieves information about calls with matched args from a sinon spy
 * @param {Function} spy Sinon stub or spy
 * @param {Array} args Expected call arguments
 * @returns {SpyCallInfo[]} Calls information
 * @throws If failed to retrieve
 */
export function getCallsWithMatch(spy, ...args) {
  return _getCallsWith(spy, 'match', args);
}

/**
 * Retrieves information about calls with deep equal args from a sinon spy
 * @param {Function} spy Sinon stub or spy
 * @param {Array} args Expected call arguments
 * @returns {SpyCallInfo[]} Calls information
 * @throws If failed to retrieve
 */
export function getCallsWithExactly(spy, ...args) {
  return _getCallsWith(spy, 'deepEqual', args);
}

function _getCallsWith(spy, checkMethod, args) {
  if (!spy.isSinonProxy) {
    throw new Error('Given function is not a sinon spy');
  }
  let result = [];
  for (let call = 0; call < spy.args.length; ++call) {
    let actualArgs = spy.args[call];
    try {
      actualArgs.should[checkMethod](args);
      result.push({
        returnValue: spy.returnValues[call],
        args: actualArgs
      });
    } catch (err) {
      if (err.name !== 'AssertionError') {
        throw err;
      }
    }
  }
  return result;
}
