import React, { useEffect, useReducer, useState } from 'react';

import { css, cx } from '@emotion/css';
import { SelectableValue } from '@grafana/data';
import {
  Button,
  Stack,
  Icon,
  Tooltip,
  ConfirmModal,
  LoadingPlaceholder,
  Select,
  RadioButtonGroup,
  Alert,
  useStyles2,
} from '@grafana/ui';
import { UserActions } from 'helpers/authorization/authorization';
import { StackSize } from 'helpers/consts';
import { openNotification } from 'helpers/helpers';
import { observer } from 'mobx-react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { getUtilStyles } from 'styles/utils.styles';

import { CollapsibleTreeView, CollapsibleItem } from 'components/CollapsibleTreeView/CollapsibleTreeView';
import { HamburgerMenuIcon } from 'components/HamburgerMenuIcon/HamburgerMenuIcon';
import { IntegrationBlock } from 'components/Integrations/IntegrationBlock';
import { MonacoEditor } from 'components/MonacoEditor/MonacoEditor';
import { MONACO_READONLY_CONFIG } from 'components/MonacoEditor/MonacoEditor.config';
import { PluginLink } from 'components/PluginLink/PluginLink';
import { RenderConditionally } from 'components/RenderConditionally/RenderConditionally';
import { Text } from 'components/Text/Text';
import { WithContextMenu } from 'components/WithContextMenu/WithContextMenu';
import { ChatOpsConnectors } from 'containers/AlertRules/AlertRules';
import { EscalationChainSteps } from 'containers/EscalationChainSteps/EscalationChainSteps';
import { RouteHeading } from 'containers/IntegrationContainers/RouteHeading';
import { RouteLabelsDisplay } from 'containers/RouteLabelsDisplay/RouteLabelsDisplay';
import { TeamName } from 'containers/TeamName/TeamName';
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
import { AlertTemplatesDTO } from 'models/alert_templates/alert_templates';
import { ChannelFilter, FilteringTermType } from 'models/channel_filter/channel_filter.types';
import { EscalationChain } from 'models/escalation_chain/escalation_chain.types';
import { ApiSchemas } from 'network/oncall-api/api.types';
import { components } from 'network/oncall-api/autogenerated-api.types';
import { CommonIntegrationHelper } from 'pages/integration/CommonIntegration.helper';
import { IntegrationHelper } from 'pages/integration/Integration.helper';
import { MONACO_INPUT_HEIGHT_SMALL } from 'pages/integration/IntegrationCommon.config';
import { AppFeature } from 'state/features';
import { useStore } from 'state/useStore';

import { getExpandedIntegrationRouteDisplayStyles } from './ExpandedIntegrationRouteDisplay.styles';

interface ExpandedIntegrationRouteDisplayProps {
  alertReceiveChannelId: ApiSchemas['AlertReceiveChannel']['id'];
  channelFilterId: ChannelFilter['id'];
  routeIndex: number;
  templates: AlertTemplatesDTO[];
  openEditTemplateModal: (templateName: string | string[], channelFilterId?: ChannelFilter['id']) => void;
  onEditRegexpTemplate: (channelFilterId: ChannelFilter['id']) => void;
  onRouteDelete: (routeId: string) => void;
  onItemMove: () => void;
}

interface ExpandedIntegrationRouteDisplayState {
  isEscalationCollapsed: boolean;
  isRefreshingEscalationChains: boolean;
  routeIdForDeletion: string;
}

enum RoutingOption {
  LABELS = 'Labels',
  TEMPLATE = 'Template',
}

const QueryBuilderOptions = [
  {
    label: 'Labels matching',
    value: RoutingOption.LABELS,
  },
  {
    label: 'Template matching',
    value: RoutingOption.TEMPLATE,
  },
];

