import {browserHistory} from 'react-router';
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import type {Location} from 'history';

import Avatar from 'sentry/components/avatar';
import {Button} from 'sentry/components/button';
import {DropdownMenu} from 'sentry/components/dropdownMenu';
import UserBadge from 'sentry/components/idBadge/userBadge';
import Link from 'sentry/components/links/link';
import ContextIcon from 'sentry/components/replays/contextIcon';
import {formatTime} from 'sentry/components/replays/utils';
import ScoreBar from 'sentry/components/scoreBar';
import TimeSince from 'sentry/components/timeSince';
import {Tooltip} from 'sentry/components/tooltip';
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
import {
  IconCalendar,
  IconCursorArrow,
  IconDelete,
  IconEllipsis,
  IconFire,
} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space, ValidSize} from 'sentry/styles/space';
import type {Organization} from 'sentry/types';
import {trackAnalytics} from 'sentry/utils/analytics';
import EventView from 'sentry/utils/discover/eventView';
import {spanOperationRelativeBreakdownRenderer} from 'sentry/utils/discover/fieldRenderers';
import {getShortEventId} from 'sentry/utils/events';
import {decodeScalar} from 'sentry/utils/queryString';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import {useLocation} from 'sentry/utils/useLocation';
import useMedia from 'sentry/utils/useMedia';
import useProjects from 'sentry/utils/useProjects';
import {normalizeUrl} from 'sentry/utils/withDomainRequired';
import type {ReplayListRecordWithTx} from 'sentry/views/performance/transactionSummary/transactionReplays/useReplaysWithTxData';
import type {ReplayListLocationQuery, ReplayListRecord} from 'sentry/views/replays/types';

type Props = {
  replay: ReplayListRecord | ReplayListRecordWithTx;
  showDropdownFilters?: boolean;
};

export type ReferrerTableType =
  | 'main'
  | 'dead-table'
  | 'errors-table'
  | 'rage-table'
  | 'selector-widget';

type EditType = 'set' | 'remove';

function generateAction({
  key,
  value,
  edit,
  location,
}: {
  edit: EditType;
  key: string;
  location: Location<ReplayListLocationQuery>;
  value: string;
}) {
  const search = new MutableSearch(decodeScalar(location.query.query) || '');

  const modifiedQuery =
    edit === 'set' ? search.setFilterValues(key, [value]) : search.removeFilter(key);

  const onAction = () => {
    browserHistory.push({
      pathname: location.pathname,
      query: {
        ...location.query,
        query: modifiedQuery.formatString(),
      },
    });
  };

  return onAction;
}

function OSBrowserDropdownFilter({
  type,
  name,
  version,
}: {
  name: string | null;
  type: string;
  version: string | null;
}) {
  const location = useLocation<ReplayListLocationQuery>();
  return (
    <DropdownMenu
      items={[
        ...(name
          ? [
              {
                key: 'name',
                label: tct('[type] name: [name]', {
                  type: <b>{type}</b>,
                  name: <b>{name}</b>,
                }),
                children: [
                  {
                    key: 'name_add',
                    label: t('Add to filter'),
                    onAction: generateAction({
                      key: `${type}.name`,
                      value: name ?? '',
                      edit: 'set',
                      location,
                    }),
                  },
                  {
                    key: 'name_exclude',
                    label: t('Exclude from filter'),
                    onAction: generateAction({
                      key: `${type}.name`,
                      value: name ?? '',
                      edit: 'remove',
                      location,
                    }),
                  },
                ],
              },
            ]
          : []),
        ...(version
          ? [
              {
                key: 'version',
                label: tct('[type] version: [version]', {
                  type: <b>{type}</b>,
                  version: <b>{version}</b>,
                }),
                children: [
                  {
                    key: 'version_add',
                    label: t('Add to filter'),
                    onAction: generateAction({
                      key: `${type}.version`,
                      value: version ?? '',
                      edit: 'set',
                      location,
                    }),
                  },
                  {
                    key: 'version_exclude',
                    label: t('Exclude from filter'),
                    onAction: generateAction({
                      key: `${type}.version`,
                      value: version ?? '',
                      edit: 'remove',
                      location,
                    }),
                  },
                ],
              },
            ]
          : []),
      ]}
      usePortal
      size="xs"
      offset={4}
      position="bottom"
      preventOverflowOptions={{padding: 4}}
      flipOptions={{
        fallbackPlacements: ['top', 'right-start', 'right-end', 'left-start', 'left-end'],
      }}
      trigger={triggerProps => (
        <ActionMenuTrigger
          {...triggerProps}
          translucentBorder
          aria-label={t('Actions')}
          icon={<IconEllipsis size="xs" />}
          size="zero"
        />
      )}
    />
  );
}

