import {useCallback, useMemo} from 'react';
import styled from '@emotion/styled';

import Count from 'sentry/components/count';
import GridEditable, {
  COL_WIDTH_UNDEFINED,
  GridColumnOrder,
} from 'sentry/components/gridEditable';
import PerformanceDuration from 'sentry/components/performanceDuration';
import {ArrayLinks} from 'sentry/components/profiling/arrayLinks';
import {t} from 'sentry/locale';
import {Project} from 'sentry/types';
import {trackAnalytics} from 'sentry/utils/analytics';
import {Container, NumberContainer} from 'sentry/utils/discover/styles';
import {getShortEventId} from 'sentry/utils/events';
import {EventsResults, Sort} from 'sentry/utils/profiling/hooks/types';
import {generateProfileFlamechartRouteWithQuery} from 'sentry/utils/profiling/routes';
import {renderTableHead} from 'sentry/utils/profiling/tableRenderer';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';

interface FunctionsTableProps {
  analyticsPageSource: 'performance_transaction' | 'profiling_transaction';
  error: string | null;
  functions: EventsResults<TableColumnKey>['data'];
  isLoading: boolean;
  project: Project | undefined;
  sort: Sort<any>;
}

function FunctionsTable(props: FunctionsTableProps) {
  const location = useLocation();
  const organization = useOrganization();

  const functions: TableDataRow[] = useMemo(() => {
    const project = props.project;
    if (!project) {
      return [];
    }
    return props.functions.map(func => {
      const profileIds = (func['examples()'] as unknown as string[]) || [];

      return {
        ...func,
        'examples()': profileIds.map(profileId => {
          return {
            value: getShortEventId(profileId),
            onClick: () =>
              trackAnalytics('profiling_views.go_to_flamegraph', {
                organization,
                source: `${props.analyticsPageSource}.suspect_functions_table`,
              }),
            target: generateProfileFlamechartRouteWithQuery({
              orgSlug: organization.slug,
              projectSlug: project.slug,
              profileId,
              query: {
                // specify the frame to focus, the flamegraph will switch
                // to the appropriate thread when these are specified
                frameName: func.function as string,
                framePackage: func.package as string,
              },
            }),
          };
        }),
      };
    });
  }, [organization, props.project, props.functions, props.analyticsPageSource]);

  const generateSortLink = useCallback(
    (column: TableColumnKey) => {
      if (!SORTABLE_COLUMNS.has(column)) {
        return () => undefined;
      }

      const direction =
        props.sort.key !== column ? 'desc' : props.sort.order === 'desc' ? 'asc' : 'desc';

      return () => ({
        ...location,
        query: {
          ...location.query,
          functionsSort: `${direction === 'desc' ? '-' : ''}${column}`,
        },
      });
    },
    [location, props.sort]
  );

  return (
    <GridEditable
      isLoading={props.isLoading}
      error={props.error}
      data={functions}
      columnOrder={COLUMN_ORDER.map(key => COLUMNS[key])}
      columnSortBy={[]}
      grid={{
        renderHeadCell: renderTableHead({
          currentSort: props.sort,
          rightAlignedColumns: RIGHT_ALIGNED_COLUMNS,
          sortableColumns: SORTABLE_COLUMNS,
          generateSortLink,
        }),
        renderBodyCell: renderFunctionsTableCell,
      }}
      location={location}
    />
  );
}

const RIGHT_ALIGNED_COLUMNS = new Set<TableColumnKey>(['p75()', 'sum()', 'count()']);

const SORTABLE_COLUMNS = RIGHT_ALIGNED_COLUMNS;

function renderFunctionsTableCell(
  column: TableColumn,
  dataRow: TableDataRow,
  rowIndex: number,
  columnIndex: number
) {
  return (
    <ProfilingFunctionsTableCell
      column={column}
      dataRow={dataRow}
      rowIndex={rowIndex}
      columnIndex={columnIndex}
    />
  );
}

interface ProfilingFunctionsTableCellProps {
  column: TableColumn;
  columnIndex: number;
  dataRow: TableDataRow;
  rowIndex: number;
}

const EmptyValueContainer = styled('span')`
  color: ${p => p.theme.gray300};
`;

function ProfilingFunctionsTableCell({
  column,
  dataRow,
}: ProfilingFunctionsTableCellProps) {
  const value = dataRow[column.key];

  switch (column.key) {
    case 'count()':
      return (
        <NumberContainer>
          <Count value={value} />
        </NumberContainer>
      );
    case 'p75()':
    case 'sum()':
      return (
        <NumberContainer>
          <PerformanceDuration nanoseconds={value} abbreviation />
        </NumberContainer>
      );
    case 'examples()':
      return <ArrayLinks items={value} />;
    case 'function':
    case 'package':
      const name = value || <EmptyValueContainer>{t('Unknown')}</EmptyValueContainer>;
      return <Container>{name}</Container>;
    default:
      return <Container>{value}</Container>;
  }
}

const FIELDS = [
  'function',
  'package',
  'count()',
  'p75()',
  'sum()',
  'examples()',
] as const;

type TableColumnKey = (typeof FIELDS)[number];

type TableDataRow = Record<TableColumnKey, any>;

type TableColumn = GridColumnOrder<TableColumnKey>;

const COLUMN_ORDER: TableColumnKey[] = [
  'function',
  'package',
  'count()',
  'p75()',
  'sum()',
  'examples()',
];

const COLUMNS: Record<TableColumnKey, TableColumn> = {
  function: {
    key: 'function',
    name: t('Name'),
    width: COL_WIDTH_UNDEFINED,
  },
  package: {
    key: 'package',
    name: t('Package'),
    width: COL_WIDTH_UNDEFINED,
  },
  'p75()': {
    key: 'p75()',
    name: t('P75 Self Time'),
    width: COL_WIDTH_UNDEFINED,
  },
  'sum()': {
    key: 'sum()',
    name: t('Total Self Time'),
    width: COL_WIDTH_UNDEFINED,
  },
  'count()': {
    key: 'count()',
    name: t('Occurrences'),
    width: COL_WIDTH_UNDEFINED,
  },
  'examples()': {
    key: 'examples()',
    name: t('Example Profiles'),
    width: COL_WIDTH_UNDEFINED,
  },
};

export {FunctionsTable};
