import { Assignment as AssignmentIcon } from '@mui/icons-material';
import { Grid } from '@mui/material';
import { useRouter } from 'next/router';
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';

import API_BASE_URL from '../lib/consts/API_BASE_URL';
import { BLACK } from '../lib/consts/DEFAULT_THEME';

import api from '../lib/api';
import ConfirmDialog from './ConfirmDialog';
import ContainedButton from './ContainedButton';
import FlexBox from './FlexBox';
import GeneralInitForm, {
  GeneralInitFormForwardedRefContent,
  GeneralInitFormValues,
} from './GeneralInitForm';
import handleAPIError from '../lib/handleAPIError';
import IconButton from './IconButton';
import IconWithIndicator, {
  IconWithIndicatorForwardedRefContent,
} from './IconWithIndicator';
import JobSummary, { JobSummaryForwardedRefContent } from './JobSummary';
import Link from './Link';
import MessageBox, { Message } from './MessageBox';
import NetworkInitForm, {
  NetworkInitFormForwardedRefContent,
  NetworkInitFormValues,
} from './NetworkInitForm';
import { Panel, PanelHeader } from './Panels';
import setMapNetwork from '../lib/setMapNetwork';
import Spinner from './Spinner';
import { BodyText, HeaderText, InlineMonoText, MonoText } from './Text';