function NumericDropdownFilter({
  type,
  val,
  triggerOverlay,
}: {
  type: string;
  val: number;
  triggerOverlay?: boolean;
}) {
  const location = useLocation<ReplayListLocationQuery>();
  return (
    <DropdownMenu
      items={[
        {
          key: 'add',
          label: 'Add to filter',
          onAction: generateAction({
            key: type,
            value: val.toString(),
            edit: 'set',
            location,
          }),
        },
        {
          key: 'greater',
          label: 'Show values greater than',
          onAction: generateAction({
            key: type,
            value: '>' + val.toString(),
            edit: 'set',
            location,
          }),
        },
        {
          key: 'less',
          label: 'Show values less than',
          onAction: generateAction({
            key: type,
            value: '<' + val.toString(),
            edit: 'set',
            location,
          }),
        },
        {
          key: 'exclude',
          label: t('Exclude from filter'),
          onAction: generateAction({
            key: type,
            value: val.toString(),
            edit: 'remove',
            location,
          }),
        },
      ]}
      usePortal
      size="xs"
      offset={4}
      position="bottom"
      preventOverflowOptions={{padding: 4}}
      flipOptions={{
        fallbackPlacements: ['top', 'right-start', 'right-end', 'left-start', 'left-end'],
      }}
      trigger={triggerProps =>
        triggerOverlay ? (
          <OverlayActionMenuTrigger
            {...triggerProps}
            translucentBorder
            aria-label={t('Actions')}
            icon={<IconEllipsis size="xs" />}
            size="zero"
          />
        ) : (
          <NumericActionMenuTrigger
            {...triggerProps}
            translucentBorder
            aria-label={t('Actions')}
            icon={<IconEllipsis size="xs" />}
            size="zero"
          />
        )
      }
    />
  );
}

function getUserBadgeUser(replay: Props['replay']) {
  return replay.is_archived
    ? {
        username: '',
        email: '',
        id: '',
        ip_address: '',
        name: '',
      }
    : {
        username: replay.user?.display_name || '',
        email: replay.user?.email || '',
        id: replay.user?.id || '',
        ip_address: replay.user?.ip || '',
        name: replay.user?.username || '',
      };
}

export function ReplayCell({
  eventView,
  organization,
  referrer,
  replay,
  referrer_table,
  isWidget,
}: Props & {
  eventView: EventView;
  organization: Organization;
  referrer: string;
  referrer_table: ReferrerTableType;
  isWidget?: boolean;
}) {
  const {projects} = useProjects();
  const project = projects.find(p => p.id === replay.project_id);

  const replayDetails = {
    pathname: normalizeUrl(`/organizations/${organization.slug}/replays/${replay.id}/`),
    query: {
      referrer,
      ...eventView.generateQueryStringObject(),
    },
  };

  const replayDetailsErrorTab = {
    pathname: normalizeUrl(`/organizations/${organization.slug}/replays/${replay.id}/`),
    query: {
      referrer,
      ...eventView.generateQueryStringObject(),
      t_main: 'errors',
    },
  };

  const replayDetailsDOMEventsTab = {
    pathname: normalizeUrl(`/organizations/${organization.slug}/replays/${replay.id}/`),
    query: {
      referrer,
      ...eventView.generateQueryStringObject(),
      t_main: 'dom',
      f_d_type: 'ui.slowClickDetected',
    },
  };

  const detailsTab = () => {
    switch (referrer_table) {
      case 'errors-table':
        return replayDetailsErrorTab;
      case 'dead-table':
      case 'rage-table':
      case 'selector-widget':
        return replayDetailsDOMEventsTab;
      default:
        return replayDetails;
    }
  };

  const trackNavigationEvent = () =>
    trackAnalytics('replay.list-navigate-to-details', {
      project_id: project?.id,
      platform: project?.platform,
      organization,
      referrer,
      referrer_table,
    });

  if (replay.is_archived) {
    return (
      <Item isArchived={replay.is_archived}>
        <Row gap={1}>
          <StyledIconDelete color="gray500" size="md" />
          <div>
            <Row gap={0.5}>{t('Deleted Replay')}</Row>
            <Row gap={0.5}>
              {project ? <Avatar size={12} project={project} /> : null}
              {getShortEventId(replay.id)}
            </Row>
          </div>
        </Row>
      </Item>
    );
  }

  const subText = (
    <Cols>
      <Row gap={1}>
        <Row gap={0.5}>
          {/* Avatar is used instead of ProjectBadge because using ProjectBadge increases spacing, which doesn't look as good */}
          {project ? <Avatar size={12} project={project} /> : null}
          {project ? project.slug : null}
          <Link to={detailsTab} onClick={trackNavigationEvent}>
            {getShortEventId(replay.id)}
          </Link>
        </Row>
        <Row gap={0.5}>
          <IconCalendar color="gray300" size="xs" />
          <TimeSince date={replay.started_at} />
        </Row>
      </Row>
    </Cols>
  );

  return (
    <Item isWidget={isWidget}>
      <UserBadge
        avatarSize={24}
        displayName={
          replay.is_archived ? (
            replay.user.display_name || t('Anonymous User')
          ) : (
            <MainLink to={detailsTab} onClick={trackNavigationEvent}>
              {replay.user.display_name || t('Anonymous User')}
            </MainLink>
          )
        }
        user={getUserBadgeUser(replay)}
        // this is the subheading for the avatar, so displayEmail in this case is a misnomer
        displayEmail={subText}
      />
    </Item>
  );
}

