import { Box } from '@mui/material';
import {
  FC,
  FormEventHandler,
  ReactElement,
  ReactNode,
  useMemo,
  useRef,
  useState,
} from 'react';

import API_BASE_URL from '../../lib/consts/API_BASE_URL';

import AddFenceInputGroup from './AddFenceInputGroup';
import api from '../../lib/api';
import { ID_SEPARATOR } from './CommonFenceInputGroup';
import ConfirmDialog from '../ConfirmDialog';
import EditFenceInputGroup from './EditFenceInputGroup';
import FlexBox from '../FlexBox';
import handleAPIError from '../../lib/handleAPIError';
import List from '../List';
import { Panel, PanelHeader } from '../Panels';
import periodicFetch from '../../lib/fetchers/periodicFetch';
import Spinner from '../Spinner';
import {
  BodyText,
  HeaderText,
  InlineMonoText,
  MonoText,
  SensitiveText,
  SmallText,
} from '../Text';
import useIsFirstRender from '../../hooks/useIsFirstRender';
import useProtectedState from '../../hooks/useProtectedState';

type FormFenceParameterData = {
  fenceAgent: string;
  fenceName: string;
  parameterInputs: {
    [parameterInputId: string]: {
      isParameterSensitive: boolean;
      parameterId: string;
      parameterType: string;
      parameterValue: string;
    };
  };
};

const fenceParameterBooleanToString = (value: boolean) => (value ? '1' : '0');

const getFormFenceParameters = (
  fenceTemplate: APIFenceTemplate,
  ...[{ target }]: Parameters<FormEventHandler<HTMLDivElement>>
) => {
  const { elements } = target as HTMLFormElement;

  return Object.values(elements).reduce<FormFenceParameterData>(
    (previous, formElement) => {
      const { id: inputId } = formElement;
      const reExtract = new RegExp(`^(fence[^-]+)${ID_SEPARATOR}([^\\s]+)$`);
      const matched = inputId.match(reExtract);

      if (matched) {
        const [, fenceId, parameterId] = matched;

        previous.fenceAgent = fenceId;

        const inputElement = formElement as HTMLInputElement;
        const {
          checked,
          dataset: { sensitive: rawSensitive },
          value,
        } = inputElement;

        if (parameterId === 'name') {
          previous.fenceName = value;
        }

        const {
          [fenceId]: {
            parameters: {
              [parameterId]: { content_type: parameterType = 'string' } = {},
            },
          },
        } = fenceTemplate;

        previous.parameterInputs[inputId] = {
          isParameterSensitive: rawSensitive === 'true',
          parameterId,
          parameterType,
          parameterValue:
            parameterType === 'boolean'
              ? fenceParameterBooleanToString(checked)
              : value,
        };
      }

      return previous;
    },
    { fenceAgent: '', fenceName: '', parameterInputs: {} },
  );
};

const buildConfirmFenceParameters = (
  parameterInputs: FormFenceParameterData['parameterInputs'],
) => (
  <List
    listItems={parameterInputs}
    listItemProps={{ sx: { padding: 0 } }}
    renderListItem={(
      parameterInputId,
      { isParameterSensitive, parameterId, parameterValue },
    ) => {
      let textElement: ReactElement;

      if (parameterValue) {
        textElement = isParameterSensitive ? (
          <SensitiveText monospaced>{parameterValue}</SensitiveText>
        ) : (
          <Box sx={{ maxWidth: '100%', overflowX: 'scroll' }}>
            <MonoText lineHeight={2.8} whiteSpace="nowrap">
              {parameterValue}
            </MonoText>
          </Box>
        );
      } else {
        textElement = <SmallText>none</SmallText>;
      }

      return (
        <FlexBox
          fullWidth
          growFirst
          height="2.8em"
          key={`confirm-${parameterInputId}`}
          maxWidth="100%"
          row
        >
          <BodyText>{parameterId}</BodyText>
          {textElement}
        </FlexBox>
      );
    }}
  />
);

