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

import DateTime from 'sentry/components/dateTime';
import {Tooltip} from 'sentry/components/tooltip';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Event} from 'sentry/types';
import {TraceTimelineTooltip} from 'sentry/views/issueDetails/traceTimeline/traceTimelineTooltip';

import type {TimelineEvent} from './useTraceTimelineEvents';
import {useTraceTimelineEvents} from './useTraceTimelineEvents';
import {getChunkTimeRange, getEventsByColumn} from './utils';

// Adjusting this will change the number of tooltip groups
const PARENT_WIDTH = 12;
// Adjusting subwidth changes how many dots to render
const CHILD_WIDTH = 4;

interface TraceTimelineEventsProps {
  event: Event;
  width: number;
}

export function TraceTimelineEvents({event, width}: TraceTimelineEventsProps) {
  const {startTimestamp, endTimestamp, traceEvents} = useTraceTimelineEvents({event});
  let paddedStartTime = startTimestamp;
  let paddedEndTime = endTimestamp;
  // Duration is 0, pad both sides, this is how we end up with 1 dot in the middle
  if (endTimestamp - startTimestamp === 0) {
    // If the duration is 0, we need to pad the end time
    paddedEndTime = startTimestamp + 1500;
    paddedStartTime = startTimestamp - 1500;
  }
  const durationMs = paddedEndTime - paddedStartTime;

  const totalColumns = Math.floor(width / PARENT_WIDTH);
  const eventsByColumn = useMemo(
    () => getEventsByColumn(traceEvents, durationMs, totalColumns, paddedStartTime),
    [durationMs, traceEvents, totalColumns, paddedStartTime]
  );

  const columnSize = width / totalColumns;

  // If the duration is less than 2 minutes, show seconds
  const showTimelineSeconds = durationMs < 120 * 1000;
  const middleTimestamp = paddedStartTime + Math.floor(durationMs / 2);
  const leftMiddleTimestamp = paddedStartTime + Math.floor(durationMs / 4);
  const rightMiddleTimestamp = paddedStartTime + Math.floor((durationMs / 4) * 3);

  return (
    <Fragment>
      {/* Add padding to the total columns, 1 column of padding on each side */}
      <TimelineColumns style={{gridTemplateColumns: `repeat(${totalColumns + 2}, 1fr)`}}>
        {eventsByColumn.map(([column, colEvents]) => {
          // Calculate the timestamp range that this column represents
          const timeRange = getChunkTimeRange(
            paddedStartTime,
            column - 1,
            durationMs / totalColumns
          );
          const hasCurrentEvent = colEvents.some(e => e.id === event.id);
          return (
            <EventColumn
              key={`${column}-${hasCurrentEvent ? 'current-event' : 'regular'}`}
              // Add 1 to the column to account for the padding
              style={{gridColumn: Math.floor(column) + 1, width: columnSize}}
            >
              <NodeGroup
                event={event}
                colEvents={colEvents}
                columnSize={columnSize}
                timeRange={timeRange}
                currentEventId={event.id}
                currentColumn={column}
              />
            </EventColumn>
          );
        })}
      </TimelineColumns>
      <TimestampColumns>
        <DateTime date={paddedStartTime} seconds={showTimelineSeconds} timeOnly />
        <DateTime date={leftMiddleTimestamp} seconds={showTimelineSeconds} timeOnly />
        <DateTime date={middleTimestamp} seconds={showTimelineSeconds} timeOnly />
        <DateTime date={rightMiddleTimestamp} seconds={showTimelineSeconds} timeOnly />
        <DateTime date={paddedEndTime} seconds={showTimelineSeconds} timeOnly />
      </TimestampColumns>
    </Fragment>
  );
}

/**
 * Use grid to create columns that we can place child nodes into.
 * Leveraging grid for alignment means we don't need to calculate percent offset
 * nor use position:absolute to lay out items.
 *
 * <Columns>
 *   <Col>...</Col>
 *   <Col>...</Col>
 * </Columns>
 */
const TimelineColumns = styled('div')`
  /* Reset defaults for <ul> */
  list-style: none;
  margin: 0;
  padding: 0;

  /* Layout of the lines */
  display: grid;
  margin-top: -1px;
  height: 0;
`;

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

