import { css } from '@emotion/css';
import { orderBy } from 'lodash';
import { Fragment, useMemo } from 'react';
import { useMeasure } from 'react-use';

import { type GrafanaTheme2, type Labels } from '@grafana/data';
import { t } from '@grafana/i18n';
import { config, isFetchError } from '@grafana/runtime';
import { TimeRangePicker, useTimeRange } from '@grafana/scenes-react';
import {
  Alert,
  Box,
  Drawer,
  Icon,
  LoadingBar,
  LoadingPlaceholder,
  Stack,
  Text,
  TextLink,
  useStyles2,
} from '@grafana/ui';
import { type AlertQuery, type GrafanaRuleDefinition } from 'app/types/unified-alerting-dto';

import { alertRuleApi } from '../../api/alertRuleApi';
import { stateHistoryApi } from '../../api/stateHistoryApi';
import { getThresholdsForQueries } from '../../components/rule-editor/util';
import { EventState } from '../../components/rules/central-state-history/EventListSceneObject';
import { type LogRecord, historyDataFrameToLogRecords } from '../../components/rules/state-history/common';
import { isAlertQueryOfAlertData } from '../../rule-editor/formProcessing';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { labelsToMatchersParam } from '../../utils/matchers';
import { stringifyErrorLike } from '../../utils/misc';
import { groups, rulesNav } from '../../utils/navigation';
import { useWorkbenchContext } from '../WorkbenchContext';

import { DrawerTimeRangeInfoBanner } from './DrawerTimeRangeInfoBanner';
import { InstanceDetailsDrawerTitle } from './InstanceDetailsDrawerTitle';
import { InstanceStateInfoBanner } from './InstanceStateInfoBanner';
import { InstanceTimelineSection } from './InstanceTimelineSection';
import { QueryVisualization } from './QueryVisualization';
import { isDrawerRangeShorterThanQuery } from './drawerTimeRangeUtils';
import { useInstanceAlertState } from './instanceStateUtils';
import { convertStateHistoryToAnnotations } from './stateHistoryUtils';
import { formatTimelineDate, noop } from './timelineUtils';

const { useGetAlertRuleQuery } = alertRuleApi;
const { useGetRuleHistoryQuery } = stateHistoryApi;

function calculateDrawerWidth(rightColumnWidth: number): number {
  const calculatedWidth = rightColumnWidth + 32;
  return Math.max(700, Math.min(calculatedWidth, 1400));
}

interface InstanceDetailsDrawerProps {
  ruleUID: string;
  instanceLabels: Labels;
  onClose: () => void;
}

export function InstanceDetailsDrawer({ ruleUID, instanceLabels, onClose }: InstanceDetailsDrawerProps) {
  const [ref, { width: loadingBarWidth }] = useMeasure<HTMLDivElement>();
  const [timeRange] = useTimeRange();
  const { rightColumnWidth } = useWorkbenchContext();

  const drawerWidth = calculateDrawerWidth(rightColumnWidth);

  const { data: rule, isLoading: loading, error } = useGetAlertRuleQuery({ uid: ruleUID });

  const { dataQueries, thresholds } = useMemo(() => {
    if (rule) {
      return extractQueryDetails(rule.grafana_alert);
    }
    return { dataQueries: [], thresholds: {} };
  }, [rule]);

  // Fetch state history for this specific instance
  const {
    data: stateHistoryData,
    isFetching: stateHistoryFetching,
    isError: stateHistoryError,
  } = useGetRuleHistoryQuery({
    ruleUid: ruleUID,
    matchers: labelsToMatchersParam(instanceLabels),
    from: timeRange.from.unix(),
    to: timeRange.to.unix(),
  });

  // Convert state history to LogRecords and filter by instance labels
  const { historyRecords, annotations } = useMemo(() => {
    const historyRecords = historyDataFrameToLogRecords(stateHistoryData);
    const annotations = convertStateHistoryToAnnotations(historyRecords);

    return { historyRecords, annotations };
  }, [stateHistoryData]);

  const instanceState = useInstanceAlertState(ruleUID, instanceLabels);

  const showInstanceTimeline =
    config.featureToggles.alertingNotificationHistoryTriage && config.featureToggles.kubernetesAlertingHistorian;

  const showDrawerTimeRangeBanner = useMemo(() => {
    if (!rule?.grafana_alert) {
      return false;
    }
    return isDrawerRangeShorterThanQuery(rule.grafana_alert, timeRange);
  }, [rule, timeRange]);

  if (error) {
    return (
      <Drawer
        title={<InstanceDetailsDrawerTitle instanceLabels={instanceLabels} />}
        onClose={onClose}
        width={drawerWidth}
      >
        <ErrorContent error={error} />
      </Drawer>
    );
  }

  if (loading || !rule) {
    return (
      <Drawer
        title={<InstanceDetailsDrawerTitle instanceLabels={instanceLabels} />}
        onClose={onClose}
        width={drawerWidth}
      >
        <LoadingPlaceholder text={t('alerting.common.loading', 'Loading...')} />
      </Drawer>
    );
  }

  return (
    <Drawer
      title={<InstanceDetailsDrawerTitle instanceLabels={instanceLabels} rule={rule.grafana_alert} />}
      onClose={onClose}
      width={drawerWidth}
    >
      <Stack direction="column" gap={3}>
        <Stack justifyContent="flex-end">
          <TimeRangePicker />
        </Stack>
        {showDrawerTimeRangeBanner && !instanceState && <DrawerTimeRangeInfoBanner />}
        {instanceState && <InstanceStateInfoBanner state={instanceState} />}
        {dataQueries.length > 0 && (
          <Box>
            <Stack direction="column" gap={2}>
              {dataQueries.map((query, index) => (
                <QueryVisualization
                  key={query.refId ?? `query-${index}`}
                  query={query}
                  instanceLabels={instanceLabels}
                  thresholds={thresholds}
                  annotations={annotations}
                />
              ))}
            </Stack>
          </Box>
        )}

        {showInstanceTimeline ? (
          <InstanceTimelineSection
            ruleUID={ruleUID}
            instanceLabels={instanceLabels}
            timeRange={timeRange}
            historyRecords={historyRecords}
            stateHistoryFetching={stateHistoryFetching}
            stateHistoryError={stateHistoryError}
            loadingBarRef={ref}
          />
        ) : (
          <Box ref={ref}>
            <Text variant="h5">{t('alerting.instance-details.state-history', 'Recent State Changes')}</Text>
            {stateHistoryFetching && <LoadingBar width={loadingBarWidth} />}
            {stateHistoryError && (
              <Alert
                severity="error"
                title={t('alerting.instance-details.history-error', 'Failed to load state history')}
              >
                {t(
                  'alerting.instance-details.history-error-desc',
                  'Unable to fetch state transition history for this instance.'
                )}
              </Alert>
            )}
            {!stateHistoryFetching && !stateHistoryError && (
              <Stack direction="column" gap={1}>
                {historyRecords.length > 0 ? (
                  <InstanceStateTransitions records={historyRecords} maxItems={10} />
                ) : (
                  <Text color="secondary">{t('alerting.instance-details.no-history', 'No recent state changes')}</Text>
                )}
              </Stack>
            )}
          </Box>
        )}
      </Stack>
    </Drawer>
  );
}