export const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayProps> = observer(
  ({
    alertReceiveChannelId,
    channelFilterId,
    templates,
    routeIndex,
    openEditTemplateModal,
    onEditRegexpTemplate,
    onRouteDelete,
    onItemMove,
  }) => {
    const store = useStore();
    const {
      telegramChannelStore,
      escalationPolicyStore,
      escalationChainStore,
      alertReceiveChannelStore,
      grafanaTeamStore,
    } = store;

    const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];

    const [isLoading, setIsLoading] = useState(false);
    const [routingOption, setRoutingOption] = useState<string>(undefined);
    const [labels, setLabels] = useState<Array<components['schemas']['LabelPair']>>([]);
    const [labelErrors, setLabelErrors] = useState([]);
    const styles = useStyles2(getExpandedIntegrationRouteDisplayStyles);

    const [{ isEscalationCollapsed, isRefreshingEscalationChains, routeIdForDeletion }, setState] = useReducer(
      (state: ExpandedIntegrationRouteDisplayState, newState: Partial<ExpandedIntegrationRouteDisplayState>) => ({
        ...state,
        ...newState,
      }),
      {
        isEscalationCollapsed: true,
        isRefreshingEscalationChains: false,
        routeIdForDeletion: undefined,
      }
    );

    useEffect(() => {
      setIsLoading(true);
      (async () => {
        await Promise.all([escalationChainStore.updateItems(), telegramChannelStore.updateTelegramChannels()]);
        setIsLoading(false);
      })();
    }, []);

    useEffect(() => {
      if (channelFilter && !labels?.length) {
        setLabels(channelFilter.filtering_labels);
      }

      if (channelFilter && !routingOption) {
        setRoutingOption(
          (channelFilter.filtering_term_type === FilteringTermType.labels
            ? QueryBuilderOptions[0]
            : QueryBuilderOptions[1]
          ).value
        );
      }
    }, [channelFilter]);

    if (!channelFilter) {
      return null;
    }

    const escalationChainRedirectObj: any = { page: 'escalations', id: channelFilter.escalation_chain || 'new' };
    const channelFilterIds = alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId];
    const isDefault = CommonIntegrationHelper.getRouteConditionWording(channelFilterIds, routeIndex) === 'Default';
    const channelFilterTemplate = channelFilter.filtering_term
      ? IntegrationHelper.getFilteredTemplate(channelFilter.filtering_term, false)
      : '{# Add Routing Template, e.g. {{ payload.severity == "critical" }} #}';

    if (isLoading) {
      return <LoadingPlaceholder text="Loading..." />;
    }

    const hasLabels = store.hasFeature(AppFeature.Labels);
    const escChainDisplayName = escalationChainStore.items[channelFilter.escalation_chain]?.name;
    const getTreeViewElements = () => {
      const configs: CollapsibleItem[] = [
        {
          isHidden: false,
          isCollapsible: false,
          isExpanded: false,
          isTextIcon: true,
          collapsedView: null,
          canHoverIcon: false,
          expandedView: () => (
            <div className={styles.adjustElementPadding}>
              {isDefault ? (
                <div className={styles.defaultRouteView}>
                  <Text customTag="h6" type="primary">
                    All unmatched alerts are directed to this route, grouped using the Grouping Template, sent to
                    messengers, and trigger the escalation chain
                  </Text>
                </div>
              ) : (
                <Stack direction="column" gap={StackSize.sm}>
                  <Text customTag="h6" type="primary">
                    {hasLabels ? 'Alerts matched by' : 'Use routing template'}
                  </Text>

                  <RenderConditionally shouldRender={hasLabels}>
                    <Stack direction="column">
                      <div className={styles.labelsPanel}>
                        <RadioButtonGroup
                          options={QueryBuilderOptions}
                          value={routingOption}
                          onChange={onRouteOptionChange}
                        />
                      </div>

                      <RenderConditionally shouldRender={routingOption === RoutingOption.LABELS}>
                        <Stack direction="column">
                          <RouteLabelsDisplay labels={labels} onChange={onLabelsChange} labelErrors={labelErrors} />

                          <RenderConditionally shouldRender={shouldShowLabelAlert()}>
                            <Alert
                              severity="info"
                              title={
                                (
                                  <Text type="secondary">
                                    Alerts by default will not match this route if no labels are being provided
                                  </Text>
                                ) as unknown as string
                              }
                            />
                          </RenderConditionally>
                        </Stack>
                      </RenderConditionally>
                    </Stack>
                  </RenderConditionally>

                  <RenderConditionally shouldRender={routingOption === RoutingOption.TEMPLATE || !hasLabels}>
                    <Stack direction="column">
                      <Stack gap={StackSize.xs}>
                        <div className={cx(styles.input, styles.inputAlign)}>
                          <MonacoEditor
                            value={channelFilterTemplate}
                            disabled={true}
                            height={MONACO_INPUT_HEIGHT_SMALL}
                            data={templates}
                            showLineNumbers={false}
                            monacoOptions={MONACO_READONLY_CONFIG}
                          />
                        </div>
                        <Button
                          variant={'secondary'}
                          icon="edit"
                          size={'md'}
                          onClick={() => handleEditRoutingTemplate(channelFilter, channelFilterId)}
                        />
                      </Stack>
                      <Alert
                        severity="info"
                        title={
                          (
                            <Text type="secondary">
                              If the Routing template evaluates to True, the alert will be grouped with the Grouping
                              template and proceed to the following steps
                            </Text>
                          ) as unknown as string
                        }
                      />
                    </Stack>
                  </RenderConditionally>
                </Stack>
              )}
            </div>
          ),
        },
        IntegrationHelper.hasChatopsInstalled(store) && {
          isHidden: false,
          isCollapsible: false,
          isTextIcon: true,
          collapsedView: null,
          canHoverIcon: false,
          expandedView: () => (
            <div className={styles.adjustElementPadding}>
              <Stack direction="column" gap={StackSize.sm}>
                <Text customTag="h6" type="primary">
                  Publish to ChatOps
                </Text>
                <ChatOpsConnectors channelFilterId={channelFilterId} showLineNumber={false} />
              </Stack>
            </div>
          ),
        },
        {
          isHidden: false,
          isCollapsible: false,
          isExpanded: false,
          isTextIcon: true,
          collapsedView: null,
          canHoverIcon: false,
          expandedView: () => (
            <div className={styles.adjustElementPadding}>
              <Stack direction="column" gap={StackSize.sm}>
                <Text customTag="h6" type="primary">
                  Trigger escalation chain
                </Text>

                <div data-testid="escalation-chain-select">
                  <Stack gap={StackSize.xs}>
                    <WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
                      <Select
                        isClearable
                        isSearchable
                        width={'auto'}
                        menuShouldPortal
                        className={cx('select', 'control')}
                        placeholder="Select escalation chain"
                        isLoading={isRefreshingEscalationChains}
                        onChange={onEscalationChainChange}
                        options={Object.keys(escalationChainStore.items).map(
                          (eschalationChainId: EscalationChain['id']) => ({
                            id: escalationChainStore.items[eschalationChainId].id,
                            value: escalationChainStore.items[eschalationChainId].name,
                            label: escalationChainStore.items[eschalationChainId].name,
                          })
                        )}
                        value={escChainDisplayName}
                        getOptionLabel={(item: SelectableValue) => {
                          return (
                            <>
                              <Text>{item.label} </Text>
                              <TeamName
                                team={grafanaTeamStore.items[escalationChainStore.items[item.id].team]}
                                size="small"
                              />
                            </>
                          );
                        }}
                      ></Select>
                    </WithPermissionControlTooltip>

                    <Tooltip placement={'top'} content={'Reload list'}>
                      <Button variant={'secondary'} icon={'sync'} size={'md'} onClick={onEscalationChainsRefresh} />
                    </Tooltip>

                    <PluginLink target="_blank" query={escalationChainRedirectObj}>
                      <Tooltip
                        placement={'top'}
                        content={channelFilter.escalation_chain ? 'Edit escalation chain' : 'Add an escalation chain'}
                      >
                        <Button variant={'secondary'} icon={'external-link-alt'} size={'md'} />
                      </Tooltip>
                    </PluginLink>

                    {channelFilter.escalation_chain && (
                      <Button
                        variant={'secondary'}
                        onClick={() => setState({ isEscalationCollapsed: !isEscalationCollapsed })}
                      >
                        <Stack>
                          <Text type="link">{isEscalationCollapsed ? 'Show' : 'Hide'} escalation chain</Text>
                          {isEscalationCollapsed && <Icon name={'angle-right'} />}
                          {!isEscalationCollapsed && <Icon name={'angle-up'} />}
                        </Stack>
                      </Button>
                    )}
                  </Stack>
                </div>
                {!isEscalationCollapsed && (
                  <ReadOnlyEscalationChain escalationChainId={channelFilter.escalation_chain} />
                )}
              </Stack>
            </div>
          ),
        },
      ];

      return configs;
    };

    const routeWording = CommonIntegrationHelper.getRouteConditionWording(channelFilterIds, routeIndex);

    return (
      <>
        <IntegrationBlock
          noContent={false}
          key={channelFilterId}
          heading={
            <div className={styles.headingContainer}>
              <RouteHeading
                className={cx(styles.headingContainerItem, styles.headingContainerItemLarge)}
                routeWording={routeWording}
                routeIndex={routeIndex}
                channelFilter={channelFilter}
                channelFilterIds={alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId]}
              />

              <div className={styles.headingContainerItem}>
                <RouteButtonsDisplay
                  alertReceiveChannelId={alertReceiveChannelId}
                  channelFilterId={channelFilterId}
                  routeIndex={routeIndex}
                  onItemMove={onItemMove}
                  setRouteIdForDeletion={() => setState({ routeIdForDeletion: channelFilterId })}
                  openRouteTemplateEditor={() => handleEditRoutingTemplate(channelFilter, channelFilterId)}
                />
              </div>
            </div>
          }
          content={
            <CollapsibleTreeView configElements={getTreeViewElements() as any} isRouteView startingElemPosition="0%" />
          }
        />
        {routeIdForDeletion && (
          <ConfirmModal
            isOpen
            title="Delete route?"
            body="Are you sure you want to delete this route?"
            confirmText="Delete"
            icon="exclamation-triangle"
            onConfirm={onRouteDeleteConfirm}
            onDismiss={() => setState({ routeIdForDeletion: undefined })}
          />
        )}
      </>
    );

    async function onLabelsChange(labels: Array<components['schemas']['LabelPair']>) {
      setLabelErrors([]);
      setLabels(labels);

      if (!areLabelsValid(labels)) {
        return;
      }

      await alertReceiveChannelStore.saveChannelFilter(channelFilterId, {
        filtering_labels: labels,
        filtering_term_type: FilteringTermType.labels,
      });
    }

    async function onRouteOptionChange(option: string) {
      if (option === RoutingOption.TEMPLATE) {
        await alertReceiveChannelStore.saveChannelFilter(channelFilterId, {
          filtering_term: channelFilter.filtering_term || '',
          filtering_term_type: FilteringTermType.jinja2,
        });
      }

      if (option === RoutingOption.LABELS) {
        await alertReceiveChannelStore.saveChannelFilter(channelFilterId, {
          filtering_labels: channelFilter.filtering_labels || [],
          filtering_term_type: FilteringTermType.labels,
        });
      }

      setRoutingOption(option);
    }

    function shouldShowLabelAlert() {
      if (!labels?.length) {
        return true;
      }
      if (labels.length === 1) {
        return !areLabelsValid(labels);
      }
      return false;
    }

    function areLabelsValid(labels: Array<components['schemas']['LabelPair']>): boolean {
      return labels.every((v) => v.key?.id !== undefined && v.value?.id !== undefined);
    }

    async function onRouteDeleteConfirm() {
      setState({ routeIdForDeletion: undefined });
      onRouteDelete(routeIdForDeletion);
    }

    async function onEscalationChainChange(value: { id: string }) {
      const newEscalationChainId = value ? value.id : null;
      await alertReceiveChannelStore.saveChannelFilter(channelFilterId, {
        escalation_chain: newEscalationChainId,
      });
      escalationChainStore.updateItems(); // to update number_of_integrations and number_of_routes
      escalationPolicyStore.updateEscalationPolicies(newEscalationChainId);
    }

    async function onEscalationChainsRefresh() {
      setState({ isRefreshingEscalationChains: true });
      await escalationChainStore.updateItems();
      setState({ isRefreshingEscalationChains: false });
    }

    function handleEditRoutingTemplate(channelFilter, channelFilterId) {
      if (channelFilter.filtering_term_type === 0) {
        onEditRegexpTemplate(channelFilterId);
      } else {
        openEditTemplateModal('route_template', channelFilterId);
      }
    }
  }
);