const StyledIconDelete = styled(IconDelete)`
  margin: ${space(0.25)};
`;

const Cols = styled('div')`
  display: flex;
  flex-direction: column;
  gap: ${space(0.5)};
  width: 100%;
`;

const Row = styled('div')<{gap: ValidSize; minWidth?: number}>`
  display: flex;
  gap: ${p => space(p.gap)};
  align-items: center;
  ${p => (p.minWidth ? `min-width: ${p.minWidth}px;` : '')}
`;

const MainLink = styled(Link)`
  font-size: ${p => p.theme.fontSizeLarge};
`;

export function TransactionCell({
  organization,
  replay,
}: Props & {organization: Organization}) {
  const location = useLocation();

  if (replay.is_archived) {
    return <Item isArchived />;
  }
  const hasTxEvent = 'txEvent' in replay;
  const txDuration = hasTxEvent ? replay.txEvent?.['transaction.duration'] : undefined;
  return hasTxEvent ? (
    <Item>
      <SpanOperationBreakdown>
        {txDuration ? <div>{txDuration}ms</div> : null}
        {spanOperationRelativeBreakdownRenderer(
          replay.txEvent,
          {organization, location},
          {enableOnClick: false}
        )}
      </SpanOperationBreakdown>
    </Item>
  ) : null;
}

export function OSCell({replay, showDropdownFilters}: Props) {
  const {name, version} = replay.os ?? {};
  const theme = useTheme();
  const hasRoomForColumns = useMedia(`(min-width: ${theme.breakpoints.large})`);

  if (replay.is_archived) {
    return <Item isArchived />;
  }
  return (
    <Item>
      <Container>
        <Tooltip title={`${name} ${version}`}>
          <ContextIcon
            name={name ?? ''}
            version={version && hasRoomForColumns ? version : undefined}
            showVersion={false}
            showTooltip={false}
          />
          {showDropdownFilters ? (
            <OSBrowserDropdownFilter type="os" name={name} version={version} />
          ) : null}
        </Tooltip>
      </Container>
    </Item>
  );
}

export function BrowserCell({replay, showDropdownFilters}: Props) {
  const {name, version} = replay.browser ?? {};
  const theme = useTheme();
  const hasRoomForColumns = useMedia(`(min-width: ${theme.breakpoints.large})`);

  if (replay.is_archived) {
    return <Item isArchived />;
  }
  return (
    <Item>
      <Container>
        <Tooltip title={`${name} ${version}`}>
          <ContextIcon
            name={name ?? ''}
            version={version && hasRoomForColumns ? version : undefined}
            showVersion={false}
            showTooltip={false}
          />
          {showDropdownFilters ? (
            <OSBrowserDropdownFilter type="browser" name={name} version={version} />
          ) : null}
        </Tooltip>
      </Container>
    </Item>
  );
}

export function DurationCell({replay, showDropdownFilters}: Props) {
  if (replay.is_archived) {
    return <Item isArchived />;
  }
  return (
    <Item>
      <Container>
        <Time>{formatTime(replay.duration.asMilliseconds())}</Time>
        {showDropdownFilters ? (
          <NumericDropdownFilter type="duration" val={replay.duration.asSeconds()} />
        ) : null}
      </Container>
    </Item>
  );
}

