import { css } from '@emotion/css';
import { useState } from 'react';
import { useDebounce, useDeepCompareEffect } from 'react-use';

import { AlertLabels } from '@grafana/alerting/unstable';
import { type GrafanaTheme2, dateTime } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { Alert, Badge, Icon, LoadingPlaceholder, Tooltip, useStyles2 } from '@grafana/ui';
import { type MatcherFieldValue } from 'app/features/alerting/unified/types/silence-form';
import { matcherFieldToMatcher } from 'app/features/alerting/unified/utils/alertmanager';
import { MATCHER_ALERT_RULE_UID } from 'app/features/alerting/unified/utils/constants';
import { type AlertmanagerAlert, type Matcher, MatcherOperator } from 'app/plugins/datasource/alertmanager/types';

import { alertmanagerApi } from '../../api/alertmanagerApi';
import { isNullDate } from '../../utils/time';
import { DynamicTable, type DynamicTableColumnProps, type DynamicTableItemProps } from '../DynamicTable';

import { AmAlertStateTag } from './AmAlertStateTag';

interface Props {
  amSourceName: string;
  matchers: MatcherFieldValue[];
  ruleUid?: string;
}

/**
 * Performs a deep equality check on the dependencies, and debounces the callback
 */
const useDebouncedDeepCompare = (cb: () => void, debounceMs: number, dependencies: unknown[]) => {
  const [state, setState] = useState<unknown[]>();

  useDebounce(cb, debounceMs, [state]);

  useDeepCompareEffect(() => {
    setState(dependencies);
  }, [dependencies]);
};

export const SilencedInstancesPreview = ({ amSourceName, matchers: inputMatchers, ruleUid }: Props) => {
  const matchers: Matcher[] = [
    ...(ruleUid ? [{ name: MATCHER_ALERT_RULE_UID, value: ruleUid, operator: MatcherOperator.equal }] : []),
    ...inputMatchers,
  ].map(matcherFieldToMatcher);
  const useLazyQuery = alertmanagerApi.endpoints.getAlertmanagerAlerts.useLazyQuery;
  const styles = useStyles2(getStyles);
  const columns = useColumns();

  // By default the form contains an empty matcher - with empty name and value and = operator
  // We don't want to fetch previews for empty matchers as it results in all alerts returned
  const hasValidMatchers = ruleUid || inputMatchers.some((matcher) => matcher.value && matcher.name);

  const [getAlertmanagerAlerts, { currentData: alerts = [], isFetching, isError }] = useLazyQuery();

  // We need to deep compare the matchers, as otherwise the preview API call is triggered on every render
  // of the component. This is because between react-hook-form's useFieldArray, and our parsing of the matchers,
  // we end up otherwise triggering the call too frequently
  useDebouncedDeepCompare(
    () => {
      if (hasValidMatchers) {
        getAlertmanagerAlerts({ amSourceName, filter: { matchers } });
      }
    },
    500,
    [amSourceName, matchers]
  );

  if (isError) {
    return (
      <Alert
        title={t('alerting.silenced-instances-preview.title-preview-not-available', 'Preview not available')}
        severity="error"
      >
        <Trans i18nKey="alerting.silenced-instances-preview.error-generating-preview">
          Error occurred when generating preview of affected alerts. Are your matchers valid?
        </Trans>
      </Alert>
    );
  }

  const tableItemAlerts = alerts.map<DynamicTableItemProps<AlertmanagerAlert>>((alert) => ({
    id: alert.fingerprint,
    data: alert,
  }));

  return (
    <div>
      <h4 className={styles.title}>
        <Trans i18nKey="alerting.silences.affected-instances">Affected alert instances</Trans>
        <Tooltip
          content={
            <div>
              <Trans i18nKey="alerting.silences.preview-affected-instances">
                Preview the alert instances affected by this silence.
              </Trans>
              <br />
              <Trans i18nKey="alerting.silences.only-firing-instances">
                Only alert instances in the firing state are displayed.
              </Trans>
            </div>
          }
        >
          <span>
            &nbsp;
            <Icon name="info-circle" size="sm" />
          </span>
        </Tooltip>
        {tableItemAlerts.length > 0 ? (
          <Badge className={styles.badge} color="blue" text={tableItemAlerts.length} />
        ) : null}
      </h4>
      {!hasValidMatchers && (
        <span>
          <Trans i18nKey="alerting.silenced-instances-preview.valid-matcher-affected-alerts">
            Add a valid matcher to see affected alerts
          </Trans>
        </span>
      )}

      {isFetching && (
        <LoadingPlaceholder
          text={t(
            'alerting.silenced-instances-preview.text-loading-affected-alert-rule-instances',
            'Loading affected alert rule instances...'
          )}
        />
      )}
      {!isFetching && !isError && hasValidMatchers && (
        <div className={styles.table}>
          {tableItemAlerts.length > 0 ? (
            <DynamicTable
              items={tableItemAlerts}
              isExpandable={false}
              cols={columns}
              pagination={{ itemsPerPage: 10 }}
            />
          ) : (
            <span>
              <Trans i18nKey="alerting.silenced-instances-preview.no-firing-alert-instances-found">
                No firing alert instances found
              </Trans>
            </span>
          )}
        </div>
      )}
    </div>
  );
};

function useColumns(): Array<DynamicTableColumnProps<AlertmanagerAlert>> {
  const styles = useStyles2(getStyles);

  return [
    {
      id: 'state',
      label: t('alerting.use-columns.label.state', 'State'),
      renderCell: function renderStateTag({ data }) {
        return <AmAlertStateTag state={data.status.state} />;
      },
      size: '120px',
      className: styles.stateColumn,
    },
    {
      id: 'labels',
      label: t('alerting.use-columns.label.labels', 'Labels'),
      renderCell: function renderName({ data }) {
        return <AlertLabels labels={data.labels} size="sm" />;
      },
      size: 'auto',
    },
    {
      id: 'created',
      label: t('alerting.use-columns.label.created', 'Created'),
      renderCell: function renderSummary({ data }) {
        return <>{isNullDate(data.startsAt) ? '-' : dateTime(data.startsAt).format('YYYY-MM-DD HH:mm:ss')}</>;
      },
      size: '180px',
    },
  ];
}

const getStyles = (theme: GrafanaTheme2) => ({
  table: css({
    maxWidth: `${theme.breakpoints.values.lg}px`,
  }),
  moreMatches: css({
    marginTop: theme.spacing(1),
  }),
  title: css({
    display: 'flex',
    alignItems: 'center',
  }),
  badge: css({
    marginLeft: theme.spacing(1),
  }),
  stateColumn: css({
    display: 'flex',
    alignItems: 'center',
  }),
});