const ManageFencePanel: FC = () => {
  const isFirstRender = useIsFirstRender();

  const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
  const formDialogRef = useRef<ConfirmDialogForwardedRefContent>({});

  const [confirmDialogProps, setConfirmDialogProps] =
    useState<ConfirmDialogProps>({
      actionProceedText: '',
      content: '',
      titleText: '',
    });
  const [formDialogProps, setFormDialogProps] = useState<ConfirmDialogProps>({
    actionProceedText: '',
    content: '',
    titleText: '',
  });
  const [fenceTemplate, setFenceTemplate] = useProtectedState<
    APIFenceTemplate | undefined
  >(undefined);
  const [isEditFences, setIsEditFences] = useState<boolean>(false);
  const [isLoadingFenceTemplate, setIsLoadingFenceTemplate] =
    useProtectedState<boolean>(true);

  const { data: fenceOverviews, isLoading: isFenceOverviewsLoading } =
    periodicFetch<APIFenceOverview>(`${API_BASE_URL}/fence`, {
      refreshInterval: 60000,
    });

  const listElement = useMemo(
    () => (
      <List
        allowEdit
        allowItemButton={isEditFences}
        edit={isEditFences}
        header
        listItems={fenceOverviews}
        onAdd={() => {
          setFormDialogProps({
            actionProceedText: 'Add',
            content: <AddFenceInputGroup fenceTemplate={fenceTemplate} />,
            onSubmitAppend: (event) => {
              if (!fenceTemplate) {
                return;
              }

              const addData = getFormFenceParameters(fenceTemplate, event);

              setConfirmDialogProps({
                actionProceedText: 'Add',
                content: buildConfirmFenceParameters(addData.parameterInputs),
                titleText: (
                  <HeaderText>
                    Add a{' '}
                    <InlineMonoText fontSize="inherit">
                      {addData.fenceAgent}
                    </InlineMonoText>{' '}
                    fence device with the following parameters?
                  </HeaderText>
                ),
              });

              confirmDialogRef.current.setOpen?.call(null, true);
            },
            titleText: 'Add a fence device',
          });

          formDialogRef.current.setOpen?.call(null, true);
        }}
        onEdit={() => {
          setIsEditFences((previous) => !previous);
        }}
        onItemClick={({ fenceAgent: fenceId, fenceName, fenceParameters }) => {
          setFormDialogProps({
            actionProceedText: 'Update',
            content: (
              <EditFenceInputGroup
                fenceId={fenceId}
                fenceTemplate={fenceTemplate}
                previousFenceName={fenceName}
                previousFenceParameters={fenceParameters}
              />
            ),
            onSubmitAppend: (event) => {
              if (!fenceTemplate) {
                return;
              }

              const editData = getFormFenceParameters(fenceTemplate, event);

              setConfirmDialogProps({
                actionProceedText: 'Update',
                content: buildConfirmFenceParameters(editData.parameterInputs),
                titleText: (
                  <HeaderText>
                    Update{' '}
                    <InlineMonoText fontSize="inherit">
                      {editData.fenceName}
                    </InlineMonoText>{' '}
                    fence device with the following parameters?
                  </HeaderText>
                ),
              });

              confirmDialogRef.current.setOpen?.call(null, true);
            },
            titleText: (
              <HeaderText>
                Update fence device{' '}
                <InlineMonoText fontSize="inherit">{fenceName}</InlineMonoText>{' '}
                parameters
              </HeaderText>
            ),
          });

          formDialogRef.current.setOpen?.call(null, true);
        }}
        renderListItem={(
          fenceUUID,
          { fenceAgent, fenceName, fenceParameters },
        ) => (
          <FlexBox row>
            <BodyText>{fenceName}</BodyText>
            <BodyText>
              {Object.entries(fenceParameters).reduce<ReactNode>(
                (previous, [parameterId, parameterValue]) => {
                  let current: ReactNode = <>{parameterId}=&quot;</>;

                  current = /passw/i.test(parameterId) ? (
                    <>
                      {current}
                      <SensitiveText inline>{parameterValue}</SensitiveText>
                    </>
                  ) : (
                    <>
                      {current}
                      {parameterValue}
                    </>
                  );

                  return (
                    <>
                      {previous} {current}&quot;
                    </>
                  );
                },
                fenceAgent,
              )}
            </BodyText>
          </FlexBox>
        )}
      />
    ),
    [fenceOverviews, fenceTemplate, isEditFences],
  );
  const panelContent = useMemo(
    () =>
      isLoadingFenceTemplate || isFenceOverviewsLoading ? (
        <Spinner />
      ) : (
        listElement
      ),
    [isFenceOverviewsLoading, isLoadingFenceTemplate, listElement],
  );

  if (isFirstRender) {
    api
      .get<APIFenceTemplate>(`/fence/template`)
      .then(({ data }) => {
        setFenceTemplate(data);
      })
      .catch((error) => {
        handleAPIError(error);
      })
      .finally(() => {
        setIsLoadingFenceTemplate(false);
      });
  }

  return (
    <>
      <Panel>
        <PanelHeader>
          <HeaderText>Manage fence devices</HeaderText>
        </PanelHeader>
        {panelContent}
      </Panel>
      <ConfirmDialog
        dialogProps={{
          PaperProps: { sx: { minWidth: { xs: '90%', md: '50em' } } },
        }}
        formContent
        scrollBoxProps={{
          padding: '.3em .5em',
        }}
        scrollContent
        {...formDialogProps}
        ref={formDialogRef}
      />
      <ConfirmDialog
        scrollBoxProps={{ paddingRight: '1em' }}
        scrollContent
        {...confirmDialogProps}
        ref={confirmDialogRef}
      />
    </>
  );
};

export default ManageFencePanel;