function NodeGroup({
  event,
  timeRange,
  colEvents,
  columnSize,
  currentEventId,
  currentColumn,
}: {
  colEvents: TimelineEvent[];
  columnSize: number;
  currentColumn: number;
  currentEventId: string;
  event: Event;
  timeRange: [number, number];
}) {
  const totalSubColumns = Math.floor(columnSize / CHILD_WIDTH);
  const {eventsByColumn, columns} = useMemo(() => {
    const durationMs = timeRange[1] - timeRange[0];
    const eventColumns = getEventsByColumn(
      colEvents,
      durationMs,
      totalSubColumns,
      timeRange[0]
    );
    return {
      eventsByColumn: eventColumns,
      columns: eventColumns.map<number>(([column]) => column).sort(),
    };
  }, [colEvents, totalSubColumns, timeRange]);

  return (
    <Fragment>
      <TimelineColumns style={{gridTemplateColumns: `repeat(${totalSubColumns}, 1fr)`}}>
        {eventsByColumn.map(([column, groupEvents]) => {
          const isCurrentNode = groupEvents.some(e => e.id === currentEventId);
          return (
            <EventColumn key={`${column}-currrent-event`} style={{gridColumn: column}}>
              {isCurrentNode ? (
                <CurrentNodeContainer aria-label={t('Current Event')}>
                  <CurrentNodeRing />
                  <CurrentIconNode />
                </CurrentNodeContainer>
              ) : (
                groupEvents
                  .slice(0, 5)
                  .map(groupEvent =>
                    'event.type' in groupEvent ? (
                      <IconNode key={groupEvent.id} />
                    ) : (
                      <PerformanceIconNode key={groupEvent.id} />
                    )
                  )
              )}
            </EventColumn>
          );
        })}
      </TimelineColumns>
      <TimelineColumns style={{gridTemplateColumns: `repeat(${totalSubColumns}, 1fr)`}}>
        <Tooltip
          title={<TraceTimelineTooltip event={event} timelineEvents={colEvents} />}
          overlayStyle={{
            padding: `0 !important`,
          }}
          position="bottom"
          isHoverable
          skipWrapper
        >
          <TooltipHelper
            style={{
              gridColumn:
                columns.length > 1
                  ? `${columns.at(0)} / ${columns.at(-1)}`
                  : columns.at(0)!,
              width: 8 * columns.length,
            }}
            data-test-id={`trace-timeline-tooltip-${currentColumn}`}
          />
        </Tooltip>
      </TimelineColumns>
    </Fragment>
  );
}

const EventColumn = styled('div')`
  place-items: stretch;
  display: grid;
  align-items: center;
  position: relative;

  &:hover {
    z-index: ${p => p.theme.zIndex.initial};
  }
`;

const IconNode = styled('div')`
  position: absolute;
  grid-column: 1;
  grid-row: 1;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  color: ${p => p.theme.white};
  box-shadow: ${p => p.theme.dropShadowLight};
  user-select: none;
  background-color: ${p => color(p.theme.red200).alpha(0.3).string()};
  margin-left: -8px;
`;

const PerformanceIconNode = styled(IconNode)`
  background-color: unset;
  border: 1px solid ${p => p.theme.red300};
`;

const CurrentNodeContainer = styled('div')`
  position: absolute;
  grid-column: 1;
  grid-row: 1;
  width: 12px;
  height: 12px;
`;

const CurrentNodeRing = styled('div')`
  border: 1px solid ${p => p.theme.red300};
  height: 24px;
  width: 24px;
  border-radius: 100%;
  position: absolute;
  top: -6px;
  left: -16px;
  animation: pulse 2s ease-out infinite;

  @keyframes pulse {
    0% {
      transform: scale(0.1, 0.1);
      opacity: 0.0;
    }
    50% {
      transform: scale(0.1, 0.1);
      opacity: 0.0;
    }
    70% {
      opacity: 1.0;
    }
    100% {
      transform: scale(1.2, 1.2);
      opacity: 0.0;
    }
  }
`;

const CurrentIconNode = styled(IconNode)`
  background-color: ${p => p.theme.red300};
  width: 12px;
  height: 12px;
  margin-left: -10px;
`;

const TooltipHelper = styled('span')`
  height: 12px;
  margin-left: -8px;
  margin-top: -6px;
  z-index: 1;
`;
