const { EventFactory } = require('../event_factory');

// All three of these users should represent the same user.
const basicUser = { key: 'userkey' };
const basicSingleKindUser = { kind: 'user', key: 'userkey' };
const basicMultiKindUser = { kind: 'multi', user: { key: 'userkey' } };

const eventFactory = EventFactory(false);

// Evaluator.evaluate uses a callback instead of a promise because it's a slightly more efficient
// way to pass multiple return values. But for the purposes of our tests, it's much easier to use
// a promise and async/await, so we'll transform it with this helper. Unlike usual Node promise
// semantics, here we treat "err" as just another return parameter rather than throwing an error
// (because the other parameters can still be non-null even if there's an error).
function asyncEvaluate(evaluator, flag, user, eventFactory) {
  return new Promise(resolve => {
    evaluator.evaluate(flag, user, eventFactory, (err, detail, events) => resolve([err, detail, events]));
  });
}

function makeFlagWithRules(rules, fallthrough) {
  if (!fallthrough) {
    fallthrough = { variation: 0 };
  }
  return {
    key: 'feature',
    on: true,
    rules: rules,
    targets: [],
    fallthrough: fallthrough,
    offVariation: 1,
    variations: ['a', 'b', 'c']
  };
}

function makeBooleanFlagWithRules(rules) {
  return {
    key: 'feature',
    on: true,
    prerequisites: [],
    rules: rules,
    targets: [],
    salt: '',
    fallthrough: { variation: 0 },
    offVariation: 0,
    variations: [false, true],
    version: 1
  };
}

function makeBooleanFlagWithOneClause(clause) {
  return makeBooleanFlagWithRules([{ clauses: [clause], variation: 1 }]);
}

function makeFlagWithSegmentMatch(segment) {
  return makeBooleanFlagWithOneClause(makeSegmentMatchClause(segment));
}

function makeClauseThatMatchesUser(user) {
  return { attribute: 'key', op: 'in', values: [user.key] };
}

function makeClauseThatDoesNotMatchUser(user) {
  return { attribute: 'key', op: 'in', values: ['not-' + user.key] };
}

function makeSegmentMatchClause(segment) {
  return { attribute: '', op: 'segmentMatch', values: [segment.key] };
}

function prepareQueries(data) {
  let flagsMap = {}, segmentsMap = {};
  for (const f of (data.flags || [])) {
    flagsMap[f.key] = f;
  }
  for (const s of (data.segments || [])) {
    segmentsMap[s.key] = s;
  }
  return {
    getFlag: (key, cb) => cb(flagsMap[key]),
    getSegment: (key, cb) => cb(segmentsMap[key]),
    getBigSegmentsMembership: (key, cb) => {
      if (data.bigSegments) {
        cb([data.bigSegments[key], 'HEALTHY']);
      } else {
        cb(null);
      }
    },
  };
}

module.exports = {
  basicUser,
  basicSingleKindUser,
  basicMultiKindUser,
  eventFactory,
  asyncEvaluate,
  makeFlagWithRules,
  makeBooleanFlagWithRules,
  makeBooleanFlagWithOneClause,
  makeFlagWithSegmentMatch,
  makeClauseThatMatchesUser,
  makeClauseThatDoesNotMatchUser,
  makeSegmentMatchClause,
  prepareQueries,
};