import {useCallback, useMemo, useState} from 'react';
import {browserHistory} from 'react-router';
import styled from '@emotion/styled';

import {CompactSelect, SelectOption} from 'sentry/components/compactSelect';
import Count from 'sentry/components/count';
import Link from 'sentry/components/links/link';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import Pagination from 'sentry/components/pagination';
import PerformanceDuration from 'sentry/components/performanceDuration';
import {TextTruncateOverflow} from 'sentry/components/profiling/textTruncateOverflow';
import {t, tn} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {Organization, Project} from 'sentry/types';
import {trackAnalytics} from 'sentry/utils/analytics';
import {Frame} from 'sentry/utils/profiling/frame';
import {EventsResultsDataRow} from 'sentry/utils/profiling/hooks/types';
import {useCurrentProjectFromRouteParam} from 'sentry/utils/profiling/hooks/useCurrentProjectFromRouteParam';
import {useProfileFunctions} from 'sentry/utils/profiling/hooks/useProfileFunctions';
import {formatSort} from 'sentry/utils/profiling/hooks/utils';
import {generateProfileFlamechartRouteWithQuery} from 'sentry/utils/profiling/routes';
import {decodeScalar} from 'sentry/utils/queryString';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';

const SLOWEST_FUNCTIONS_LIMIT = 15;
const SLOWEST_FUNCTIONS_CURSOR = 'functionsCursor';

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

type FunctionsField = (typeof functionsFields)[number];
interface SlowestProfileFunctionsProps {
  transaction: string;
}

export function SlowestProfileFunctions(props: SlowestProfileFunctionsProps) {
  const organization = useOrganization();
  const project = useCurrentProjectFromRouteParam();
  const [functionType, setFunctionType] = useState<'application' | 'system' | 'all'>(
    'application'
  );
  const location = useLocation();
  const functionsCursor = useMemo(
    () => decodeScalar(location.query[SLOWEST_FUNCTIONS_CURSOR]),
    [location.query]
  );
  const functionsSort = useMemo(
    () =>
      formatSort<FunctionsField>(
        decodeScalar(location.query.functionsSort),
        functionsFields,
        {
          key: 'sum()',
          order: 'desc',
        }
      ),
    [location.query.functionsSort]
  );

  const handleFunctionsCursor = useCallback((cursor, pathname, query) => {
    browserHistory.push({
      pathname,
      query: {...query, [SLOWEST_FUNCTIONS_CURSOR]: cursor},
    });
  }, []);

  const query = useMemo(() => {
    const conditions = new MutableSearch('');
    conditions.setFilterValues('transaction', [props.transaction]);
    if (functionType === 'application') {
      conditions.setFilterValues('is_application', ['1']);
    } else if (functionType === 'system') {
      conditions.setFilterValues('is_application', ['0']);
    }
    return conditions.formatString();
  }, [functionType, props.transaction]);

  const functionsQuery = useProfileFunctions<FunctionsField>({
    fields: functionsFields,
    referrer: 'api.profiling.profile-summary-functions-table',
    sort: functionsSort,
    query,
    limit: SLOWEST_FUNCTIONS_LIMIT,
    cursor: functionsCursor,
  });

  const onChangeFunctionType = useCallback(v => setFunctionType(v.value), []);
  const functions = functionsQuery.data?.data ?? [];

  const onSlowestFunctionClick = useCallback(() => {
    trackAnalytics('profiling_views.go_to_flamegraph', {
      organization,
      source: `profiling_transaction.slowest_functions_table`,
    });
  }, [organization]);

  return (
    <SlowestFunctionsContainer>
      <SlowestFunctionsTitleContainer>
        <SlowestFunctionsTypeSelect
          value={functionType}
          options={SLOWEST_FUNCTION_OPTIONS}
          onChange={onChangeFunctionType}
          triggerProps={TRIGGER_PROPS}
          offset={4}
        />
        <SlowestFunctionsPagination
          pageLinks={functionsQuery.getResponseHeader?.('Link')}
          onCursor={handleFunctionsCursor}
          size="xs"
        />
      </SlowestFunctionsTitleContainer>
      <SlowestFunctionsList>
        {functionsQuery.isLoading ? (
          <SlowestFunctionsQueryState>
            <LoadingIndicator size={36} />
          </SlowestFunctionsQueryState>
        ) : functionsQuery.isError ? (
          <SlowestFunctionsQueryState>
            {t('Failed to fetch slowest functions')}
          </SlowestFunctionsQueryState>
        ) : !functions.length ? (
          <SlowestFunctionsQueryState>
            {t('The fastest code is one that never runs.')}
          </SlowestFunctionsQueryState>
        ) : (
          functions.map((fn, i) => {
            return (
              <SlowestFunctionEntry
                key={i}
                func={fn}
                organization={organization}
                project={project}
                onSlowestFunctionClick={onSlowestFunctionClick}
              />
            );
          })
        )}
      </SlowestFunctionsList>
    </SlowestFunctionsContainer>
  );
}

