'use strict';

/*
 * Module dependencies.
 */

import { InitOptions } from './types';
var Entity = require('./entity');
var bindAll = require('bind-all');
var cookie = require('./cookie');
var debug = require('debug')('analytics:user');
var inherit = require('inherits');
var rawCookie = require('@segment/cookie');
var uuid = require('uuid');
var localStorage = require('./store');

/**
 * User defaults
 */

interface UserDefaults {
  persist: boolean;
  cookie: {
    key: string;
    oldKey: string;
  };
  localStorage: {
    key: string;
  };
}

interface User {
  defaults: UserDefaults;
  debug: unknown;
}

User.defaults = {
  persist: true,
  cookie: {
    key: 'ajs_user_id',
    oldKey: 'ajs_user'
  },
  localStorage: {
    key: 'ajs_user_traits'
  }
};

/**
 * Initialize a new `User` with `options`.
 */

function User(options?: InitOptions) {
  this.defaults = User.defaults;
  this.debug = debug;
  Entity.call(this, options);
}

/**
 * Inherit `Entity`
 */

inherit(User, Entity);

/**
 * Set/get the user id.
 *
 * When the user id changes, the method will reset his anonymousId to a new one.
 *
 * @example
 * // didn't change because the user didn't have previous id.
 * anonymousId = user.anonymousId();
 * user.id('foo');
 * assert.equal(anonymousId, user.anonymousId());
 *
 * // didn't change because the user id changed to null.
 * anonymousId = user.anonymousId();
 * user.id('foo');
 * user.id(null);
 * assert.equal(anonymousId, user.anonymousId());
 *
 * // change because the user had previous id.
 * anonymousId = user.anonymousId();
 * user.id('foo');
 * user.id('baz'); // triggers change
 * user.id('baz'); // no change
 * assert.notEqual(anonymousId, user.anonymousId());
 */

User.prototype.id = function(id?: string): string | undefined {
  var prev = this._getId();
  var ret = Entity.prototype.id.apply(this, arguments);
  if (prev == null) return ret;
  // FIXME: We're relying on coercion here (1 == "1"), but our API treats these
  // two values differently. Figure out what will break if we remove this and
  // change to strict equality
  /* eslint-disable eqeqeq */
  if (prev != id && id) this.anonymousId(null);
  /* eslint-enable eqeqeq */
  return ret;
};

/**
 * Set / get / remove anonymousId.
 *
 * @param {String} anonymousId
 * @return {String|User}
 */

User.prototype.anonymousId = function(anonymousId?: string): string | User {
  var store = this.storage();

  // set / remove
  if (arguments.length) {
    store.set('ajs_anonymous_id', anonymousId);
    this._setAnonymousIdInLocalStorage(anonymousId);
    return this;
  }

  // new
  anonymousId = store.get('ajs_anonymous_id');
  if (anonymousId) {
    // value exists in cookie, copy it to localStorage
    this._setAnonymousIdInLocalStorage(anonymousId);
    // refresh cookie to extend expiry
    store.set('ajs_anonymous_id', anonymousId);
    return anonymousId;
  }

  if (!this._options.localStorageFallbackDisabled) {
    // if anonymousId doesn't exist in cookies, check localStorage
    anonymousId = localStorage.get('ajs_anonymous_id');
    if (anonymousId) {
      // Write to cookies if available in localStorage but not cookies
      store.set('ajs_anonymous_id', anonymousId);
      return anonymousId;
    }
  }

  // old - it is not stringified so we use the raw cookie.
  anonymousId = rawCookie('_sio');
  if (anonymousId) {
    anonymousId = anonymousId.split('----')[0];
    store.set('ajs_anonymous_id', anonymousId);
    this._setAnonymousIdInLocalStorage(anonymousId);
    store.remove('_sio');
    return anonymousId;
  }

  // empty
  anonymousId = uuid.v4();
  store.set('ajs_anonymous_id', anonymousId);
  this._setAnonymousIdInLocalStorage(anonymousId);
  return store.get('ajs_anonymous_id');
};

/**
 * Set the user's `anonymousid` in local storage.
 */

User.prototype._setAnonymousIdInLocalStorage = function(id: string) {
  if (!this._options.localStorageFallbackDisabled) {
    localStorage.set('ajs_anonymous_id', id);
  }
};

/**
 * Remove anonymous id on logout too.
 */

User.prototype.logout = function() {
  Entity.prototype.logout.call(this);
  this.anonymousId(null);
};

/**
 * Load saved user `id` or `traits` from storage.
 */

User.prototype.load = function() {
  if (this._loadOldCookie()) return;
  Entity.prototype.load.call(this);
};

/**
 * BACKWARDS COMPATIBILITY: Load the old user from the cookie.
 *
 * @api private
 */

User.prototype._loadOldCookie = function(): boolean {
  var user = cookie.get(this._options.cookie.oldKey);
  if (!user) return false;

  this.id(user.id);
  this.traits(user.traits);
  cookie.remove(this._options.cookie.oldKey);
  return true;
};

/**
 * Expose the user singleton.
 */

module.exports = bindAll(new User());

/**
 * Expose the `User` constructor.
 */

module.exports.User = User;
