import { css, cx } from '@emotion/css';
import { Draggable } from '@hello-pangea/dnd';
import { useCallback, useState } from 'react';

import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t } from '@grafana/i18n';
import { SceneComponentProps } from '@grafana/scenes';
import { clearButtonStyles, Icon, Tooltip, useElementSelection, usePointerDistance, useStyles2 } from '@grafana/ui';

import { useIsConditionallyHidden } from '../../conditional-rendering/hooks/useIsConditionallyHidden';
import { isRepeatCloneOrChildOf } from '../../utils/clone';
import { useDashboardState, useInterpolatedTitle } from '../../utils/utils';
import { DashboardScene } from '../DashboardScene';
import { useSoloPanelContext } from '../SoloPanelContext';
import { DASHBOARD_DROP_TARGET_KEY_ATTR } from '../types/DashboardDropTarget';
import { isDashboardLayoutGrid } from '../types/DashboardLayoutGrid';

import { RowItem } from './RowItem';

export function RowItemRenderer({ model }: SceneComponentProps<RowItem>) {
  const { layout, collapse, fillScreen, hideHeader: isHeaderHidden, isDropTarget, key } = model.useState();
  const isCollapsed = collapse && !isHeaderHidden; // never allow a row without a header to be collapsed
  const isClone = isRepeatCloneOrChildOf(model);
  const { isEditing } = useDashboardState(model);
  const [isConditionallyHidden, conditionalRenderingClass, conditionalRenderingOverlay] = useIsConditionallyHidden(
    model.state.conditionalRendering
  );
  const { isSelected, onSelect, isSelectable, onClear: onClearSelection } = useElementSelection(key);
  const title = useInterpolatedTitle(model);
  const { rows } = model.getParentLayout().useState();
  const styles = useStyles2(getStyles);
  const clearStyles = useStyles2(clearButtonStyles);
  const isTopLevel = model.parent?.parent instanceof DashboardScene;
  const pointerDistance = usePointerDistance();
  const soloPanelContext = useSoloPanelContext();

  const myIndex = rows.findIndex((row) => row === model);

  const shouldGrow = !isCollapsed && fillScreen;
  const isHidden = isConditionallyHidden && !isEditing;

  // Highlight the full row when hovering over header
  const [selectableHighlight, setSelectableHighlight] = useState(false);
  const onHeaderEnter = useCallback(() => setSelectableHighlight(true), []);
  const onHeaderLeave = useCallback(() => setSelectableHighlight(false), []);

  const isDraggable = !isClone && isEditing;

  if (isHidden) {
    return null;
  }

  if (soloPanelContext) {
    return <layout.Component model={layout} />;
  }

  const titleElement = (
    <span
      className={cx(
        styles.rowTitle,
        isHeaderHidden && styles.rowTitleHidden,
        !isTopLevel && styles.rowTitleNested,
        isCollapsed && styles.rowTitleCollapsed
      )}
    >
      {!model.hasUniqueTitle() && (
        <Tooltip content={t('dashboard.rows-layout.row-warning.title-not-unique', 'This title is not unique')}>
          <Icon name="exclamation-triangle" />
        </Tooltip>
      )}
      {title}
      {isHeaderHidden && (
        <Tooltip content={t('dashboard.rows-layout.header-hidden-tooltip', 'Row header only visible in edit mode')}>
          <Icon name="eye-slash" />
        </Tooltip>
      )}
    </span>
  );

  return (
    <Draggable key={key!} draggableId={key!} index={myIndex} isDragDisabled={!isDraggable}>
      {(dragProvided, dragSnapshot) => (
        <div
          ref={(ref) => {
            dragProvided.innerRef(ref);
            model.containerRef.current = ref;
          }}
          {...{ [DASHBOARD_DROP_TARGET_KEY_ATTR]: isDashboardLayoutGrid(layout) ? model.state.key : undefined }}
          className={cx(
            styles.wrapper,
            'dashboard-row-wrapper',
            !isCollapsed && styles.wrapperNotCollapsed,
            dragSnapshot.isDragging && styles.dragging,
            isCollapsed && styles.wrapperCollapsed,
            shouldGrow && styles.wrapperGrow,
            conditionalRenderingClass,
            !isClone && isSelected && 'dashboard-selected-element',
            !isClone && !isSelected && selectableHighlight && 'dashboard-selectable-element',
            isDropTarget && 'dashboard-drop-target'
          )}
          onPointerDown={(evt) => {
            evt.stopPropagation();
            pointerDistance.set(evt);
          }}
          onPointerUp={(evt) => {
            // If we selected and are clicking a button inside row header then don't de-select row
            if (evt.target instanceof Element && evt.target.closest('button')) {
              // Stop propagation otherwise dashboaed level onPointerDown will de-select row
              evt.stopPropagation();
              return;
            }

            if (pointerDistance.check(evt)) {
              return;
            }

            setTimeout(() => onSelect?.(evt));
          }}
          data-testid={selectors.components.DashboardRow.wrapper(title!)}
          {...dragProvided.draggableProps}
        >
          {(!isHeaderHidden || isEditing) && (
            <div
              className={cx(styles.rowHeader, 'dashboard-row-header')}
              onMouseEnter={isSelectable ? onHeaderEnter : undefined}
              onMouseLeave={isSelectable ? onHeaderLeave : undefined}
              {...dragProvided.dragHandleProps}
            >
              <button
                onClick={(evt) => {
                  model.onCollapseToggle();
                  onClearSelection?.();
                }}
                className={cx(clearStyles, styles.rowTitleButton)}
                aria-label={
                  isCollapsed
                    ? t('dashboard.rows-layout.row.expand', 'Expand row')
                    : t('dashboard.rows-layout.row.collapse', 'Collapse row')
                }
                data-testid={selectors.components.DashboardRow.title(title!)}
              >
                <Icon name={isCollapsed ? 'angle-right' : 'angle-down'} />
                {!isEditing && titleElement}
              </button>
              {isEditing && titleElement}
              {isDraggable && <Icon name="draggabledots" className="dashboard-row-header-drag-handle" />}
            </div>
          )}
          {!isCollapsed && <layout.Component model={layout} />}
          {conditionalRenderingOverlay}
        </div>
      )}
    </Draggable>
  );
}

