/**
 * Utility file for Calendars
 */
import React from 'react';
import {numberShorthand, decapitalize } from './formatters';
import isNumber from 'lodash/isNumber';
import round from 'lodash/round';
import Ticker from '../components/ui/Ticker';
import compact from 'lodash/compact';
import forEach from 'lodash/forEach';
import includes from 'lodash/includes';
import { SortTypes } from '../components/widgets/calendar/CalendarTable.jsx';

export function surprisePercent(est, actual) {
  return (est && actual) ? 100 * ((actual - est) / Math.abs(est)) : '\u00a0';
}

export function marketString(eventTime) {
  const preMarket = '09:30:00';
  const afterMarket = '15:59:59';

  if (eventTime < preMarket) {
    return 'Pre-Market';
  } else if (eventTime > afterMarket) {
    return 'After-Market';
  }
  return 'Intraday';
}

export function boolToString(data) {
  if (data === true || data === 'Y' || data === 1) {
    return 'Y';
  }
  return 'N';
}

function computePercent(data) {
  return ((isNumber(data) && data !== 0) ? round(data * 100, 2) + '%' : '');
}

function roundPercent(num) {
  const rounded = Math.round(num * 100) / 100;
  if (rounded) {
    return rounded.toLocaleString() + '%';
  }
  return '';
}

/**
 * isDefault: Controls whether the field is shown by default when making a new
 *  calendar of the type.
 * display: Controls whether the field can be displayed on the calendar.
 * computeValue: Used when a field needs to be computed and is not given by the query
 *   function(event: Object) => any
 */
