import { FC, useMemo, useState } from 'react';

import AddMailRecipientForm from './AddMailRecipientForm';
import { toAnvilOverviewList } from '../../lib/api_converters';
import CrudList from '../CrudList';
import EditMailRecipientForm from './EditMailRecipientForm';
import { BodyText } from '../Text';
import useActiveFetch from '../../hooks/useActiveFetch';
import useFetch from '../../hooks/useFetch';

const ManageMailRecipient: FC = () => {
  const [alertOverrides, setAlertOverrides] = useState<
    APIAlertOverrideOverviewList | undefined
  >();

  const { altData: nodes, loading: loadingNodes } = useFetch<
    APIAnvilOverviewArray,
    APIAnvilOverviewList
  >('/anvil', { mod: toAnvilOverviewList });

  const alertOverrideTargetOptions = useMemo<AlertOverrideTarget[] | undefined>(
    () =>
      nodes &&
      Object.values(nodes)
        .sort((a, b) => a.name.localeCompare(b.name))
        .reduce<AlertOverrideTarget[]>((options, node) => {
          const nodeTarget: AlertOverrideTarget = {
            description: node.description,
            name: node.name,
            node: node.uuid,
            subnodes: [],
            type: 'node',
            uuid: node.uuid,
          };

          const subnodeTargets = Object.values(node.hosts)
            .sort((a, b) => a.name.localeCompare(b.name))
            .reduce<AlertOverrideTarget[]>((previous, subnode) => {
              if (subnode.type === 'dr') return previous;

              previous.push({
                name: subnode.name,
                node: node.uuid,
                type: 'subnode',
                uuid: subnode.uuid,
              });

              nodeTarget.subnodes?.push(subnode.uuid);

              return previous;
            }, []);

          // Append the options in sequence: node followed by its subnode(s).
          options.push(nodeTarget, ...subnodeTargets);

          return options;
        }, []),
    [nodes],
  );

  const { fetch: getAlertOverrides, loading: loadingAlertOverrides } =
    useActiveFetch<APIAlertOverrideOverviewList>({
      onData: (data) => setAlertOverrides(data),
      url: '/alert-override',
    });

  const formikAlertOverrides = useMemo<
    AlertOverrideFormikValues | undefined
  >(() => {
    if (!nodes || !alertOverrides) return undefined;

    /**
     * Group alert override rules based on node UUID. The groups will be used
     * for comparison to see whether the subnodes are assigned the same alert
     * level.
     *
     * If subnodes have the same level, they will be consolidated into a single
     * target for display. Otherwise, every subnode will get its own visual.
     */
    const groups = Object.values(alertOverrides).reduce<
      Record<string, APIAlertOverrideOverview[]>
    >((previous, override) => {
      const {
        node: { uuid: nodeUuid },
      } = override;

      if (previous[nodeUuid]) {
        previous[nodeUuid].push(override);
      } else {
        previous[nodeUuid] = [override];
      }

      return previous;
    }, {});

    return Object.entries(groups).reduce<AlertOverrideFormikValues>(
      (previous, pair) => {
        const [nodeUuid, overrides] = pair;
        const [firstOverride, ...restOverrides] = overrides;

        const sameLevel =
          overrides.length > 1 &&
          restOverrides.every(({ level }) => level === firstOverride.level);

        if (sameLevel) {
          const {
            0: { level },
          } = overrides;

          const { [nodeUuid]: node } = nodes;

          previous[nodeUuid] = {
            level,
            target: {
              description: node.description,
              name: node.name,
              node: node.uuid,
              subnodes: overrides.map<string>(({ subnode: { uuid } }) => uuid),
              type: 'node',
              uuid: node.uuid,
            },
            uuids: overrides.reduce<Record<string, string>>(
              (uuids, { subnode, uuid: overrideUuid }) => {
                uuids[overrideUuid] = subnode.uuid;

                return uuids;
              },
              {},
            ),
          };
        } else {
          overrides.forEach(({ level, node, subnode, uuid: overrideUuid }) => {
            previous[subnode.uuid] = {
              level,
              target: {
                name: subnode.name,
                node: node.uuid,
                type: 'subnode',
                uuid: subnode.uuid,
              },
              uuids: { [overrideUuid]: subnode.uuid },
            };
          });
        }

        return previous;
      },
      {},
    );
  }, [alertOverrides, nodes]);

  return (
    <>
      <CrudList<APIMailRecipientOverview, APIMailRecipientDetail>
        addHeader="Add mail recipient"
        editHeader={(entry) => `Update ${entry?.name}`}
        entriesUrl="/mail-recipient"
        getAddLoading={(previous) => previous || loadingNodes}
        getDeleteErrorMessage={({ children, ...rest }) => ({
          ...rest,
          children: <>Failed to delete mail recipient(s). {children}</>,
        })}
        getDeleteHeader={(count) =>
          `Delete the following ${count} mail recipient(s)?`
        }
        getDeleteSuccessMessage={() => ({
          children: <>Successfully deleted mail recipient(s).</>,
        })}
        getEditLoading={(previous) => previous || loadingAlertOverrides}
        onItemClick={(base, ...args) => {
          const [, mailRecipientUuid] = args;

          base(...args);

          getAlertOverrides(undefined, {
            params: { 'mail-recipient': mailRecipientUuid },
          });
        }}
        renderAddForm={(tools) =>
          alertOverrideTargetOptions && (
            <AddMailRecipientForm
              alertOverrideTargetOptions={alertOverrideTargetOptions}
              tools={tools}
            />
          )
        }
        renderDeleteItem={(mailRecipientList, { key }) => {
          const mr = mailRecipientList?.[key];

          return <BodyText>{mr?.name}</BodyText>;
        }}
        renderEditForm={(tools, mailRecipient) =>
          alertOverrideTargetOptions &&
          mailRecipient &&
          formikAlertOverrides && (
            <EditMailRecipientForm
              alertOverrideTargetOptions={alertOverrideTargetOptions}
              mailRecipientUuid={mailRecipient.uuid}
              previousFormikValues={{
                [mailRecipient.uuid]: {
                  alertOverrides: formikAlertOverrides,
                  ...mailRecipient,
                },
              }}
              tools={tools}
            />
          )
        }
        renderListItem={(uuid, { name }) => <BodyText>{name}</BodyText>}
      />
    </>
  );
};

export default ManageMailRecipient;
