import { type ReactNode, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';

import { ContactPointSelector as GrafanaManagedContactPointSelector } from '@grafana/alerting/unstable';
import { Trans, t } from '@grafana/i18n';
import { Collapse, Field, InlineLabel, Input, MultiSelect, Stack, TextLink, useStyles2 } from '@grafana/ui';
import { ExternalAlertmanagerContactPointSelector } from 'app/features/alerting/unified/components/notification-policies/ContactPointSelector';
import { handleContactPointSelect } from 'app/features/alerting/unified/components/notification-policies/utils';
import { type RouteWithID } from 'app/plugins/datasource/alertmanager/types';

import { useAlertmanager } from '../../state/AlertmanagerContext';
import { type FormAmRoute } from '../../types/amroutes';
import {
  amRouteToFormAmRoute,
  commonGroupByOptions,
  mapMultiSelectValueToStrings,
  promDurationValidator,
  repeatIntervalValidator,
  stringToSelectableValue,
  stringsToSelectableValues,
} from '../../utils/amroutes';
import { validateRbacEntityName } from '../../utils/k8s/utils';
import { makeAMLink } from '../../utils/misc';

import { PromDurationInput } from './PromDurationInput';
import { getFormStyles } from './formStyles';
import { TIMING_OPTIONS_DEFAULTS } from './timingOptions';

export interface AmRootRouteFormProps {
  alertManagerSourceName: string;
  actionButtons: ReactNode;
  onSubmit: (route: Partial<FormAmRoute>) => void;
  route: RouteWithID;
  showNameField?: boolean;
  existingPolicyNames?: string[];
  nameError?: string;
}

export const AmRootRouteForm = ({
  actionButtons,
  alertManagerSourceName,
  existingPolicyNames,
  nameError,
  onSubmit,
  route,
  showNameField,
}: AmRootRouteFormProps) => {
  const styles = useStyles2(getFormStyles);
  const [isTimingOptionsExpanded, setIsTimingOptionsExpanded] = useState(false);
  const { isGrafanaAlertmanager } = useAlertmanager();
  const [groupByOptions, setGroupByOptions] = useState(stringsToSelectableValues(route.group_by));

  const defaultValues = amRouteToFormAmRoute(route);
  const {
    handleSubmit,
    register,
    control,
    formState: { errors },
    setValue,
    getValues,
  } = useForm<FormAmRoute>({
    defaultValues: {
      ...defaultValues,
      overrideTimings: true,
      overrideGrouping: true,
    },
  });
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Stack direction="column" gap={2}>
        {showNameField && (
          <Field
            noMargin
            required
            label={t('alerting.am-root-route-form.label-name', 'Name')}
            description={t(
              'alerting.am-root-route-form.description-unique-routing',
              'A unique name for the routing tree'
            )}
            invalid={!!(nameError ?? errors.name?.message)}
            error={nameError ?? errors.name?.message}
          >
            <Input
              {...register('name', {
                validate: {
                  format: (value) => validateRbacEntityName(value)?.message ?? true,
                  duplicate: (value) =>
                    existingPolicyNames?.includes((value ?? '').trim())
                      ? t(
                          'alerting.am-root-route-form.validate-name-duplicate',
                          'A notification policy with this name already exists'
                        )
                      : true,
                },
              })}
              className={styles.input}
              data-testid="routing-tree-name"
              aria-label={t('alerting.am-root-route-form.aria-label-name', 'Name')}
            />
          </Field>
        )}
        <Field
          noMargin
          label={t('alerting.am-root-route-form.label-default-contact-point', 'Default contact point')}
          invalid={Boolean(errors.receiver) ? true : undefined}
          error={errors.receiver?.message}
        >
          <Stack direction="row" data-testid="am-receiver-select" wrap="nowrap">
            <Controller
              render={({ field: { onChange, ref, value, ...field } }) =>
                isGrafanaAlertmanager ? (
                  <GrafanaManagedContactPointSelector
                    onChange={(contactPoint) => {
                      handleContactPointSelect(contactPoint?.spec.title, onChange);
                    }}
                    isClearable={false}
                    value={value}
                    placeholder={t(
                      'alerting.notification-policies-filter.placeholder-search-by-contact-point',
                      'Choose a contact point'
                    )}
                  />
                ) : (
                  <ExternalAlertmanagerContactPointSelector
                    selectProps={{
                      ...field,
                      onChange: (changeValue) => handleContactPointSelect(changeValue.value?.name, onChange),
                    }}
                    selectedContactPointName={value}
                  />
                )
              }
              control={control}
              name="receiver"
              rules={{
                required: { value: true, message: t('alerting.am-root-route-form.message.required', 'Required.') },
              }}
            />
            <InlineLabel width="auto" transparent={true}>
              <TextLink
                external
                href={makeAMLink('/alerting/notifications/receivers/new', alertManagerSourceName)}
                inline={true}
              >
                <Trans i18nKey="alerting.am-root-route-form.create-a-contact-point">Create a contact point</Trans>
              </TextLink>
            </InlineLabel>
          </Stack>
        </Field>
        <Field
          noMargin
          label={t('alerting.am-root-route-form.am-group-select-label-group-by', 'Group by')}
          description={t(
            'alerting.am-root-route-form.am-group-select-description-group-by',
            'Combine multiple alerts into a single notification by grouping them by the same label values.'
          )}
          data-testid="am-group-select"
        >
          <Controller
            render={({ field: { onChange, ref, ...field } }) => (
              <MultiSelect
                aria-label={t('alerting.am-root-route-form.aria-label-group-by', 'Group by')}
                {...field}
                allowCustomValue
                className={styles.input}
                onCreateOption={(opt: string) => {
                  setGroupByOptions((opts) => [...opts, stringToSelectableValue(opt)]);
                  setValue('groupBy', [...(field.value || []), opt]);
                }}
                onChange={(value) => onChange(mapMultiSelectValueToStrings(value))}
                options={[...commonGroupByOptions, ...groupByOptions]}
              />
            )}
            control={control}
            name="groupBy"
          />
        </Field>
        <Collapse
          className={styles.collapse}
          isOpen={isTimingOptionsExpanded}
          label={t('alerting.am-root-route-form.label-timing-options', 'Timing options')}
          onToggle={setIsTimingOptionsExpanded}
        >
          <div className={styles.timingFormContainer}>
            <Stack direction="column" gap={2}>
              <Field
                noMargin
                label={t('alerting.am-root-route-form.am-group-wait-label-group-wait', 'Group wait')}
                description={t(
                  'alerting.am-root-route-form.am-group-description-label',
                  'The waiting time before sending the first notification for a new group of alerts. Default 30 seconds.'
                )}
                invalid={!!errors.groupWaitValue}
                error={errors.groupWaitValue?.message}
                data-testid="am-group-wait"
              >
                <PromDurationInput
                  {...register('groupWaitValue', { validate: promDurationValidator })}
                  placeholder={TIMING_OPTIONS_DEFAULTS.group_wait}
                  className={styles.promDurationInput}
                  aria-label={t('alerting.am-root-route-form.aria-label-group-wait', 'Group wait')}
                />
              </Field>
              <Field
                noMargin
                label={t('alerting.am-root-route-form.am-group-interval-label-group-interval', 'Group interval')}
                description={t(
                  'alerting.am-root-route-form.am-group-interval-description',
                  'The wait time before sending a notification about changes in the alert group after the first notification has been sent. Default is 5 minutes.'
                )}
                invalid={!!errors.groupIntervalValue}
                error={errors.groupIntervalValue?.message}
                data-testid="am-group-interval"
              >
                <PromDurationInput
                  {...register('groupIntervalValue', { validate: promDurationValidator })}
                  placeholder={TIMING_OPTIONS_DEFAULTS.group_interval}
                  className={styles.promDurationInput}
                  aria-label={t('alerting.am-root-route-form.aria-label-group-interval', 'Group interval')}
                />
              </Field>
              <Field
                noMargin
                label={t('alerting.am-root-route-form.am-repeat-interval-label-repeat-interval', 'Repeat interval')}
                description={t(
                  'alerting.am-root-route-form.am-repeat-interval-description',
                  'The wait time before resending a notification that has already been sent successfully. Default is 4 hours. Should be a multiple of Group interval.'
                )}
                invalid={!!errors.repeatIntervalValue}
                error={errors.repeatIntervalValue?.message}
                data-testid="am-repeat-interval"
              >
                <PromDurationInput
                  {...register('repeatIntervalValue', {
                    validate: (value: string) => {
                      const groupInterval = getValues('groupIntervalValue');
                      return repeatIntervalValidator(value, groupInterval);
                    },
                  })}
                  placeholder={TIMING_OPTIONS_DEFAULTS.repeat_interval}
                  className={styles.promDurationInput}
                  aria-label={t('alerting.am-root-route-form.aria-label-repeat-interval', 'Repeat interval')}
                />
              </Field>
            </Stack>
          </div>
        </Collapse>
        {actionButtons}
      </Stack>
    </form>
  );
};