export function RageClickCountCell({replay, showDropdownFilters}: Props) {
  if (replay.is_archived) {
    return <Item isArchived />;
  }
  return (
    <Item data-test-id="replay-table-count-rage-clicks">
      <Container>
        {replay.count_rage_clicks ? (
          <RageClickCount>
            <IconCursorArrow size="sm" />
            {replay.count_rage_clicks}
          </RageClickCount>
        ) : (
          <Count>0</Count>
        )}
        {showDropdownFilters ? (
          <NumericDropdownFilter
            type="count_rage_clicks"
            val={replay.count_rage_clicks ?? 0}
          />
        ) : null}
      </Container>
    </Item>
  );
}

export function DeadClickCountCell({replay, showDropdownFilters}: Props) {
  if (replay.is_archived) {
    return <Item isArchived />;
  }
  return (
    <Item data-test-id="replay-table-count-dead-clicks">
      <Container>
        {replay.count_dead_clicks ? (
          <DeadClickCount>
            <IconCursorArrow size="sm" />
            {replay.count_dead_clicks}
          </DeadClickCount>
        ) : (
          <Count>0</Count>
        )}
        {showDropdownFilters ? (
          <NumericDropdownFilter
            type="count_dead_clicks"
            val={replay.count_dead_clicks ?? 0}
          />
        ) : null}
      </Container>
    </Item>
  );
}

export function ErrorCountCell({replay, showDropdownFilters}: Props) {
  if (replay.is_archived) {
    return <Item isArchived />;
  }
  return (
    <Item data-test-id="replay-table-count-errors">
      <Container>
        {replay.count_errors ? (
          <ErrorCount>
            <IconFire />
            {replay.count_errors}
          </ErrorCount>
        ) : (
          <Count>0</Count>
        )}
        {showDropdownFilters ? (
          <NumericDropdownFilter type="count_errors" val={replay.count_errors ?? 0} />
        ) : null}
      </Container>
    </Item>
  );
}

export function ActivityCell({replay, showDropdownFilters}: Props) {
  if (replay.is_archived) {
    return <Item isArchived />;
  }
  const scoreBarPalette = new Array(10).fill([CHART_PALETTE[0][0]]);
  return (
    <Item>
      <Container>
        <ScoreBar
          size={20}
          score={replay?.activity ?? 1}
          palette={scoreBarPalette}
          radius={0}
        />
        {showDropdownFilters ? (
          <NumericDropdownFilter
            type="activity"
            val={replay?.activity ?? 0}
            triggerOverlay
          />
        ) : null}
      </Container>
    </Item>
  );
}

const Item = styled('div')<{isArchived?: boolean; isWidget?: boolean}>`
  display: flex;
  align-items: center;
  gap: ${space(1)};
  ${p =>
    p.isWidget
      ? `padding: ${space(0.75)} ${space(1.5)} ${space(1.5)} ${space(1.5)};`
      : `padding: ${space(1.5)};`};
  ${p => (p.isArchived ? 'opacity: 0.5;' : '')};
`;

const Count = styled('span')`
  font-variant-numeric: tabular-nums;
`;

const DeadClickCount = styled(Count)`
  display: flex;
  width: 40px;
  gap: ${space(0.5)};
  color: ${p => p.theme.yellow300};
`;

const RageClickCount = styled(Count)`
  display: flex;
  width: 40px;
  gap: ${space(0.5)};
  color: ${p => p.theme.red300};
`;

const ErrorCount = styled(Count)`
  display: flex;
  align-items: center;
  gap: ${space(0.5)};
  color: ${p => p.theme.red400};
`;

const Time = styled('span')`
  font-variant-numeric: tabular-nums;
`;

const SpanOperationBreakdown = styled('div')`
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: ${space(0.5)};
  color: ${p => p.theme.gray500};
  font-size: ${p => p.theme.fontSizeMedium};
  text-align: right;
`;

const Container = styled('div')`
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
`;

const ActionMenuTrigger = styled(Button)`
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  padding: ${space(0.75)};
  left: -${space(0.75)};
  display: flex;
  align-items: center;
  opacity: 0;
  transition: opacity 0.1s;
  &.focus-visible,
  &[aria-expanded='true'],
  ${Container}:hover & {
    opacity: 1;
  }
`;

const NumericActionMenuTrigger = styled(ActionMenuTrigger)`
  left: 100%;
  margin-left: ${space(0.75)};
  z-index: 1;
`;

const OverlayActionMenuTrigger = styled(NumericActionMenuTrigger)`
  right: 0%;
  left: unset;
`;
