import { css, cx } from '@emotion/css';
import { indexOf } from 'lodash';
import { Component } from 'react';
import { Unsubscribable } from 'rxjs';

import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { getTemplateSrv, RefreshEvent } from '@grafana/runtime';
import { Icon, TextLink, Themeable2, withTheme2 } from '@grafana/ui';
import { appEvents } from 'app/core/app_events';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/constants';
import grabDarkSvg from 'img/grab_dark.svg';
import grabLightSvg from 'img/grab_light.svg';

import { ShowConfirmModalEvent } from '../../../../types/events';
import { DashboardModel } from '../../state/DashboardModel';
import { PanelModel } from '../../state/PanelModel';
import { RowOptionsButton } from '../RowOptions/RowOptionsButton';

export interface DashboardRowProps extends Themeable2 {
  panel: PanelModel;
  dashboard: DashboardModel;
}

export class UnthemedDashboardRow extends Component<DashboardRowProps> {
  sub?: Unsubscribable;

  componentDidMount() {
    this.sub = this.props.dashboard.events.subscribe(RefreshEvent, this.onVariableUpdated);
  }

  componentWillUnmount() {
    if (this.sub) {
      this.sub.unsubscribe();
    }
  }

  onVariableUpdated = () => {
    this.forceUpdate();
  };

  onToggle = () => {
    this.props.dashboard.toggleRow(this.props.panel);
  };

  getWarning = () => {
    const panels = !!this.props.panel.panels?.length
      ? this.props.panel.panels
      : this.props.dashboard.getRowPanels(indexOf(this.props.dashboard.panels, this.props.panel));
    const isAnyPanelUsingDashboardDS = panels.some((p) => p.datasource?.uid === SHARED_DASHBOARD_QUERY);
    if (isAnyPanelUsingDashboardDS) {
      return (
        <div>
          <p>
            <Trans i18nKey="dashboard.untheme-dashboard-row.dashboard-datasource">
              Panels in this row use the {{ SHARED_DASHBOARD_QUERY }} data source. These panels will reference the panel
              in the original row, not the ones in the repeated rows.
            </Trans>
          </p>
          <TextLink
            external
            href={
              'https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/create-dashboard/#configure-repeating-rows'
            }
          >
            <Trans i18nKey="dashboard.unthemed-dashboard-row.learn-more">Learn more</Trans>
          </TextLink>
        </div>
      );
    }

    return undefined;
  };

  onUpdate = (title: string, repeat?: string | null) => {
    this.props.panel.setProperty('title', title);
    this.props.panel.setProperty('repeat', repeat ?? undefined);
    this.props.panel.render();
    this.props.dashboard.processRepeats();
    this.forceUpdate();
  };

  onDelete = () => {
    appEvents.publish(
      new ShowConfirmModalEvent({
        title: t('dashboard.unthemed-dashboard-row.title.delete-row', 'Delete row'),
        text: 'Are you sure you want to remove this row and all its panels?',
        altActionText: 'Delete row only',
        icon: 'trash-alt',
        onConfirm: () => {
          this.props.dashboard.removeRow(this.props.panel, true);
        },
        onAltAction: () => {
          this.props.dashboard.removeRow(this.props.panel, false);
        },
      })
    );
  };