function getStyles(theme: GrafanaTheme2) {
  return {
    rowHeader: css({
      display: 'flex',
      gap: theme.spacing(1),
      padding: theme.spacing(0.5, 0.5, 0.5, 0),
      alignItems: 'center',
      justifyContent: 'space-between',
      marginBottom: theme.spacing(1),

      '& .dashboard-row-header-drag-handle': css({
        opacity: 0,

        [theme.transitions.handleMotion('no-preference', 'reduce')]: {
          transition: 'opacity 0.25s',
        },
      }),

      '&:hover': css({
        '& .dashboard-row-header-drag-handle': css({
          opacity: 1,
        }),
      }),
    }),
    rowTitleButton: css({
      display: 'flex',
      alignItems: 'center',
      cursor: 'pointer',
      background: 'transparent',
      border: 'none',
      minWidth: 0,
      gap: theme.spacing(1),
    }),
    rowTitle: css({
      display: 'flex',
      alignItems: 'center',
      gap: theme.spacing(2),
      ...theme.typography.h5,
      fontWeight: theme.typography.fontWeightMedium,
      whiteSpace: 'nowrap',
      overflow: 'hidden',
      textOverflow: 'ellipsis',
      maxWidth: '100%',
      flexGrow: 1,
      minWidth: 0,
    }),
    rowTitleHidden: css({
      textDecoration: 'line-through',
      opacity: 0.6,

      '&:hover': css({
        opacity: 1,
      }),
    }),
    rowTitleNested: css({
      fontSize: theme.typography.body.fontSize,
      fontWeight: theme.typography.fontWeightRegular,
    }),
    rowTitleCollapsed: css({
      color: theme.colors.text.secondary,
    }),
    wrapper: css({
      display: 'flex',
      flexDirection: 'column',
      // Without this min height, the custom grid (SceneGridLayout) wont render
      // should be 1px more than row header + padding + margin
      // consist of lineHeight + paddingBlock + margin + 0.125 = 39px
      minHeight: theme.spacing(2.75 + 1 + 1 + 0.125),

      // Show grid controls when hovering anywhere on the row
      '&:hover .dashboard-canvas-controls': {
        opacity: 1,
      },
      // But hide controls inside nested rows (they'll show when that row is hovered)
      '&:hover .dashboard-row-wrapper .dashboard-canvas-controls': {
        opacity: 0,
      },
      // Re-enable for the specific nested row being hovered
      '&:hover .dashboard-row-wrapper:hover .dashboard-canvas-controls': {
        opacity: 1,
      },
    }),
    wrapperNotCollapsed: css({
      '> div:nth-child(2)': {
        marginLeft: theme.spacing(3),
        position: 'relative',
        width: 'auto',

        '&:before': {
          content: '""',
          position: 'absolute',
          top: `-8px`,
          bottom: 0,
          left: '-16px',
          width: '1px',
          backgroundColor: theme.colors.border.weak,
        },
      },
    }),
    dragging: css({
      cursor: 'move',
      backgroundColor: theme.colors.background.canvas,
    }),
    wrapperGrow: css({
      flexGrow: 1,
    }),
    wrapperCollapsed: css({
      flexGrow: 0,
      borderBottom: `1px solid ${theme.colors.border.weak}`,
      minHeight: 'unset',

      '.dashboard-row-header': {
        marginBottom: theme.spacing(0),
      },
    }),
    rowActions: css({
      display: 'flex',
      opacity: 0,
    }),
    checkboxWrapper: css({
      display: 'flex',
      alignItems: 'center',
      paddingLeft: theme.spacing(1),
    }),
  };
}