const ReadOnlyEscalationChain: React.FC<{ escalationChainId: string }> = ({ escalationChainId }) => {
  return <EscalationChainSteps isDisabled id={escalationChainId} />;
};

interface RouteButtonsDisplayProps {
  alertReceiveChannelId: ApiSchemas['AlertReceiveChannel']['id'];
  channelFilterId: ChannelFilter['id'];
  routeIndex: number;
  setRouteIdForDeletion(): void;
  openRouteTemplateEditor(): void;
  onItemMove();
}

export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
  alertReceiveChannelId,
  channelFilterId,
  routeIndex,
  setRouteIdForDeletion,
  openRouteTemplateEditor,
  onItemMove,
}) => {
  const { alertReceiveChannelStore } = useStore();
  const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];
  const channelFilterIds = alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId];
  const styles = useStyles2(getExpandedIntegrationRouteDisplayStyles);
  const utilStyles = useStyles2(getUtilStyles);

  return (
    <Stack gap={StackSize.xs}>
      {routeIndex > 0 && !channelFilter.is_default && (
        <WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
          <Tooltip placement="top" content={'Move Up'}>
            <Button variant={'secondary'} onClick={onRouteMoveUp} icon={'arrow-up'} size={'sm'} />
          </Tooltip>
        </WithPermissionControlTooltip>
      )}

      {routeIndex < channelFilterIds.length - 2 && !channelFilter.is_default && (
        <WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
          <Tooltip placement="top" content={'Move Down'}>
            <Button variant={'secondary'} onClick={onRouteMoveDown} icon={'arrow-down'} size={'sm'} />
          </Tooltip>
        </WithPermissionControlTooltip>
      )}

      {!channelFilter.is_default && (
        <WithContextMenu
          renderMenuItems={() => (
            <div className={styles.integrationsActionsList}>
              <div className={styles.integrationsActionItem} onClick={openRouteTemplateEditor}>
                <Text type="primary">Edit Template</Text>
              </div>

              <CopyToClipboard text={channelFilter.id} onCopy={() => openNotification('Route ID is copied')}>
                <div className={cx(styles.integrationsActionItem)}>
                  <Stack gap={StackSize.xs}>
                    <Icon name="copy" />

                    <Text type="primary">UID: {channelFilter.id}</Text>
                  </Stack>
                </div>
              </CopyToClipboard>

              <div className={cx(utilStyles.thinLineBreak)} />

              <WithPermissionControlTooltip key="delete" userAction={UserActions.IntegrationsWrite}>
                <div className={styles.integrationsActionItem} onClick={onDelete}>
                  <Text type="danger">
                    <Stack gap={StackSize.xs}>
                      <Icon name="trash-alt" />
                      <span>Delete Route</span>
                    </Stack>
                  </Text>
                </div>
              </WithPermissionControlTooltip>
            </div>
          )}
        >
          {({ openMenu }) => (
            <HamburgerMenuIcon
              openMenu={openMenu}
              listBorder={2}
              listWidth={200}
              className={css`
                height: 24px;
                width: 22px;
                cursor: pointer;
              `}
              stopPropagation={true}
            />
          )}
        </WithContextMenu>
      )}
    </Stack>
  );

  function onDelete() {
    setRouteIdForDeletion();
  }

  function onRouteMoveDown(e: React.SyntheticEvent) {
    e.stopPropagation();
    alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex + 1);
    onItemMove();
  }

  function onRouteMoveUp(e: React.SyntheticEvent) {
    e.stopPropagation();
    alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex - 1);
    onItemMove();
  }
};