  render() {
    const title = getTemplateSrv().replace(this.props.panel.title, this.props.panel.scopedVars, 'text');
    const count = this.props.panel.panels ? this.props.panel.panels.length : 0;
    const panels = count === 1 ? 'panel' : 'panels';
    const canEdit = this.props.dashboard.meta.canEdit === true;
    const collapsed = this.props.panel.collapsed;
    const styles = getStyles(this.props.theme);

    return (
      <div
        className={cx(styles.dashboardRow, {
          [styles.dashboardRowCollapsed]: collapsed,
        })}
        data-testid="dashboard-row-container"
      >
        <button
          aria-expanded={!collapsed}
          className={cx(styles.title, 'pointer')}
          type="button"
          data-testid={selectors.components.DashboardRow.title(title)}
          onClick={this.onToggle}
        >
          <Icon name={collapsed ? 'angle-right' : 'angle-down'} />
          {title}
          <span
            className={cx(styles.count, {
              [styles.countCollapsed]: collapsed,
            })}
          >
            ({count} {panels})
          </span>
        </button>
        {canEdit && (
          <div className={styles.actions}>
            <RowOptionsButton
              title={this.props.panel.title}
              repeat={this.props.panel.repeat}
              onUpdate={this.onUpdate}
              warning={this.getWarning()}
            />
            <button
              type="button"
              className="pointer"
              onClick={() => {
                DashboardInteractions.trackRemoveRowClick();
                this.onDelete();
              }}
              aria-label={t('dashboard.unthemed-dashboard-row.aria-label-delete-row', 'Delete row')}
            >
              <Icon name="trash-alt" />
            </button>
          </div>
        )}
        {collapsed === true && (
          /* disabling the a11y rules here as the button handles keyboard interactions */
          /* this is just to provide a better experience for mouse users */
          /* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
          <div
            className={cx({
              [styles.toggleTargetCollapsed]: collapsed,
            })}
            onClick={this.onToggle}
          >
            &nbsp;
          </div>
        )}
        {canEdit && (
          <div
            data-testid="dashboard-row-drag"
            className={cx(styles.dragHandle, 'grid-drag-handle', {
              [styles.dragHandleCollapsed]: collapsed,
            })}
          />
        )}
      </div>
    );
  }
}

export const DashboardRow = withTheme2(UnthemedDashboardRow);

const getStyles = (theme: GrafanaTheme2) => {
  const dragHandle = theme.name === 'dark' ? grabDarkSvg : grabLightSvg;
  const actions = css({
    color: theme.colors.text.secondary,
    opacity: 0,
    [theme.transitions.handleMotion('no-preference', 'reduce')]: {
      transition: '200ms opacity ease-in 200ms',
    },

    button: {
      color: theme.colors.text.secondary,
      paddingLeft: theme.spacing(2),
      background: 'transparent',
      border: 'none',

      '&:hover': {
        color: theme.colors.text.maxContrast,
      },
    },
  });

  return {
    dashboardRow: css({
      display: 'flex',
      alignItems: 'center',
      height: '100%',

      '&:hover, &:focus-within': {
        [`.${actions}`]: {
          opacity: 1,
        },
      },
    }),
    dashboardRowCollapsed: css({
      background: theme.components.panel.background,
    }),
    toggleTargetCollapsed: css({
      flex: 1,
      cursor: 'pointer',
      marginRight: '15px',
    }),
    title: css({
      flexGrow: 0,
      fontSize: theme.typography.h5.fontSize,
      fontWeight: theme.typography.fontWeightMedium,
      color: theme.colors.text.primary,
      background: 'transparent',
      border: 'none',

      '.fa': {
        color: theme.colors.text.secondary,
        fontSize: theme.typography.size.xs,
        padding: theme.spacing(0, 1),
      },
    }),
    actions,
    count: css({
      paddingLeft: theme.spacing(2),
      color: theme.colors.text.secondary,
      fontStyle: 'italic',
      fontSize: theme.typography.size.sm,
      fontWeight: 'normal',
      display: 'none',
    }),
    countCollapsed: css({
      display: 'inline-block',
    }),
    dragHandle: css({
      cursor: 'move',
      width: '16px',
      height: '100%',
      background: `url("${dragHandle}") no-repeat 50% 50%`,
      backgroundSize: '8px',
      visibility: 'hidden',
      position: 'absolute',
      top: 0,
      right: 0,
    }),
    dragHandleCollapsed: css({
      visibility: 'visible',
      opacity: 1,
    }),
  };
};
