import adapter from '../src/AnalyticsAdapter';
import CONSTANTS from '../src/constants.json';
import adapterManager from '../src/adapterManager';
import {ajaxBuilder} from '../src/ajax';

const utils = require('../src/utils');
let ajax = ajaxBuilder(0);

const DEFAULT_EVENT_URL = 'apex.go.sonobi.com/keymaker';
const analyticsType = 'endpoint';
const QUEUE_TIMEOUT_DEFAULT = 200;
const {
  EVENTS: {
    AUCTION_INIT,
    AUCTION_END,
    BID_REQUESTED,
    BID_ADJUSTMENT,
    BIDDER_DONE,
    BID_WON,
    BID_RESPONSE,
    BID_TIMEOUT
  }
} = CONSTANTS;

let initOptions = {};
let auctionCache = {};
let auctionTtl = 60 * 60 * 1000;

function deleteOldAuctions() {
  for (let auctionId in auctionCache) {
    let auction = auctionCache[auctionId];
    if (Date.now() - auction.start > auctionTtl) {
      delete auctionCache[auctionId];
    }
  }
}

function buildAuctionEntity(args) {
  return {
    'id': args.auctionId,
    'start': args.timestamp,
    'timeout': args.timeout,
    'adUnits': {},
    'stats': {},
    'queue': [],
    'qTimeout': false
  };
}
function buildAdUnit(data) {
  return `/${initOptions.pubId}/${initOptions.siteId}/${data.adUnitCode.toLowerCase()}`;
}
function getLatency(data) {
  if (!data.responseTimestamp) {
    return -1;
  } else {
    return data.responseTimestamp - data.requestTimestamp;
  }
}
function getBid(data) {
  if (data.cpm) {
    return Math.round(data.cpm * 100);
  } else {
    return 0;
  }
}
function buildItem(data, response, phase = 1) {
  let size = data.width ? {width: data.width, height: data.height} : {width: data.sizes[0][0], height: data.sizes[0][1]};
  return {
    'bidid': data.bidId || data.requestId,
    'p': phase,
    'buyerid': data.bidder.toLowerCase(),
    'bid': getBid(data),
    'adunit_code': buildAdUnit(data),
    's': `${size.width}x${size.height}`,
    'latency': getLatency(data),
    'response': response,
    'jsLatency': getLatency(data),
    'buyername': data.bidder.toLowerCase()
  };
}
function sendQueue(auctionId) {
  let auction = auctionCache[auctionId];
  let data = auction.queue;
  auction.queue = [];
  auction.qTimeout = false;
  sonobiAdapter.sendData(auction, data);
}
function addToAuctionQueue(auctionId, id) {
  let auction = auctionCache[auctionId];
  auction.queue = auction.queue.filter((item) => {
    if (item.bidid !== id) { return true; }
    return auction.stats[id].data.p !== item.p;
  });
  auction.queue.push(utils.deepClone(auction.stats[id].data));
  if (!auction.qTimeout) {
    auction.qTimeout = setTimeout(() => {
      sendQueue(auctionId);
    }, initOptions.delay)
  }
}
function updateBidStats(auctionId, id, data) {
  let auction = auctionCache[auctionId];
  auction.stats[id].data = {...auction.stats[id].data, ...data};
  addToAuctionQueue(auctionId, id);
  logInfo('Updated Bid Stats: ', auction.stats[id]);
  return auction.stats[id];
}

function handleOtherEvents(eventType, args) {
  logInfo('Other Event: ' + eventType, args);
}

function handlerAuctionInit(args) {
  auctionCache[args.auctionId] = buildAuctionEntity(args);
  deleteOldAuctions();
  logInfo('Auction Init', args);
}
function handlerBidRequested(args) {
  let auction = auctionCache[args.auctionId];
  let data = [];
  let phase = 1;
  let response = 1;
  args.bids.forEach(function (bidRequest) {
    auction = auctionCache[bidRequest.auctionId]
    let built = buildItem(bidRequest, response, phase);
    auction.stats[built.bidid] = {id: built.bidid, adUnitCode: bidRequest.adUnitCode, data: built};
    addToAuctionQueue(args.auctionId, built.bidid);
  })

  logInfo('Bids Requested ', data);
}

function handlerBidAdjustment(args) {
  logInfo('Bid Adjustment', args);
}
function handlerBidderDone(args) {
  logInfo('Bidder Done', args);
}

