import moment from 'moment';

import {t} from 'sentry/locale';
import {SeriesApi} from 'sentry/types';
import {Series} from 'sentry/types/echarts';
import {defined} from 'sentry/utils';
import commonTheme from 'sentry/utils/theme';
import {Outcome} from 'sentry/views/organizationStats/types';

import {quantityField} from '.';

export function projectStatsToPredictedSeries(
  projectStats?: SeriesApi,
  client?: number,
  server?: number,
  specifiedClientRate?: number
): Series[] {
  if (!projectStats || !defined(client) || !defined(server)) {
    return [];
  }

  const clientRate = Math.max(Math.min(client, 1), 0);
  let serverRate = Math.max(Math.min(server, 1), 0);

  const commonSeriesConfig = {
    barMinHeight: 1,
    type: 'bar',
    stack: 'predictedUsage',
  };

  const seriesData: Record<
    'indexedAndProcessed' | 'processed' | 'discarded',
    Series['data']
  > = {
    indexedAndProcessed: [],
    processed: [],
    discarded: [],
  };

  (
    projectStats.intervals.map((interval, index) => {
      const result = {
        indexedAndProcessed: 0,
        processed: 0,
        discarded: 0,
      };
      projectStats.groups.forEach(group => {
        switch (group.by.outcome) {
          case Outcome.ACCEPTED:
            result.indexedAndProcessed += group.series[quantityField][index];
            break;
          case Outcome.CLIENT_DISCARD:
            result.discarded += group.series[quantityField][index];
            break;
          case Outcome.FILTERED:
            if (String(group.by.reason).startsWith('Sampled')) {
              result.processed += group.series[quantityField][index];
            }
            break;
          default:
          // We do not take invalid, rate_limited and other filtered into account
        }
      });
      return {
        interval: moment(interval).valueOf(),
        ...result,
      };
    }) as Array<
      Record<'indexedAndProcessed' | 'processed' | 'discarded' | 'interval', number>
    >
  ).forEach((bucket, index) => {
    const {indexedAndProcessed, processed, discarded, interval} = bucket;

    if (clientRate < serverRate!) {
      serverRate = clientRate;
    }

    let total = indexedAndProcessed + processed + discarded;

    if (defined(specifiedClientRate)) {
      // We assume that the clientDiscard is 0 and
      // calculate the discard client (SDK) bucket according to the specified client rate
      const newClientDiscard = total / specifiedClientRate - total;
      total += newClientDiscard;
    }

    const newSentClient = total * clientRate;
    const newDiscarded = total - newSentClient;
    const newIndexedAndProcessed =
      clientRate === 0 ? 0 : newSentClient * (serverRate! / clientRate);
    const newProcessed = newSentClient - newIndexedAndProcessed;

    seriesData.indexedAndProcessed[index] = {
      name: interval,
      value: Math.round(newIndexedAndProcessed),
    };
    seriesData.processed[index] = {
      name: interval,
      value: Math.round(newProcessed),
    };
    seriesData.discarded[index] = {
      name: interval,
      value: Math.round(newDiscarded),
    };
  });

  return [
    {
      seriesName: t('Indexed and Processed'),
      color: commonTheme.green300,
      ...commonSeriesConfig,
      data: seriesData.indexedAndProcessed,
    },
    {
      seriesName: t('Processed'),
      color: commonTheme.yellow300,
      data: seriesData.processed,
      ...commonSeriesConfig,
    },
    {
      seriesName: t('Discarded'),
      color: commonTheme.red300,
      data: seriesData.discarded,
      ...commonSeriesConfig,
    },
  ];
}
