import SplitIO from '../../types/splitio';
import { IRBSegmentsCacheSync, ISegmentsCacheSync, IDefinitionsCacheSync } from './types';
import { ILogger } from '../logger/types';
import { isObject } from '../utils/lang';
import { isConsumerMode } from '../utils/settingsValidation/mode';
import { RolloutPlan } from './types';

/**
 * Validates if the given rollout plan is valid.
 */
export function validateRolloutPlan(log: ILogger, settings: SplitIO.ISettings): RolloutPlan | undefined {
  const { mode, initialRolloutPlan } = settings;

  if (isConsumerMode(mode)) {
    log.warn('storage: initial rollout plan is ignored in consumer mode');
    return;
  }

  if (isObject(initialRolloutPlan) && isObject((initialRolloutPlan as any).splitChanges)) return initialRolloutPlan as RolloutPlan;

  log.error('storage: invalid rollout plan provided');
  return;
}

/**
 * Sets the given synchronous storage with the provided rollout plan snapshot.
 * If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
 * Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
 */
export function setRolloutPlan(log: ILogger, rolloutPlan: RolloutPlan, storage: { definitions?: IDefinitionsCacheSync, rbSegments?: IRBSegmentsCacheSync, segments: ISegmentsCacheSync, largeSegments?: ISegmentsCacheSync }, matchingKey?: string) {
  const { definitions, rbSegments, segments, largeSegments } = storage;
  const { splitChanges: { ff, rbs } } = rolloutPlan;

  log.debug(`storage: set feature flags and segments${matchingKey ? ` for key ${matchingKey}` : ''}`);

  if (definitions && ff) {
    definitions.clear();
    definitions.update(ff.d || [], [], ff.t);
  }

  if (rbSegments && rbs) {
    rbSegments.clear();
    rbSegments.update(rbs.d || [], [], rbs.t);
  }

  const segmentChanges = rolloutPlan.segmentChanges;
  if (matchingKey) { // add memberships data (client-side)
    let memberships = rolloutPlan.memberships && rolloutPlan.memberships[matchingKey];
    if (!memberships && segmentChanges) {
      memberships = {
        ms: {
          k: segmentChanges.filter(segment => {
            return segment.added.indexOf(matchingKey) > -1;
          }).map(segment => ({ n: segment.name }))
        }
      };
    }

    if (memberships) {
      if (memberships.ms) segments.resetSegments(memberships.ms!);
      if (memberships.ls && largeSegments) largeSegments.resetSegments(memberships.ls!);
    }
  } else { // add segments data (server-side)
    if (segmentChanges) {
      segments.clear();
      segmentChanges.forEach(segment => {
        segments.update(segment.name, segment.added, segment.removed, segment.till);
      });
    }
  }
}