export interface InstanceLocationProps {
  folderTitle: string;
  groupName: string;
  ruleName: string;
  namespaceUid?: string;
  ruleUid?: string;
}

export function InstanceLocation({ folderTitle, groupName, ruleName, namespaceUid, ruleUid }: InstanceLocationProps) {
  const groupUrl =
    namespaceUid != null ? groups.detailsPageLink(GRAFANA_RULES_SOURCE_NAME, namespaceUid, groupName) : undefined;
  const ruleViewUrl =
    ruleUid != null
      ? rulesNav.detailsPageLink(GRAFANA_RULES_SOURCE_NAME, { uid: ruleUid, ruleSourceName: 'grafana' })
      : undefined;

  return (
    <Stack direction="row" alignItems="center" gap={1}>
      <Icon size="xs" name="folder" />
      <Stack direction="row" alignItems="center" gap={0.5}>
        <Text variant="bodySmall">{folderTitle}</Text>
        <Icon size="sm" name="angle-right" />
        {groupUrl ? (
          <TextLink href={groupUrl} variant="bodySmall" color="primary" inline={false}>
            {groupName}
          </TextLink>
        ) : (
          <Text variant="bodySmall">{groupName}</Text>
        )}
        <Icon size="sm" name="angle-right" />
        {ruleViewUrl ? (
          <TextLink href={ruleViewUrl} variant="bodySmall" color="primary" inline={false}>
            {ruleName}
          </TextLink>
        ) : (
          <Text variant="bodySmall">{ruleName}</Text>
        )}
      </Stack>
    </Stack>
  );
}

function extractQueryDetails(rule: GrafanaRuleDefinition) {
  const dataQueries = rule.data.filter((query: AlertQuery) => isAlertQueryOfAlertData(query));

  const allQueries = rule.data;
  const condition = rule.condition;

  const thresholds = getThresholdsForQueries(allQueries, condition);

  return { dataQueries, thresholds };
}

const MAX_STATE_TRANSITIONS = 10;

function InstanceStateTransitions({
  records,
  maxItems = MAX_STATE_TRANSITIONS,
}: {
  records: LogRecord[];
  maxItems?: number;
}) {
  const styles = useStyles2(stateTransitionStyles);
  const sortedRecords = orderBy(records, (r) => r.timestamp, 'desc').slice(0, maxItems);

  return (
    <div className={styles.container}>
      {sortedRecords.map((record, index) => (
        <Fragment key={`${record.timestamp}-${index}`}>
          <Text color="secondary" variant="bodySmall">
            {formatTimelineDate(record.timestamp)}
          </Text>
          <EventState state={record.line.previous} showLabel addFilter={noop} type="from" />
          <Icon name="arrow-right" size="sm" />
          <EventState state={record.line.current} showLabel addFilter={noop} type="to" />
        </Fragment>
      ))}
    </div>
  );
}

const stateTransitionStyles = (theme: GrafanaTheme2) => ({
  container: css({
    display: 'grid',
    gridTemplateColumns: 'max-content max-content max-content max-content',
    gap: theme.spacing(1, 2),
    alignItems: 'center',
    padding: theme.spacing(1, 0),
  }),
});

interface ErrorContentProps {
  error: unknown;
}

function ErrorContent({ error }: ErrorContentProps) {
  if (isFetchError(error) && error.status === 404) {
    return (
      <Alert title={t('alerting.triage.rule-not-found.title', 'Rule not found')} severity="error">
        {t('alerting.triage.rule-not-found.description', 'The requested rule could not be found.')}
      </Alert>
    );
  }

  return (
    <Alert title={t('alerting.triage.error-loading-rule', 'Error loading rule')} severity="error">
      {stringifyErrorLike(error)}
    </Alert>
  );
}