const StrikerInitForm: FC = () => {
  const {
    isReady,
    query: { re },
  } = useRouter();

  const [submitMessage, setSubmitMessage] = useState<Message | undefined>();
  const [requestBody, setRequestBody] = useState<
    (GeneralInitFormValues & NetworkInitFormValues) | undefined
  >();
  const [isOpenConfirm, setIsOpenConfirm] = useState<boolean>(false);
  const [isDisableSubmit, setIsDisableSubmit] = useState<boolean>(true);
  const [isGeneralInitFormValid, setIsGeneralInitFormValid] =
    useState<boolean>(false);
  const [isNetworkInitFormValid, setIsNetworkInitFormValid] =
    useState<boolean>(false);
  const [isSubmittingForm, setIsSubmittingForm] = useState<boolean>(false);
  const [hostNumber, setHostNumber] = useState<string | undefined>();

  const [hostDetail, setHostDetail] = useState<APIHostDetail | undefined>();

  // Make sure the fetch for host detail only happens once.
  const allowGetHostDetail = useRef<boolean>(true);

  const generalInitFormRef = useRef<GeneralInitFormForwardedRefContent>({});
  const networkInitFormRef = useRef<NetworkInitFormForwardedRefContent>({});

  const jobIconRef = useRef<IconWithIndicatorForwardedRefContent>({});
  const jobSummaryRef = useRef<JobSummaryForwardedRefContent>({});

  const [panelTitle, setPanelTitle] = useState<string>('Loading...');

  const reconfig = useMemo<boolean>(() => Boolean(re), [re]);

  const buildSubmitSection = useMemo(
    () =>
      isSubmittingForm ? (
        <Spinner />
      ) : (
        <FlexBox row sx={{ flexDirection: 'row-reverse' }}>
          <ContainedButton
            disabled={isDisableSubmit}
            onClick={() => {
              setRequestBody({
                ...(generalInitFormRef.current.get?.call(null) ?? {}),
                ...(networkInitFormRef.current.get?.call(null) ?? {
                  networks: [],
                }),
              });

              setIsOpenConfirm(true);
            }}
          >
            Initialize
          </ContainedButton>
        </FlexBox>
      ),
    [isDisableSubmit, isSubmittingForm],
  );

  const toggleSubmitDisabled = useCallback((...testResults: boolean[]) => {
    setIsDisableSubmit(!testResults.every((testResult) => testResult));
  }, []);

  useEffect(() => {
    if (!isReady) return;

    if (!reconfig) {
      setPanelTitle('Initialize striker');

      return;
    }

    if (!allowGetHostDetail.current) return;

    allowGetHostDetail.current = false;

    api
      .get<APIHostDetail>('/host/local')
      .then(({ data }) => {
        setHostDetail(data);
        setPanelTitle(`Reconfigure ${data.shortHostName}`);
      })
      .catch((error) => {
        const emsg = handleAPIError(error);

        emsg.children = <>Failed to get host detail data. {emsg.children}</>;

        setSubmitMessage(emsg);
      });
  }, [isReady, reconfig, setHostDetail]);

  return (
    <>
      <Panel>
        <PanelHeader>
          <HeaderText>{panelTitle}</HeaderText>
          <IconButton
            onClick={({ currentTarget }) => {
              jobSummaryRef.current.setAnchor?.call(null, currentTarget);
              jobSummaryRef.current.setOpen?.call(null, true);
            }}
            variant="normal"
          >
            <IconWithIndicator icon={AssignmentIcon} ref={jobIconRef} />
          </IconButton>
        </PanelHeader>
        <FlexBox>
          <GeneralInitForm
            expectHostDetail={reconfig}
            hostDetail={hostDetail}
            onHostNumberBlurAppend={({ target: { value } }) => {
              setHostNumber(value);
            }}
            ref={generalInitFormRef}
            toggleSubmitDisabled={(testResult) => {
              if (testResult !== isGeneralInitFormValid) {
                setIsGeneralInitFormValid(testResult);
                toggleSubmitDisabled(testResult, isNetworkInitFormValid);
              }
            }}
          />
          <NetworkInitForm
            expectHostDetail={reconfig}
            hostDetail={hostDetail}
            hostSequence={hostNumber}
            ref={networkInitFormRef}
            toggleSubmitDisabled={(testResult) => {
              if (testResult !== isNetworkInitFormValid) {
                setIsNetworkInitFormValid(testResult);
                toggleSubmitDisabled(isGeneralInitFormValid, testResult);
              }
            }}
          />
          {submitMessage && (
            <MessageBox
              {...submitMessage}
              onClose={() => setSubmitMessage(undefined)}
            />
          )}
          {buildSubmitSection}
        </FlexBox>
      </Panel>
      <ConfirmDialog
        actionProceedText="Initialize"
        content={
          <Grid container spacing=".6em" columns={{ xs: 2 }}>
            <Grid item xs={1}>
              <BodyText>Organization name</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.organizationName}</MonoText>
            </Grid>
            <Grid item xs={1}>
              <BodyText>Organization prefix</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.organizationPrefix}</MonoText>
            </Grid>
            <Grid item xs={1}>
              <BodyText>Striker number</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.hostNumber}</MonoText>
            </Grid>
            <Grid item xs={1}>
              <BodyText>Domain name</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.domainName}</MonoText>
            </Grid>
            <Grid item xs={1}>
              <BodyText>Host name</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.hostName}</MonoText>
            </Grid>
            <Grid item sx={{ marginTop: '1.4em' }} xs={2}>
              <BodyText>Networks</BodyText>
            </Grid>
            {requestBody?.networks.map(
              ({
                inputUUID,
                interfaces,
                ipAddress,
                name,
                subnetMask,
                type,
                typeCount,
              }) => (
                <Grid key={`network-confirm-${inputUUID}`} item xs={1}>
                  <Grid container spacing=".6em" columns={{ xs: 2 }}>
                    <Grid item xs={2}>
                      <BodyText>
                        {name} (
                        <InlineMonoText>
                          {`${type.toUpperCase()}${typeCount}`}
                        </InlineMonoText>
                        )
                      </BodyText>
                    </Grid>
                    {interfaces.map((iface, ifaceIndex) => {
                      let key = `network-confirm-${inputUUID}-interface${ifaceIndex}`;
                      let ifaceName = 'none';

                      if (iface) {
                        const { networkInterfaceName, networkInterfaceUUID } =
                          iface;

                        key = `${key}-${networkInterfaceUUID}`;
                        ifaceName = networkInterfaceName;
                      }

                      return (
                        <Grid container key={key} item>
                          <Grid item xs={1}>
                            <BodyText>{`Link ${ifaceIndex + 1}`}</BodyText>
                          </Grid>
                          <Grid item xs={1}>
                            <MonoText>{ifaceName}</MonoText>
                          </Grid>
                        </Grid>
                      );
                    })}
                    <Grid item xs={2}>
                      <MonoText>{`${ipAddress}/${subnetMask}`}</MonoText>
                    </Grid>
                  </Grid>
                </Grid>
              ),
            )}
            <Grid item sx={{ marginBottom: '1.4em' }} xs={2} />
            <Grid item xs={1}>
              <BodyText>Gateway</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.gateway}</MonoText>
            </Grid>
            <Grid item xs={1}>
              <BodyText>Gateway network</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>
                {requestBody?.gatewayInterface?.toUpperCase()}
              </MonoText>
            </Grid>
            <Grid item xs={1}>
              <BodyText>Domain name server(s)</BodyText>
            </Grid>
            <Grid item xs={1}>
              <MonoText>{requestBody?.dns}</MonoText>
            </Grid>
          </Grid>
        }
        dialogProps={{ open: isOpenConfirm }}
        onCancelAppend={() => {
          setIsOpenConfirm(false);
        }}
        onProceedAppend={() => {
          setSubmitMessage(undefined);
          setIsSubmittingForm(true);
          setIsOpenConfirm(false);

          api
            .put('/init', requestBody)
            .then(() => {
              // Stop network mapping only on successful form submission.
              setMapNetwork(0);

              setIsSubmittingForm(false);
              setSubmitMessage({
                children: reconfig ? (
                  <>Successfully initiated reconfiguration.</>
                ) : (
                  <>
                    Successfully registered the configuration job! You can check
                    the progress at the top right icon. Once the job completes,
                    you can access the{' '}
                    <Link
                      href="/login"
                      sx={{ color: BLACK, display: 'inline-flex' }}
                    >
                      login page
                    </Link>
                    .
                  </>
                ),
                type: 'info',
              });
            })
            .catch((error) => {
              const errorMessage = handleAPIError(error);

              setSubmitMessage(errorMessage);
              setIsSubmittingForm(false);
            });
        }}
        titleText="Confirm striker initialization"
      />
      <JobSummary
        getJobUrl={(epoch) => `${API_BASE_URL}/init/job?start=${epoch}`}
        onFetchSuccessAppend={(jobs) => {
          jobIconRef.current.indicate?.call(null, Object.keys(jobs).length > 0);
        }}
        ref={jobSummaryRef}
      />
    </>
  );
};

export default StrikerInitForm;