const definition = {
  economics: {
    name: 'Economics',
    sort: {
      date: SortTypes.DESC,
      time: SortTypes.DESC,
    },
    fields: {
      date: {
        listName: 'Date',
        listNameFull: 'Call Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      time: {
        listName: 'Time',
        listNameFull: 'Time',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      country: {
        listName: 'Country',
        listNameFull: 'Country',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      event_name: {
        listName: 'Event',
        listNameFull: 'Economic Event Name',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      event_period: {
        listName: 'Event Period',
        listNameFull: 'Event Period',
        display: true
      },
      period_year: {
        listName: 'Year',
        listNameFull: 'Period Year',
        display: true
      },
      actual: {
        listName: 'Actual',
        listNameFull: 'Actual',
        isDefault: true,
        display: true,
        transform: 'numberShorten'
      },
      consensus: {
        listName: 'Consensus',
        listNameFull: 'Consensus',
        isDefault: true,
        display: true,
        transform: 'numberShorten'
      },
      prior: {
        listName: 'Prior',
        listNameFull: 'Prior',
        isDefault: true,
        display: true,
        transform: 'numberShorten'
      },
      importance: {
        listName: 'Importance',
        listNameFull: 'Importance',
        isDefault: true,
        display: true
      },
      // description: {
      //   listName: 'Description',
      //   listNameFull: 'Event Description',
      //   display: false
      // },
      period: {
        listName: 'Period',
        listNameFull: 'Period',
        display: false
      },
    }
  },
  conference: {
    name: 'Conference Calls',
    sort: {
      date: SortTypes.DESC,
      name: SortTypes.ASC,
    },
    fields: {
      date: {
        type: 'date',
        listName: 'Date',
        listNameFull: 'Call Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      start_time: {
        type: 'string',
        listName: 'Time',
        listNameFull: 'Start Time',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      ticker: {
        type: 'string',
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        type: 'string',
        listName: 'Name',
        listNameFull: 'Name',
        isDefault: true,
        display: true
      },
      webcast_url: {
        type: 'numeric',
        transform: 'link',
        listName: 'Webcast Link',
        listNameFull: 'Webcast',
        isDefault: true,
        display: true
      },
      phone_num: {
        type: 'numeric',
        listName: 'Phone',
        listNameFull: 'Phone Number',
        isDefault: true,
        display: true
      },
      international_num: {
        type: 'numeric',
        listName: 'Int. Num.',
        listNameFull: 'International Number',
        isDefault: true,
        display: true
      },
      access_code: {
        type: 'numeric',
        listName: 'Access Code',
        listNameFull: 'Access Code',
        isDefault: true,
        display: true
      },
      // reservation_num: {
      //   type: 'string',
      //   listName: 'Res Num',
      //   listNameFull: 'Reservation Number',
      //   display: false
      // },
      importance: {
        type: 'int',
        listName: 'Importance',
        listNameFull: 'Importance',
        display: false
      },
    }
  },
  dividends: {
    name: 'Dividends',
    sort: {
      ex_dividend_date: SortTypes.DESC,
    },
    fields: {
      ex_dividend_date: {
        type: 'numeric',
        listName: 'ExDate',
        listNameFull: 'Dividend Ex Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      ticker: {
        type: 'string',
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        type: 'string',
        listName: 'Name',
        listNameFull: 'Name',
        isDefault: true,
        display: true
      },
      frequency: {
        type: 'int',
        listName: 'Frequency',
        listNameFull: 'Dividend Frequency',
        isDefault: true,
        display: true
      },
      dividend: {
        type: 'numeric',
        listName: 'Dividend',
        listNameFull: 'Dividend',
        isDefault: true,
        display: true
      },
      dividend_yield: {
        type: 'numeric',
        computeValue: (event) => computePercent(event.dividend_yield),
        listName: 'Yield',
        listNameFull: 'Dividend Yield',
        isDefault: true,
        display: true
      },
      date: {
        type: 'date',
        listName: 'Ann Date',
        listNameFull: 'Announced Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      record_date: {
        type: 'numeric',
        listName: 'Record',
        listNameFull: 'Record Date',
        isDefault: true,
        display: true
      },
      payable_date: {
        type: 'numeric',
        listName: 'Payable',
        listNameFull: 'Payable Date',
        isDefault: true,
        display: true
      },
      exchange: {
        type: 'string',
        listName: 'Ex',
        listNameFull: 'Exchange',
        display: false
      },
      dividend_prior: {
        type: 'numeric',
        listName: 'Prior',
        listNameFull: 'Dividend Prior',
        display: false
      },
      dividend_type: {
        type: 'string',
        listName: 'Type',
        listNameFull: 'Type of Dividend',
        display: false
      },
      importance: {
        type: 'int',
        listName: 'Importance',
        listNameFull: 'Importance',
        display: false
      },
    }
  },
  ipos: {
    name: 'IPOs',
    sort: {
      date: SortTypes.DESC,
      time: SortTypes.DESC,
    },
    fields: {
      date: {
        listName: 'Date',
        listNameFull: 'Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      time: {
        listName: 'Time',
        listNameFull: 'Time',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      ticker: {
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        listName: 'Name',
        listNameFull: 'Name',
        isDefault: true,
        display: true
      },
      pricing_date: {
        listName: 'Pricing Date',
        listNameFull: 'Pricing Date',
        isDefault: true,
        display: true
      },
      price_min: {
        listName: 'Price Min',
        listNameFull: 'Price Minimum',
        isDefault: true,
        display: true
      },
      price_max: {
        listName: 'Price Max',
        listNameFull: 'Price Maximum',
        isDefault: true,
        display: true
      },
      deal_status: {
        listName: 'Deal Status',
        listNameFull: 'Deal Status',
        display: true
      },
      insider_lockup_days: {
        listName: 'Insider Lockup Days',
        listNameFull: 'Insider Lockup Days',
        display: true
      },
      insider_lockup_date: {
        listName: 'Insider Lockup Date',
        listNameFull: 'Insider Lockup Date',
        isDefault: true,
        display: true
      },
      offering_value: {
        transform: 'numberShorten',
        listName: 'Offering',
        listNameFull: 'Offering Value',
        display: true
      },
      offering_shares: {
        transform: 'numberShorten',
        listName: 'Offering Shares',
        listNameFull: 'Number of Shares Offered',
        display: true
      },
      shares_outstanding: {
        transform: 'numberShorten',
        listName: 'Shares Outstanding',
        listNameFull: 'Number of Shares Outstanding',
        display: true
      },
      shares_from_selling_holders: {
        transform: 'numberShorten',
        listName: 'Shares Holder',
        listNameFull: 'Shares Available form Selling Holders',
        display: true
      },
      lead_underwriter: {
        listName: 'Lead Underwriter(s)',
        listNameFull: 'Lead Underwriter(s)',
        display: true
      },
      other_underwriters: {
        listName: 'Other Underwriter(s)',
        listNameFull: 'Lead Underwriter(s)',
        display: true
      },
      underwriter_quiet_expiration_days: {
        listName: 'Underwrite Quiet Period',
        listNameFull: 'Underwrite Quiet Period',
        display: true
      },
      underwriter_quiet_expiration_date: {
        listName: 'Underwrite Quiet Period Date',
        listNameFull: 'Underwrite Quiet Period Date',
        display: true
      },
    }
  },
  splits: {
    name: 'Splits',
    sort: {
      date_announced: SortTypes.DESC,
    },
    fields: {
      date_announced: {
        type: 'date',
        listName: 'Ann Date',
        listNameFull: 'Announced Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      exchange: {
        type: 'string',
        listName: 'Ex',
        listNameFull: 'Exchange',
        isDefault: true,
        display: true
      },
      ticker: {
        type: 'string',
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        type: 'string',
        listName: 'Name',
        listNameFull: 'Name',
        isDefault: true,
        display: true
      },
      ratio: {
        type: 'string',
        listName: 'Ratio',
        listNameFull: 'Split Ratio',
        isDefault: true,
        display: true
      },
      optionable: {
        type: 'string',
        listName: 'Optionable',
        listNameFull: 'Optionable',
        isDefault: true,
        display: true
      },
      date_ex: {
        type: 'date',
        listName: 'Ex Date',
        listNameFull: 'Ex Date',
        isDefault: true,
        display: true
      },
      date_distribution: {
        type: 'date',
        listName: 'Dist Date',
        listNameFull: 'Distribution Date',
        isDefault: true,
        display: true
      },
      date_recorded: {
        type: 'date',
        listName: 'Rec Date',
        listNameFull: 'Recorded Date',
        isDefault: true,
        display: true
      },
    }
  },
  earnings: {
    name: 'Earnings',
    sort: {
      date: SortTypes.DESC,
      market: SortTypes.DESC,
    },
    fields: {
      date: {
        type: 'date',
        listName: 'Date',
        listNameFull: 'Announced Date',
        category: 'info',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      time: {
        type: 'time',
        listName: 'Time',
        listNameFull: 'Announced Time',
        category: 'info',
        startsLocked: true,
        display: true
      },
      market: {
        type: 'market',
        listName: 'Market',
        listNameFull: 'Market Session',
        category: 'info',
        computeValue: (event) => marketString(event.time),
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        type: 'string',
        listName: 'Name',
        listNameFull: 'Name',
        category: 'info',
        display: true
      },
      ticker: {
        type: 'string',
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        category: 'info',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      exchange: {
        type: 'string',
        listName: 'Ex',
        listNameFull: 'Exchange',
        display: true,
      },
      period: {
        type: 'string',
        listName: 'Period',
        listNameFull: 'Period',
        category: 'info',
        isDefault: true,
        display: true
      },
      period_year: {
        type: 'int',
        listName: 'Year',
        listNameFull: 'Period Year',
        display: true
      },
      date_confirmed: {
        type: 'bool',
        computeValue: (event) => boolToString(event.date_confirmed),
        listName: 'Conf.',
        listNameFull: 'Date Confirmed',
        isDefault: true,
        display: true
      },
      eps: {
        type: 'numeric',
        listName: 'EPS',
        listNameFull: 'Earnings per Share',
        isDefault: true,
        display: true
      },
      eps_est: {
        type: 'numeric',
        listName: 'EPS Est',
        listNameFull: 'EPS Estimate',
        isDefault: true,
        display: true
      },
      eps_bmpercent: {
        computeValue: (event) => roundPercent(surprisePercent(event.eps_est, event.eps)),
        listName: 'EPS Surprise (%)',
        listNameFull: 'EPS Surprise Percent',
        category: 'stats',
        isDefault: true,
        display: true
      },
      eps_prior: {
        type: 'numeric',
        listName: 'Prior',
        listNameFull: 'EPS Prior',
        display: false
      },
      revenue: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Rev',
        listNameFull: 'Revenue',
        isDefault: true,
        display: true
      },
      revenue_est: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Rev Est',
        listNameFull: 'Revenue Estimate',
        isDefault: true,
        display: true
      },
      revenue_bmpercent: {
        computeValue: (event) => roundPercent(surprisePercent(event.revenue_est, event.revenue)),
        listName: 'Rev Surprise (%)',
        listNameFull: 'Revenue Surprise Percent',
        category: 'stats',
        isDefault: true,
        display: true
      },
      revenue_prior: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Prior',
        listNameFull: 'Revenue Prior',
        display: false
      },
      importance: {
        listName: 'Imp.',
        listNameFull: 'Importance',
        category: 'info',
        display: true
      },
    }
  },
  // Date, Time, Symbol, Name, Period, Year, Prelim, EPS Min, EPS
  guidance: {
    name: 'Guidance',
    sort: {
      date: SortTypes.DESC,
      time: SortTypes.DESC,
    },
    fields: {
      date: {
        type: 'date',
        listName: 'Date',
        listNameFull: 'Announced Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      time: {
        type: 'time',
        listName: 'Time',
        listNameFull: 'Announced Time',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      ticker: {
        type: 'string',
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        type: 'string',
        listName: 'Name',
        listNameFull: 'Name',
        isDefault: true,
        display: true
      },
      period: {
        type: 'string',
        listName: 'Period',
        listNameFull: 'Period',
        isDefault: true,
        display: true
      },
      period_year: {
        type: 'int',
        listName: 'Year',
        listNameFull: 'Period Year',
        isDefault: true,
        display: true
      },
      prelim: {
        type: 'bool',
        computeValue: (event) => boolToString(event.prelim),
        listName: 'Preliminary',
        listNameFull: 'Preliminary Earnings',
        isDefault: true,
        display: true
      },
      eps_guidance_min: {
        type: 'numeric',
        listName: 'EPS Min',
        listNameFull: 'EPS Guidance Min',
        isDefault: true,
        display: true
      },
      eps_guidance_max: {
        type: 'numeric',
        listName: 'EPS Max',
        listNameFull: 'EPS Guidance Max',
        isDefault: true,
        display: true
      },
      eps_guidance_est: {
        type: 'numeric',
        listName: 'EPS Est',
        listNameFull: 'EPS Consensus Estimate',
        isDefault: true,
        display: true
      },
      exchange: {
        type: 'string',
        listName: 'Ex',
        listNameFull: 'Exchange',
        display: false
      },
      eps_guidance_prior_min: {
        type: 'numeric',
        listName: 'Prior Min',
        listNameFull: 'EPS Guidance Prior Min',
        display: false
      },
      eps_guidance_prior_max: {
        type: 'numeric',
        listName: 'Prior Max',
        listNameFull: 'EPS Guidance Prior Max',
        display: false
      },
      revenue_guidance_est: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Rev Est',
        listNameFull: 'Revenue Consensus Estimate',
        isDefault: true,
        display: false
      },
      revenue_guidance_min: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Rev Min',
        listNameFull: 'Revenue Guidance Min',
        isDefault: true,
        display: true
      },
      revenue_guidance_max: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Rev Max',
        listNameFull: 'Revenue Guidance Max',
        isDefault: true,
        display: true
      },
      revenue_guidance_prior_min: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Prior Min',
        listNameFull: 'Revenue Guidance Prior Min',
        display: false
      },
      revenue_guidance_prior_max: {
        type: 'numeric',
        transform: 'numberShorten',
        listName: 'Prior Max',
        listNameFull: 'Revenue Guidance Prior Max',
        display: false
      },
      importance: {
        type: 'int',
        listName: 'Importance',
        listNameFull: 'Importance',
        display: false
      },
    }
  },
  ratings: {
    name: 'Analyst Ratings',
    sort: {
      date: SortTypes.DESC,
      time: SortTypes.DESC,
    },
    fields: {
      date: {
        listName: 'Date',
        listNameFull: 'Call Date',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      time: {
        listName: 'Time',
        listNameFull: 'Time',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      ticker: {
        transform: 'symbol',
        listName: 'Symbol',
        listNameFull: 'Symbol',
        isDefault: true,
        startsLocked: true,
        display: true
      },
      name: {
        listName: 'Name',
        listNameFull: 'Name',
        isDefault: true,
        display: true
      },
      analyst: {
        listName: 'Analyst',
        listNameFull: 'Analyst',
        isDefault: true,
        display: true
      },
      action_company: {
        listName: 'Action Co',
        listNameFull: 'Action on Security',
        isDefault: true,
        display: true
      },
      rating_prior: {
        listName: 'Prior',
        listNameFull: 'Rating Prior',
        isDefault: true,
        display: true
      },
      rating_current: {
        listName: 'Rating',
        listNameFull: 'Rating',
        isDefault: true,
        display: true
      },
      action_pt: {
        listName: 'Action PT',
        listNameFull: 'Action PT',
        isDefault: true,
        display: true
      },
      pt_prior: {
        listName: 'Prior',
        listNameFull: 'Price Target Prior',
        isDefault: true,
        display: true
      },
      pt_current: {
        listName: 'PT',
        listNameFull: 'Price Target',
        isDefault: true,
        display: true
      },
      ptpo: {
        listName: 'PT/PO',
        listNameFull: 'PT/PO',
        display: false
      },
      url: {
        transform: 'link',
        listName: 'URL',
        listNameFull: 'Article URL',
        display: false
      },
      importance: {
        listName: 'Importance',
        listNameFull: 'Importance',
        display: false
      }
    }
  },
};

export function calendarNames() {
  const names = {};
  const calKeys = Object.keys(definition).sort((a, b) => definition[a].name > definition[b].name);
  for (const key in calKeys) {
    names[calKeys[key]] = definition[calKeys[key]].name;
  }
  return names;
}

export function calendarFields(calendarType) {
  const fields = {};
  if (calendarType) {
    forEach(definition[calendarType].fields, (field, name) => {
      if (field.display) {
        fields[name] = field;
      }
    });
  }
  return fields;
}

export function calendarEmptySettings() {
  const cal = {};
  for (const calKey of Object.keys(definition)) {
    cal[calKey] = {};
  }
  return cal;
}

export function calendarDefaultSortSettings() {
  const defSort = {};
  for (const calKey of Object.keys(definition)) {
    defSort[calKey] = definition[calKey].sort;
  }
  return defSort;
}

export function calendarDefaultFrozenSettings() {
  const cal = {};
  for (const calKey of Object.keys(definition)) {
    cal[calKey] = {};
    const fields = calendarFields(calKey);
    for (const fieldKey of Object.keys(fields)) {
      if (fields[fieldKey].startsLocked) {
        cal[calKey][fieldKey] = true;
      }
    }
  }
  return cal;
}

export function calendarDefaultDisplaySettings() {
  const cal = {};
  for (const calKey of Object.keys(definition)) {
    cal[calKey] = {};
    const fields = calendarFields(calKey);
    for (const fieldKey of Object.keys(fields)) {
      if (!fields[fieldKey].isDefault) {
        cal[calKey][fieldKey] = true;
      }
    }
  }
  return cal;
}

export function transformField(transform, data) {
  switch (transform) {
  case 'webcast_url':
  case 'link':
    return (data ? <a href={data} target="_blank">Click Here</a> : '');
  case 'numberShorten':
    return numberShorthand(data);
  case 'ticker':
  case 'symbol':
    if (data) {
      return <Ticker symbol={data} />;
    }
    return '';
  default:
    return data;
  }
}

// Ways of coloring calendar fields. //

function determineActionCo(action) {
  if (includes(action, 'upgrade')) {
    return 'u-upBg';
  }
  if (includes(action, 'downgrade')) {
    return 'u-downBg';
  }
  if (action === '') {
    return '';
  }
  return 'u-neutralBg';
}

function determineActionPt(action) {
  if (includes(action, 'raises')) {
    return 'u-upBg';
  }
  if (includes(action, 'lowers')) {
    return 'u-downBg';
  }
  if (action === '') {
    return '';
  }
  return 'u-neutralBg';
}

function determineRating(rating) {
  const pos = ['buy', 'conviction buy', 'market outperform', 'outperform', 'strong buy', 'overweight'];
  const neg = ['sell', 'underperform', 'underweight'];
  if (includes(pos, rating)) {
    return 'u-upBg';
  }
  if (includes(neg, rating)) {
    return 'u-downBg';
  }
  if (rating === '') {
    return '';
  }
  return 'u-neutralBg';
}

function determineSign(value) {
  if ( (!isNaN(parseFloat(value)) && isFinite(value)) && value !== 0) {
    return value > 0 ? 'u-upBg' : 'u-downBg';
  }
  return 'u-neutralText';
}

export function colorField(fieldName, value) {
  switch (fieldName) {
  case 'action_company':
    return determineActionCo(decapitalize(value));
  case 'action_pt':
    return determineActionPt(decapitalize(value));
  case 'rating_prior':
  case 'rating_current':
    return determineRating(decapitalize(value));
  case 'eps_bmpercent':
  case 'revenue_bmpercent':
    return determineSign(parseFloat(value));
  default:
    return '';
  }
}
