/**
 * This module adds LiveIntentId to the User ID module
 * The {@link module:modules/userId} module is required
 * @module modules/liveIntentIdSystem
 * @requires module:modules/userId
 */
import { triggerPixel, logError } from '../src/utils.js';
import { ajaxBuilder } from '../src/ajax.js';
import { submodule } from '../src/hook.js';
import { LiveConnect } from 'live-connect-js'; // eslint-disable-line prebid/validate-imports
import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js';
import {getStorageManager} from '../src/storageManager.js';
import {MODULE_TYPE_UID} from '../src/activities/modules.js';

const EVENTS_TOPIC = 'pre_lips'
const MODULE_NAME = 'liveIntentId';
const LI_PROVIDER_DOMAIN = 'liveintent.com';
export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: MODULE_NAME});
const defaultRequestedAttributes = {'nonId': true}
const calls = {
  ajaxGet: (url, onSuccess, onError, timeout) => {
    ajaxBuilder(timeout)(
      url,
      {
        success: onSuccess,
        error: onError
      },
      undefined,
      {
        method: 'GET',
        withCredentials: true
      }
    )
  },
  pixelGet: (url, onload) => triggerPixel(url, onload)
}

let eventFired = false;
let liveConnect = null;

/**
 * This function is used in tests
 */
export function reset() {
  if (window && window.liQ_instances) {
    window.liQ_instances.forEach(i => i.eventBus.off(EVENTS_TOPIC, setEventFiredFlag))
    window.liQ_instances = [];
  }
  liveIntentIdSubmodule.setModuleMode(null)
  eventFired = false;
  liveConnect = null;
}

/**
 * This function is also used in tests
 */
export function setEventFiredFlag() {
  eventFired = true;
}

function parseLiveIntentCollectorConfig(collectConfig) {
  const config = {};
  collectConfig = collectConfig || {}
  collectConfig.appId && (config.appId = collectConfig.appId);
  collectConfig.fpiStorageStrategy && (config.storageStrategy = collectConfig.fpiStorageStrategy);
  collectConfig.fpiExpirationDays && (config.expirationDays = collectConfig.fpiExpirationDays);
  collectConfig.collectorUrl && (config.collectorUrl = collectConfig.collectorUrl);
  return config;
}

/**
 * Create requestedAttributes array to pass to liveconnect
 * @function
 * @param {Object} overrides - object with boolean values that will override defaults { 'foo': true, 'bar': false }
 * @returns {Array}
 */
function parseRequestedAttributes(overrides) {
  function createParameterArray(config) {
    return Object.entries(config).flatMap(([k, v]) => (typeof v === 'boolean' && v) ? [k] : []);
  }
  if (typeof overrides === 'object') {
    return createParameterArray({...defaultRequestedAttributes, ...overrides})
  } else {
    return createParameterArray(defaultRequestedAttributes);
  }
}

function initializeLiveConnect(configParams) {
  configParams = configParams || {};
  if (liveConnect) {
    return liveConnect;
  }

  const publisherId = configParams.publisherId || 'any';
  const identityResolutionConfig = {
    publisherId: publisherId,
    requestedAttributes: parseRequestedAttributes(configParams.requestedAttributesOverrides)
  };
  if (configParams.url) {
    identityResolutionConfig.url = configParams.url
  }
  if (configParams.ajaxTimeout) {
    identityResolutionConfig.ajaxTimeout = configParams.ajaxTimeout;
  }

  const liveConnectConfig = parseLiveIntentCollectorConfig(configParams.liCollectConfig);

  if (!liveConnectConfig.appId && configParams.distributorId) {
    liveConnectConfig.distributorId = configParams.distributorId;
    identityResolutionConfig.source = configParams.distributorId;
  } else {
    identityResolutionConfig.source = configParams.partner || 'prebid'
  }

  liveConnectConfig.wrapperName = 'prebid';
  liveConnectConfig.identityResolutionConfig = identityResolutionConfig;
  liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || [];
  liveConnectConfig.fireEventDelay = configParams.fireEventDelay;
  const usPrivacyString = uspDataHandler.getConsentData();
  if (usPrivacyString) {
    liveConnectConfig.usPrivacyString = usPrivacyString;
  }
  const gdprConsent = gdprDataHandler.getConsentData()
  if (gdprConsent) {
    liveConnectConfig.gdprApplies = gdprConsent.gdprApplies;
    liveConnectConfig.gdprConsent = gdprConsent.consentString;
  }

  // The second param is the storage object, LS & Cookie manipulation uses PBJS
  // The third param is the ajax and pixel object, the ajax and pixel use PBJS
  liveConnect = liveIntentIdSubmodule.getInitializer()(liveConnectConfig, storage, calls);
  if (configParams.emailHash) {
    liveConnect.push({ hash: configParams.emailHash })
  }
  return liveConnect;
}