function handlerAuctionEnd(args) {
  let winners = {};
  args.bidsReceived.forEach((bid) => {
    if (!winners[bid.adUnitCode]) {
      winners[bid.adUnitCode] = {bidId: bid.requestId, cpm: bid.cpm};
    } else if (winners[bid.adUnitCode].cpm < bid.cpm) {
      winners[bid.adUnitCode] = {bidId: bid.requestId, cpm: bid.cpm};
    }
  })
  args.adUnitCodes.forEach((adUnitCode) => {
    if (winners[adUnitCode]) {
      let bidId = winners[adUnitCode].bidId;
      updateBidStats(args.auctionId, bidId, {response: 4});
    }
  })
  logInfo('Auction End', args);
  logInfo('Auction Cache', auctionCache[args.auctionId].stats);
}
function handlerBidWon(args) {
  let {auctionId, requestId} = args;
  let res = updateBidStats(auctionId, requestId, {p: 3, response: 6});
  logInfo('Bid Won ', args);
  logInfo('Bid Update Result: ', res);
}
function handlerBidResponse(args) {
  let {auctionId, requestId, cpm, size, timeToRespond} = args;
  updateBidStats(auctionId, requestId, {bid: cpm, s: size, jsLatency: timeToRespond, latency: timeToRespond, p: 2, response: 9});

  logInfo('Bid Response ', args);
}
function handlerBidTimeout(args) {
  let {auctionId, bidId} = args;
  logInfo('Bid Timeout ', args);
  updateBidStats(auctionId, bidId, {p: 2, response: 0, latency: args.timeout, jsLatency: args.timeout});
}
let sonobiAdapter = Object.assign(adapter({url: DEFAULT_EVENT_URL, analyticsType}), {
  track({eventType, args}) {
    switch (eventType) {
      case AUCTION_INIT:
        handlerAuctionInit(args);
        break;
      case BID_REQUESTED:
        handlerBidRequested(args);
        break;
      case BID_ADJUSTMENT:
        handlerBidAdjustment(args);
        break;
      case BIDDER_DONE:
        handlerBidderDone(args);
        break;
      case AUCTION_END:
        handlerAuctionEnd(args);
        break;
      case BID_WON:
        handlerBidWon(args);
        break;
      case BID_RESPONSE:
        handlerBidResponse(args);
        break;
      case BID_TIMEOUT:
        handlerBidTimeout(args);
        break;
      default:
        handleOtherEvents(eventType, args);
        break;
    }
  },

});

sonobiAdapter.originEnableAnalytics = sonobiAdapter.enableAnalytics;

sonobiAdapter.enableAnalytics = function (config) {
  if (this.initConfig(config)) {
    logInfo('Analytics adapter enabled', initOptions);
    sonobiAdapter.originEnableAnalytics(config);
  }
};

sonobiAdapter.initConfig = function (config) {
  let isCorrectConfig = true;
  initOptions = {};
  initOptions.options = utils.deepClone(config.options);

  initOptions.pubId = initOptions.options.pubId || null;
  initOptions.siteId = initOptions.options.siteId || null;
  initOptions.delay = initOptions.options.delay || QUEUE_TIMEOUT_DEFAULT;
  if (!initOptions.pubId) {
    logError('"options.pubId" is empty');
    isCorrectConfig = false;
  }
  if (!initOptions.siteId) {
    logError('"options.siteId" is empty');
    isCorrectConfig = false;
  }

  initOptions.server = DEFAULT_EVENT_URL;
  initOptions.host = initOptions.options.host || window.location.hostname;
  this.initOptions = initOptions;
  return isCorrectConfig;
};

sonobiAdapter.getOptions = function () {
  return initOptions;
};

sonobiAdapter.sendData = function (auction, data) {
  let url = 'https://' + initOptions.server + '?pageviewid=' + auction.id + '&corscred=1&pubId=' + initOptions.pubId + '&siteId=' + initOptions.siteId;
  ajax(
    url,
    function () { logInfo('Auction [' + auction.id + '] sent ', data); },
    JSON.stringify(data),
    {
      method: 'POST',
      // withCredentials: true,
      contentType: 'text/plain'
    }
  );
}

function logInfo(message, meta) {
  utils.logInfo(buildLogMessage(message), meta);
}

function logError(message) {
  utils.logError(buildLogMessage(message));
}

function buildLogMessage(message) {
  return 'Sonobi Prebid Analytics: ' + message;
}

adapterManager.registerAnalyticsAdapter({
  adapter: sonobiAdapter,
  code: 'sonobi'
});

export default sonobiAdapter;
