import { css } from '@emotion/css';
import { Fragment, Suspense, lazy } from 'react';
import { useEffectOnce } from 'react-use';

import { type GrafanaTheme2 } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { Alert, Button, LoadingPlaceholder, Stack, Text, Tooltip, useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/services/context_srv';
import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
import { AccessControlAction } from 'app/types/accessControl';
import { type AlertQuery, type Labels } from 'app/types/unified-alerting-dto';

import { type Folder, type KBObjectArray } from '../../../types/rule-form';
import { useGetAlertManagerDataSourcesByPermissionAndConfig } from '../../../utils/datasource';

const NotificationPreviewByAlertManager = lazy(() => import('./NotificationPreviewByAlertManager'));
const NotificationPreviewForGrafanaManaged = lazy(() => import('./NotificationPreviewGrafanaManaged'));

interface NotificationPreviewProps {
  customLabels: KBObjectArray;
  alertQueries: AlertQuery[];
  condition: string | null;
  folder?: Folder;
  alertName?: string;
  alertUid?: string;
  policyName?: string;
}

const { preview } = alertRuleApi.endpoints;

// TODO the scroll position keeps resetting when we preview
// this is to be expected because the list of routes dissapears as we start the request but is very annoying
export const NotificationPreview = ({
  alertQueries,
  customLabels,
  condition,
  folder,
  alertName,
  alertUid,
  policyName,
}: NotificationPreviewProps) => {
  const styles = useStyles2(getStyles);

  const previewRoutingDisabled = !condition || !folder;

  const [trigger, { data = [], isLoading, isUninitialized: previewUninitialized }] = preview.useMutation();

  // potential instances are the instances that are going to be routed to the notification policies
  // convert data to list of labels: are the representation of the potential instances
  const potentialInstances = data.reduce<Labels[]>((acc = [], instance) => {
    if (instance.labels) {
      acc.push(instance.labels);
    }

    return acc;
  }, []);

  const onPreview = () => {
    if (previewRoutingDisabled) {
      return;
    }

    // Get the potential labels given the alert queries, the condition and the custom labels (autogenerated labels are calculated on the BE side)
    trigger({
      alertQueries: alertQueries,
      condition: condition,
      customLabels: customLabels,
      folder: folder,
      alertName: alertName,
      alertUid: alertUid,
    });
  };

  useEffectOnce(() => {
    onPreview();
  });

  //  Get alert managers's data source information
  const alertManagerDataSources = useGetAlertManagerDataSourcesByPermissionAndConfig('notification');
  const singleAlertManagerConfigured = alertManagerDataSources.length === 1;

  const getTooltipContent = () => {
    if (!condition) {
      return (
        <Trans i18nKey="alerting.notification-preview.no-condition-tooltip">
          Select a query condition to preview routing
        </Trans>
      );
    }
    if (!folder) {
      return (
        <Trans i18nKey="alerting.notification-preview.select-folder-tooltip">Select a folder to preview routing</Trans>
      );
    }
    return '';
  };

  return (
    <Stack direction="column">
      <Stack direction="row" alignItems="flex-start" justifyContent="space-between">
        <Stack direction="column" gap={1}>
          <Text element="h5">
            <Trans i18nKey="alerting.notification-preview.title">Alert instance routing preview</Trans>
          </Text>
          {isLoading && previewUninitialized && (
            <Text color="secondary" variant="bodySmall">
              <Trans i18nKey="alerting.common.loading">Loading...</Trans>
            </Text>
          )}
          {previewUninitialized ? (
            <Text color="secondary" variant="bodySmall">
              <Trans i18nKey="alerting.notification-preview.uninitialized">
                When you have your folder selected and your query and labels are configured, click &quot;Preview
                routing&quot; to see the results here.
              </Trans>
            </Text>
          ) : (
            <Text color="secondary" variant="bodySmall">
              <Trans i18nKey="alerting.notification-preview.initialized">
                Based on the labels added, alert instances are routed to the following notification policies. Expand
                each notification policy below to view more details.
              </Trans>
            </Text>
          )}
        </Stack>
        <Tooltip content={getTooltipContent()}>
          <Button icon="sync" variant="secondary" type="button" onClick={onPreview} disabled={previewRoutingDisabled}>
            <Trans i18nKey="alerting.notification-preview.preview-routing">Preview routing</Trans>
          </Button>
        </Tooltip>
      </Stack>
      {potentialInstances.length > 0 && (
        <Suspense
          fallback={
            <LoadingPlaceholder text={t('alerting.notification-preview.text-loading-preview', 'Loading preview...')} />
          }
        >
          {alertManagerDataSources.map((alertManagerSource) => (
            <Fragment key={alertManagerSource.name}>
              {!singleAlertManagerConfigured && (
                <Stack direction="row" alignItems="center">
                  <div className={styles.firstAlertManagerLine} />
                  <div className={styles.alertManagerName}>
                    <Trans i18nKey="alerting.notification-preview.alertmanager">Alertmanager:</Trans>
                    <img src={alertManagerSource.imgUrl || undefined} alt="" className={styles.img} />
                    {alertManagerSource.name}
                  </div>
                  <div className={styles.secondAlertManagerLine} />
                </Stack>
              )}
              {alertManagerSource.name === 'grafana' ? (
                <NotificationPreviewGrafanaPermissionCheck>
                  <NotificationPreviewForGrafanaManaged
                    alertManagerSource={alertManagerSource}
                    instances={potentialInstances}
                    policyName={policyName}
                  />
                </NotificationPreviewGrafanaPermissionCheck>
              ) : (
                <NotificationPreviewByAlertManager
                  alertManagerSource={alertManagerSource}
                  instances={potentialInstances}
                  policyName={policyName}
                />
              )}
            </Fragment>
          ))}
        </Suspense>
      )}
    </Stack>
  );
};

/**
 * Permission check for Grafana notification preview.
 * This is a workaround because useGetAlertManagerDataSourcesByPermissionAndConfig
 * doesn't properly filter by the new K8s-style RBAC permissions.
 *
 * We check for either:
 * - alert.notifications:read (legacy permission)
 * - alert.notifications.routes:read (new granular permission)
 */
function NotificationPreviewGrafanaPermissionCheck({ children }: React.PropsWithChildren) {
  const hasLegacyNotificationPermission = contextSrv.hasPermission(AccessControlAction.AlertingNotificationsRead);
  const hasNotificationPolicyTreePermission =
    contextSrv.hasPermission(AccessControlAction.AlertingRoutesRead) ||
    contextSrv.hasPermission(AccessControlAction.ActionAlertingManagedRoutesRead);

  if (hasLegacyNotificationPermission || hasNotificationPolicyTreePermission) {
    return <>{children}</>;
  }

  return (
    <Alert severity="warning" title={t('alerting.notification-preview.permission-warning', 'Preview not available')}>
      <Trans i18nKey="alerting.notification-preview.permission-warning-message">
        You don&apos;t have permission to view notification policies. Preview is not available.
      </Trans>
    </Alert>
  );
}

const getStyles = (theme: GrafanaTheme2) => ({
  firstAlertManagerLine: css({
    height: '1px',
    width: theme.spacing(4),
    backgroundColor: theme.colors.secondary.main,
  }),
  alertManagerName: css({
    width: 'fit-content',
  }),
  secondAlertManagerLine: css({
    height: '1px',
    width: '100%',
    flex: 1,
    backgroundColor: theme.colors.secondary.main,
  }),
  img: css({
    marginLeft: theme.spacing(2),
    width: theme.spacing(3),
    height: theme.spacing(3),
    marginRight: theme.spacing(1),
  }),
});
