import { ReactElement, useMemo } from 'react';

import FlexBox from '../FlexBox';
import Grid from '../Grid';
import InputWithRef from '../InputWithRef';
import OutlinedInputWithLabel from '../OutlinedInputWithLabel';
import { InnerPanel, InnerPanelBody, InnerPanelHeader } from '../Panels';
import SwitchWithLabel from '../SwitchWithLabel';
import {
  buildIPAddressTestBatch,
  buildPeacefulStringTestBatch,
} from '../../lib/test_input';
import { BodyText } from '../Text';

const INPUT_ID_PREFIX_AN_HOST = 'an-host-input';

const INPUT_CELL_ID_PREFIX_AH = `${INPUT_ID_PREFIX_AN_HOST}-cell`;

const INPUT_LABEL_AH_IPMI_IP = 'IPMI IP';

const MAP_TO_AH_INPUT_HANDLER: MapToManifestFormInputHandler = {
  fence: (container, input) => {
    const {
      dataset: { hostId = '', fenceId = '', fenceName = '' },
      value: fencePort,
    } = input;
    const {
      hostConfig: {
        hosts: { [hostId]: host },
      },
    } = container;
    const { fences = {} } = host;

    fences[fenceId] = {
      fenceName,
      fencePort,
    };
    host.fences = fences;
  },
  host: (container, input) => {
    const {
      dataset: { hostId = '', hostNumber: rawHostNumber = '', hostType = '' },
    } = input;
    const hostNumber = Number.parseInt(rawHostNumber, 10);

    container.hostConfig.hosts[hostId] = {
      hostNumber,
      hostType,
    };
  },
  ipmi: (container, input) => {
    const {
      dataset: { hostId = '' },
      value: ipmiIp,
    } = input;
    const {
      hostConfig: {
        hosts: { [hostId]: host },
      },
    } = container;

    host.ipmiIp = ipmiIp;
  },
  network: (container, input) => {
    const {
      dataset: {
        hostId = '',
        networkId = '',
        networkNumber: rawNetworkNumber = '',
        networkType = '',
      },
      value: networkIp,
    } = input;
    const {
      hostConfig: {
        hosts: { [hostId]: host },
      },
    } = container;
    const { networks = {} } = host;
    const networkNumber = Number.parseInt(rawNetworkNumber, 10);

    networks[networkId] = {
      networkIp,
      networkNumber,
      networkType,
    };
    host.networks = networks;
  },
  ups: (container, input) => {
    const {
      checked: isUsed,
      dataset: { hostId = '', upsId = '', upsName = '' },
    } = input;
    const {
      hostConfig: {
        hosts: { [hostId]: host },
      },
    } = container;
    const { upses = {} } = host;

    upses[upsId] = {
      isUsed,
      upsName,
    };
    host.upses = upses;
  },
};

const GRID_COLUMNS = { xs: 1, sm: 2, md: 3 };
const GRID_SPACING = '1em';

const buildInputIdAHFencePort = (hostId: string, fenceId: string): string =>
  `${INPUT_ID_PREFIX_AN_HOST}-${hostId}-${fenceId}-port`;

const buildInputIdAHIpmiIp = (hostId: string): string =>
  `${INPUT_ID_PREFIX_AN_HOST}-${hostId}-ipmi-ip`;

const buildInputIdAHNetworkIp = (hostId: string, networkId: string): string =>
  `${INPUT_ID_PREFIX_AN_HOST}-${hostId}-${networkId}-ip`;

const buildInputIdAHUpsPowerHost = (hostId: string, upsId: string): string =>
  `${INPUT_ID_PREFIX_AN_HOST}-${hostId}-${upsId}-power-host`;

