import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import Logger from 'src/application/logging/logger';
import { getAtomStore, sessionStorageUtil } from 'src/atoms/atomStore/atomStore';

const logger = new Logger('performanceMetrics');

export const APP_INITIAL_START_TIME_KEY = 'spiffy-app-initial-start-time';
export const PAGE_LOAD_OFFSET_TIME_KEY = 'spiffy-page-load-offset-time';
/**
 * The different performance metrics that can be logged. All times are relative to the
 * initial start time of the app and are stored in milliseconds.
 */
export enum PerfMetricsEvents {
  PageLoadOffset = 'page_load_offset_ms',
  MainBundleLoaded = 'main_bundle_loaded_ms',
  OrgConfigLoadStarted = 'org_config_load_started_ms',
  OrgConfigLoadEnded = 'org_config_load_ended_ms',
  FirstResponseStarted = 'first_response_started_ms',
  FirstResponseCompleted = 'first_response_completed_ms',
  FirstSuggestionsStarted = 'first_suggestions_started_ms',
  FirstSuggestionsCompleted = 'first_suggestions_completed_ms',
  EmbeddedWidgetRendered = 'embedded_widget_rendered_ms',
  FloatingButtonRendered = 'floating_button_rendered_ms',
  TopSuggestionsBarRendered = 'top_suggestions_bar_rendered_ms',
  BottomSuggestionsBarRendered = 'bottom_suggestions_bar_rendered_ms',
  SearchPromptRendered = 'search_prompt_rendered_ms',
}

const internalPerfMetricsAtom = atom<Map<PerfMetricsEvents, number>>(new Map());
export const appInitialStartTimeMsAtom = atomWithStorage<string | undefined>(
  APP_INITIAL_START_TIME_KEY,
  sessionStorage.getItem(APP_INITIAL_START_TIME_KEY) ?? undefined,
  sessionStorageUtil,
  {
    getOnInit: true,
  },
);

export const pageLoadOffsetTimeAtom = atomWithStorage<string | undefined>(
  PAGE_LOAD_OFFSET_TIME_KEY,
  sessionStorage.getItem(PAGE_LOAD_OFFSET_TIME_KEY) ?? undefined,
  sessionStorageUtil,
  {
    getOnInit: true,
  },
);

/**
 * Resets the performance metrics atom to an empty map. This should be called after the performance
 * metrics have been reported to amplitude. On SPA, it ensures that we can still capture metrics as
 * the user navigates around the app (although there is more work to be done to fully enable this).
 * On non-SPA, it ensures that previously captured metrics are not reported again.
 */
export const resetPerformanceMetricsAtom = () => {
  const atomStore = getAtomStore();
  atomStore.set(internalPerfMetricsAtom, new Map());
};
export const performanceMetricsAtom = atom(get => get(internalPerfMetricsAtom));
export const hasReportedPerformanceMetricsAtom = atom<boolean>(false);

/**
 * Logs a performance metric by capturing the delta between the initial app start time
 * and the current time. If the metric has already been logged, it will not be logged again.
 *
 * @param value The performance metric name to log.
 */
export const logPerfMetricAtom = atom(null, (get, set, value: PerfMetricsEvents) => {
  const initialTimeStorageValue = get(appInitialStartTimeMsAtom);
  const initialTimeMs = initialTimeStorageValue ? parseInt(initialTimeStorageValue, 10) : undefined;

  if (initialTimeMs == null) {
    logger.logWarn(`No initial app start time found. Skipping...`, undefined);
    return;
  }

  const currentPerfMetrics = get(internalPerfMetricsAtom);
  if (currentPerfMetrics.has(value)) {
    return;
  }

  const currentTimeMs = Date.now();
  const deltaMs = currentTimeMs - initialTimeMs;
  currentPerfMetrics.set(value, deltaMs);
  set(internalPerfMetricsAtom, currentPerfMetrics);
});