interface SlowestFunctionEntryProps {
  func: EventsResultsDataRow<'function' | 'package' | 'count()' | 'p75()' | 'sum()'>;
  onSlowestFunctionClick: () => void;
  organization: Organization;
  project: Project | null;
}
function SlowestFunctionEntry(props: SlowestFunctionEntryProps) {
  const frame = useMemo(() => {
    return new Frame(
      {
        key: 0,
        name: props.func.function as string,
        package: props.func.package as string,
      },
      // Ensures that the frame runs through the normalization code path
      props.project?.platform && /node|javascript/.test(props.project.platform)
        ? props.project.platform
        : undefined,
      'aggregate'
    );
  }, [props.func, props.project]);

  return (
    <SlowestFunctionRow>
      <SlowestFunctionMainRow>
        <div>
          <Link
            onClick={props.onSlowestFunctionClick}
            to={generateProfileFlamechartRouteWithQuery({
              orgSlug: props.organization.slug,
              projectSlug: props.project?.slug ?? '',
              profileId: (props.func['examples()']?.[0] as string) ?? '',
              query: {
                // specify the frame to focus, the flamegraph will switch
                // to the appropriate thread when these are specified
                frameName: frame.name as string,
                framePackage: frame.package as string,
              },
            })}
          >
            <TextTruncateOverflow>{frame.name}</TextTruncateOverflow>
          </Link>
        </div>
        <div>
          <PerformanceDuration nanoseconds={props.func['sum()'] as number} abbreviation />
        </div>
      </SlowestFunctionMainRow>
      <SlowestFunctionMetricsRow>
        <div>
          <TextTruncateOverflow>{frame.package}</TextTruncateOverflow>
        </div>
        <div>
          <Count value={props.func['count()'] as number} />{' '}
          {tn('time', 'times', props.func['count()'])}
          {', '}
          <PerformanceDuration nanoseconds={props.func['p75()'] as number} abbreviation />
        </div>
      </SlowestFunctionMetricsRow>
    </SlowestFunctionRow>
  );
}

const SlowestFunctionsList = styled('div')`
  flex-basis: 100%;
  overflow: auto;
  min-height: 0;
`;

const SlowestFunctionsContainer = styled('div')`
  margin-top: ${space(0.5)};
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0 ${space(1)};
  border-bottom: 1px solid ${p => p.theme.border};
`;

const SlowestFunctionsPagination = styled(Pagination)`
  margin: 0;

  button {
    height: 16px;
    width: 16px;
    min-width: 16px;
    min-height: 16px;

    svg {
      width: 10px;
      height: 10px;
    }
  }
`;

const SlowestFunctionsTitleContainer = styled('div')`
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: ${space(1)};
`;

const SlowestFunctionsTypeSelect = styled(CompactSelect)`
  button {
    margin: 0;
    padding: 0;
  }
`;

const SlowestFunctionsQueryState = styled('div')`
  text-align: center;
  padding: ${space(2)} ${space(0.5)};
  color: ${p => p.theme.subText};
`;

const SlowestFunctionRow = styled('div')`
  margin-bottom: ${space(1)};
`;

const SlowestFunctionMainRow = styled('div')`
  display: flex;
  align-items: center;
  justify-content: space-between;

  > div:first-child {
    min-width: 0;
  }
  > div:last-child {
    white-space: nowrap;
  }
`;

const SlowestFunctionMetricsRow = styled('div')`
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: ${p => p.theme.fontSizeSmall};
  color: ${p => p.theme.subText};
  margin-top: ${space(0.25)};
`;

const TRIGGER_PROPS = {borderless: true, size: 'zero' as const};
const SLOWEST_FUNCTION_OPTIONS: SelectOption<'application' | 'system' | 'all'>[] = [
  {
    label: t('Slowest Application Functions'),
    value: 'application' as const,
  },
  {
    label: t('Slowest System Functions'),
    value: 'system' as const,
  },
  {
    label: t('Slowest Functions'),
    value: 'all' as const,
  },
];
