import moment from 'moment';

import {parseStatsPeriod} from 'sentry/components/organizations/pageFilters/parse';
import type {DataCategoryInfo, IntervalPeriod} from 'sentry/types/core';
import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';

import {formatUsageWithUnits} from '../utils';

/**
 * Avoid changing "MMM D" format as X-axis labels on UsageChart are naively
 * truncated by date.slice(0, 6). This avoids "..." when truncating by ECharts.
 */
export const FORMAT_DATETIME_DAILY = 'MMM D';
export const FORMAT_DATETIME_HOURLY = 'MMM D LT';

/**
 * Used to generate X-axis data points and labels for UsageChart
 * Ensure that this method is idempotent and doesn't change the moment object
 * that is passed in
 *
 * Use the `useUtc` parameter to get the UTC date for the provided
 * moment instance.
 */
export function getDateFromMoment(
  m: moment.Moment,
  interval: IntervalPeriod = '1d',
  useUtc: boolean = false
) {
  const days = parsePeriodToHours(interval) / 24;
  if (days >= 1) {
    return useUtc
      ? moment.utc(m).format(FORMAT_DATETIME_DAILY)
      : m.format(FORMAT_DATETIME_DAILY);
  }

  const parsedInterval = parseStatsPeriod(interval);
  const datetime = useUtc ? moment(m).utc() : moment(m).local();

  return parsedInterval
    ? `${datetime.format(FORMAT_DATETIME_HOURLY)} - ${datetime
        .add(parsedInterval.period as any, parsedInterval.periodLength as any)
        .format('LT (Z)')}`
    : datetime.format(FORMAT_DATETIME_HOURLY);
}

export function getDateFromUnixTimestamp(timestamp: number) {
  const date = moment.unix(timestamp);
  return getDateFromMoment(date);
}

export function getXAxisDates(
  dateStart: moment.MomentInput,
  dateEnd: moment.MomentInput,
  dateUtc: boolean = false,
  interval: IntervalPeriod = '1d'
): string[] {
  const range: string[] = [];
  const start = moment(dateStart).startOf('h');
  const end = moment(dateEnd).startOf('h');

  if (!start.isValid() || !end.isValid()) {
    return range;
  }

  const {period, periodLength} = parseStatsPeriod(interval) ?? {
    period: 1,
    periodLength: 'd',
  };

  while (!start.isAfter(end)) {
    range.push(getDateFromMoment(start, interval, dateUtc));
    start.add(period as any, periodLength as any); // FIXME(ts): Something odd with momentjs types
  }

  return range;
}

export function getTooltipFormatter(dataCategory: DataCategoryInfo['plural']) {
  return (val: number = 0) =>
    formatUsageWithUnits(val, dataCategory, {useUnitScaling: true});
}

const MAX_NUMBER_OF_LABELS = 10;

/**
 *
 * @param dataPeriod - Quantity of hours covered by the data
 * @param numBars - Quantity of data points covered by the dataPeriod
 */
export function getXAxisLabelInterval(dataPeriod: number, numBars: number) {
  return dataPeriod > 7 * 24
    ? getLabelIntervalLongPeriod(dataPeriod, numBars)
    : getLabelIntervalShortPeriod(dataPeriod, numBars);
}

/**
 * @param dataPeriod - Quantity of hours covered by data, expected 7+ days
 */
function getLabelIntervalLongPeriod(dataPeriod: number, numBars: number) {
  const days = dataPeriod / 24;
  if (days <= 7) {
    throw new Error('This method should be used for periods > 7 days');
  }

  // Use 1 tick per day
  let numTicks = days;
  let numLabels = numTicks;

  const daysBetweenLabels = [2, 4, 7, 14];
  const daysBetweenTicks = [1, 2, 7, 7];

  for (let i = 0; i < daysBetweenLabels.length && numLabels > MAX_NUMBER_OF_LABELS; i++) {
    numLabels = numTicks / daysBetweenLabels[i];
    numTicks = days / daysBetweenTicks[i];
  }

  return {
    xAxisTickInterval: numBars / numTicks - 1,
    xAxisLabelInterval: numBars / numLabels - 1,
  };
}

/**
 * @param dataPeriod - Quantity of hours covered by data, expected <7 days
 */
function getLabelIntervalShortPeriod(dataPeriod: number, numBars: number) {
  const days = dataPeriod / 24;
  if (days > 7) {
    throw new Error('This method should be used for periods <= 7 days');
  }

  // Use 1 tick/label per day, since it's guaranteed to be 7 or less
  const numTicks = days;
  const interval = numBars / numTicks;

  return {
    xAxisTickInterval: interval - 1,
    xAxisLabelInterval: interval - 1,
  };
}