function tryFireEvent() {
  if (!eventFired && liveConnect) {
    const eventDelay = liveConnect.config.fireEventDelay || 500
    setTimeout(() => {
      const instances = window.liQ_instances
      instances.forEach(i => i.eventBus.once(EVENTS_TOPIC, setEventFiredFlag))
      if (!eventFired && liveConnect) {
        liveConnect.fire();
      }
    }, eventDelay)
  }
}

/** @type {Submodule} */
export const liveIntentIdSubmodule = {
  moduleMode: process.env.LiveConnectMode,
  /**
   * used to link submodule with config
   * @type {string}
   */
  name: MODULE_NAME,

  setModuleMode(mode) {
    this.moduleMode = mode
  },
  getInitializer() {
    return (liveConnectConfig, storage, calls) => LiveConnect(liveConnectConfig, storage, calls, this.moduleMode)
  },

  /**
   * decode the stored id value for passing to bid requests. Note that lipb object is a wrapper for everything, and
   * internally it could contain more data other than `lipbid`(e.g. `segments`) depending on the `partner` and
   * `publisherId` params.
   * @function
   * @param {{unifiedId:string}} value
   * @param {SubmoduleConfig|undefined} config
   * @returns {{lipb:Object}}
   */
  decode(value, config) {
    const configParams = (config && config.params) || {};
    function composeIdObject(value) {
      const result = {};

      // old versions stored lipbid in unifiedId. Ensure that we can still read the data.
      const lipbid = value.nonId || value.unifiedId
      if (lipbid) {
        value.lipbid = lipbid
        delete value.unifiedId
        result.lipb = value
      }

      // Lift usage of uid2 by exposing uid2 if we were asked to resolve it.
      // As adapters are applied in lexicographical order, we will always
      // be overwritten by the 'proper' uid2 module if it is present.
      if (value.uid2) {
        result.uid2 = { 'id': value.uid2, ext: { provider: LI_PROVIDER_DOMAIN } }
      }

      if (value.bidswitch) {
        result.bidswitch = { 'id': value.bidswitch, ext: { provider: LI_PROVIDER_DOMAIN } }
      }

      if (value.medianet) {
        result.medianet = { 'id': value.medianet, ext: { provider: LI_PROVIDER_DOMAIN } }
      }

      if (value.magnite) {
        result.magnite = { 'id': value.magnite, ext: { provider: LI_PROVIDER_DOMAIN } }
      }

      if (value.index) {
        result.index = { 'id': value.index, ext: { provider: LI_PROVIDER_DOMAIN } }
      }

      return result
    }

    if (!liveConnect) {
      initializeLiveConnect(configParams);
    }
    tryFireEvent();

    return composeIdObject(value);
  },

  /**
   * performs action to obtain id and return a value in the callback's response argument
   * @function
   * @param {SubmoduleConfig} [config]
   * @returns {IdResponse|undefined}
   */
  getId(config) {
    const configParams = (config && config.params) || {};
    const liveConnect = initializeLiveConnect(configParams);
    if (!liveConnect) {
      return;
    }
    tryFireEvent();
    const result = function(callback) {
      liveConnect.resolve(
        response => {
          callback(response);
        },
        error => {
          logError(`${MODULE_NAME}: ID fetch encountered an error: `, error);
          callback();
        }
      )
    }

    return { callback: result };
  },
  eids: {
    'lipb': {
      getValue: function(data) {
        return data.lipbid;
      },
      source: 'liveintent.com',
      atype: 3,
      getEidExt: function(data) {
        if (Array.isArray(data.segments) && data.segments.length) {
          return {
            segments: data.segments
          };
        }
      }
    },
    'bidswitch': {
      source: 'bidswitch.net',
      atype: 3,
      getValue: function(data) {
        return data.id;
      },
      getUidExt: function(data) {
        if (data.ext) {
          return data.ext;
        }
      }
    },
    'medianet': {
      source: 'media.net',
      atype: 3,
      getValue: function(data) {
        return data.id;
      },
      getUidExt: function(data) {
        if (data.ext) {
          return data.ext;
        }
      }
    },
    'magnite': {
      source: 'rubiconproject.com',
      atype: 3,
      getValue: function(data) {
        return data.id;
      },
      getUidExt: function(data) {
        if (data.ext) {
          return data.ext;
        }
      }
    },
    'index': {
      source: 'liveintent.indexexchange.com',
      atype: 3,
      getValue: function(data) {
        return data.id;
      },
      getUidExt: function(data) {
        if (data.ext) {
          return data.ext;
        }
      }
    }
  }
};

submodule('userId', liveIntentIdSubmodule);