const AnHostInputGroup = <M extends MapToInputTestID>({
  formUtils: {
    buildFinishInputTestBatchFunction,
    buildInputFirstRenderFunction,
    buildInputUnmountFunction,
    setMessage,
  },
  hostId,
  hostNumber,
  hostType,
  previous: {
    fences: fenceList = {},
    ipmiIp: previousIpmiIp,
    networks: networkList = {},
    upses: upsList = {},
  } = {},
  // Props that depend on others.
  hostLabel = `${hostType.replace('node', 'subnode')} ${hostNumber}`,
}: AnHostInputGroupProps<M>): ReactElement => {
  const fenceListEntries = useMemo(
    () => Object.entries(fenceList),
    [fenceList],
  );
  const networkListEntries = useMemo(
    () => Object.entries(networkList),
    [networkList],
  );
  const upsListEntries = useMemo(() => Object.entries(upsList), [upsList]);

  const isShowUpsListGrid = useMemo(
    () => Boolean(upsListEntries.length),
    [upsListEntries.length],
  );

  const inputIdAHHost = useMemo(
    () => `${INPUT_ID_PREFIX_AN_HOST}-${hostId}`,
    [hostId],
  );
  const inputIdAHIpmiIp = useMemo(() => buildInputIdAHIpmiIp(hostId), [hostId]);

  const inputCellIdAHIpmiIp = useMemo(
    () => `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-ipmi-ip`,
    [hostId],
  );

  const fenceListGridLayout = useMemo(
    () =>
      fenceListEntries.reduce<GridLayout>(
        (previous, [fenceId, { fenceName, fencePort }]) => {
          const cellId = `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-${fenceId}-port`;

          const inputId = buildInputIdAHFencePort(hostId, fenceId);
          const inputLabel = `Port on ${fenceName}`;

          previous[cellId] = {
            children: (
              <InputWithRef
                input={
                  <OutlinedInputWithLabel
                    baseInputProps={{
                      'data-handler': 'fence',
                      'data-host-id': hostId,
                      'data-fence-id': fenceId,
                      'data-fence-name': fenceName,
                    }}
                    id={inputId}
                    label={inputLabel}
                    value={fencePort}
                  />
                }
                inputTestBatch={buildPeacefulStringTestBatch(
                  `${hostId} ${inputLabel}`,
                  () => {
                    setMessage(inputId);
                  },
                  { onFinishBatch: buildFinishInputTestBatchFunction(inputId) },
                  (message) => {
                    setMessage(inputId, { children: message });
                  },
                )}
                onFirstRender={buildInputFirstRenderFunction(inputId)}
                required
              />
            ),
          };

          return previous;
        },
        {},
      ),
    [
      buildFinishInputTestBatchFunction,
      buildInputFirstRenderFunction,
      fenceListEntries,
      hostId,
      setMessage,
    ],
  );

  const networkListGridLayout = useMemo(
    () =>
      networkListEntries.reduce<GridLayout>(
        (previous, [networkId, { networkIp, networkNumber, networkType }]) => {
          const cellId = `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-${networkId}-ip`;

          const inputId = buildInputIdAHNetworkIp(hostId, networkId);
          const inputLabel = `${networkType.toUpperCase()} ${networkNumber} IP`;

          previous[cellId] = {
            children: (
              <InputWithRef
                input={
                  <OutlinedInputWithLabel
                    baseInputProps={{
                      'data-handler': 'network',
                      'data-host-id': hostId,
                      'data-network-id': networkId,
                      'data-network-number': networkNumber,
                      'data-network-type': networkType,
                    }}
                    id={inputId}
                    label={inputLabel}
                    value={networkIp}
                  />
                }
                inputTestBatch={buildIPAddressTestBatch(
                  `${hostId} ${inputLabel}`,
                  () => {
                    setMessage(inputId);
                  },
                  { onFinishBatch: buildFinishInputTestBatchFunction(inputId) },
                  (message) => {
                    setMessage(inputId, { children: message });
                  },
                )}
                onFirstRender={buildInputFirstRenderFunction(inputId)}
                onUnmount={buildInputUnmountFunction(inputId)}
                required
              />
            ),
          };

          return previous;
        },
        {},
      ),
    [
      networkListEntries,
      hostId,
      buildFinishInputTestBatchFunction,
      buildInputFirstRenderFunction,
      buildInputUnmountFunction,
      setMessage,
    ],
  );

  const upsListGridLayout = useMemo(
    () =>
      upsListEntries.reduce<GridLayout>(
        (previous, [upsId, { isUsed, upsName }]) => {
          const cellId = `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-${upsId}-power-host`;

          const inputId = buildInputIdAHUpsPowerHost(hostId, upsId);
          const inputLabel = `Uses ${upsName}`;

          previous[cellId] = {
            children: (
              <InputWithRef
                input={
                  <SwitchWithLabel
                    baseInputProps={{
                      'data-handler': 'ups',
                      'data-host-id': hostId,
                      'data-ups-id': upsId,
                      'data-ups-name': upsName,
                    }}
                    checked={isUsed}
                    id={inputId}
                    label={inputLabel}
                  />
                }
                valueType="boolean"
              />
            ),
          };

          return previous;
        },
        {},
      ),
    [hostId, upsListEntries],
  );

  const upsListGrid = useMemo(
    () =>
      isShowUpsListGrid && (
        <Grid
          columns={GRID_COLUMNS}
          layout={upsListGridLayout}
          spacing={GRID_SPACING}
        />
      ),
    [isShowUpsListGrid, upsListGridLayout],
  );

  return (
    <InnerPanel mv={0}>
      <InnerPanelHeader>
        <BodyText>{hostLabel}</BodyText>
      </InnerPanelHeader>
      <InnerPanelBody>
        <input
          hidden
          id={inputIdAHHost}
          readOnly
          data-handler="host"
          data-host-id={hostId}
          data-host-number={hostNumber}
          data-host-type={hostType}
        />
        <FlexBox>
          <Grid
            columns={GRID_COLUMNS}
            layout={{
              ...networkListGridLayout,
              [inputCellIdAHIpmiIp]: {
                children: (
                  <InputWithRef
                    input={
                      <OutlinedInputWithLabel
                        baseInputProps={{
                          'data-handler': 'ipmi',
                          'data-host-id': hostId,
                        }}
                        id={inputIdAHIpmiIp}
                        label={INPUT_LABEL_AH_IPMI_IP}
                        value={previousIpmiIp}
                      />
                    }
                    inputTestBatch={buildIPAddressTestBatch(
                      `${hostId} ${INPUT_LABEL_AH_IPMI_IP}`,
                      () => {
                        setMessage(inputIdAHIpmiIp);
                      },
                      {
                        onFinishBatch:
                          buildFinishInputTestBatchFunction(inputIdAHIpmiIp),
                      },
                      (message) => {
                        setMessage(inputIdAHIpmiIp, { children: message });
                      },
                    )}
                    onFirstRender={buildInputFirstRenderFunction(
                      inputIdAHIpmiIp,
                    )}
                    onUnmount={buildInputUnmountFunction(inputIdAHIpmiIp)}
                  />
                ),
              },
              ...fenceListGridLayout,
            }}
            spacing={GRID_SPACING}
          />
          {upsListGrid}
        </FlexBox>
      </InnerPanelBody>
    </InnerPanel>
  );
};

export {
  INPUT_ID_PREFIX_AN_HOST,
  MAP_TO_AH_INPUT_HANDLER,
  buildInputIdAHFencePort,
  buildInputIdAHIpmiIp,
  buildInputIdAHNetworkIp,
  buildInputIdAHUpsPowerHost,
};

export default AnHostInputGroup;
