SessionData = new Mongo.Collection('sessiondata');

/**
 * A UserSessionData in Wekan. Organization in Trello.
 */
SessionData.attachSchema(
  new SimpleSchema({
    _id: {
      /**
       * the organization id
       */
      type: Number,
      optional: true,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert && !this.isSet) {
          return incrementCounter('counters', 'orgId', 1);
        }
      },
    },
    userId: {
      /**
       * userId of the user
       */
      type: String,
      optional: false,
    },
    sessionId: {
      /**
       * unique session ID
       */
      type: String,
      optional: false,
    },
    totalHits: {
      /**
       * total number of hits in the last report query
       */
      type: Number,
      optional: true,
    },
    resultsCount: {
      /**
       * number of results returned
       */
      type: Number,
      optional: true,
    },
    lastHit: {
      /**
       * the last hit returned from a report query
       */
      type: Number,
      optional: true,
    },
    cards: {
      type: [String],
      optional: true,
    },
    selector: {
      type: String,
      optional: true,
      blackbox: true,
    },
    projection: {
      type: String,
      optional: true,
      blackbox: true,
      defaultValue: {},
    },
    errorMessages: {
      type: [String],
      optional: true,
    },
    errors: {
      type: [Object],
      optional: true,
      defaultValue: [],
    },
    debug: {
      type: String,
      optional: true,
    },
    'errors.$': {
      type: new SimpleSchema({
        tag: {
          /**
           * i18n tag
           */
          type: String,
          optional: false,
        },
        value: {
          /**
           * value for the tag
           */
          type: String,
          optional: true,
          defaultValue: null,
        },
        color: {
          type: Boolean,
          optional: true,
          defaultValue: false,
        },
      }),
    },
    createdAt: {
      /**
       * creation date of the team
       */
      type: Date,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert) {
          return new Date();
        } else if (this.isUpsert) {
          return { $setOnInsert: new Date() };
        } else {
          this.unset();
        }
      },
    },
    modifiedAt: {
      type: Date,
      denyUpdate: false,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert || this.isUpsert || this.isUpdate) {
          return new Date();
        } else {
          this.unset();
        }
      },
    },
  }),
);

SessionData.helpers({
  getSelector() {
    return SessionData.unpickle(this.selector);
  },
  getProjection() {
    return SessionData.unpickle(this.projection);
  },
});

SessionData.unpickle = pickle => {
  return JSON.parse(pickle, (key, value) => {
    return unpickleValue(value);
  });
};

function unpickleValue(value) {
  if (value === null) {
    return null;
  } else if (typeof value === 'object') {
    // eslint-disable-next-line no-prototype-builtins
    if (value.hasOwnProperty('$$class')) {
      switch (value.$$class) {
        case 'RegExp':
          return new RegExp(value.source, value.flags);
        case 'Date':
          return new Date(value.stringValue);
        case 'Object':
          return unpickleObject(value);
      }
    }
  }
  return value;
}

function unpickleObject(obj) {
  const newObject = {};
  Object.entries(obj).forEach(([key, value]) => {
    newObject[key] = unpickleValue(value);
  });
  return newObject;
}

SessionData.pickle = value => {
  return JSON.stringify(value, (key, value) => {
    return pickleValue(value);
  }, 2);
};

function pickleValue(value) {
  if (value === null) {
    return null;
  } else if (typeof value === 'object') {
    switch (value.constructor.name) {
      case 'RegExp':
        return {
          $$class: 'RegExp',
          source: value.source,
          flags: value.flags,
        };
      case 'Date':
        return {
          $$class: 'Date',
          stringValue: String(value),
        };
      case 'Object':
        return pickleObject(value);
    }
  }
  return value;
}

function pickleObject(obj) {
  const newObject = {};
  Object.entries(obj).forEach(([key, value]) => {
    newObject[key] = pickleValue(value);
  });
  return newObject;
}

if (!Meteor.isServer) {
  SessionData.getSessionId = () => {
    let sessionId = Session.get('sessionId');
    if (!sessionId) {
      sessionId = `${String(Meteor.userId())}-${String(Math.random())}`;
      Session.set('sessionId', sessionId);
    }

    return sessionId;
  };
}

export default SessionData;
