Загрузка данных


import { OTHER_VALUES } from '@/domains/controller/components/CreateController/model/formNames';
import { useControllerRepository } from '@/domains/equipment/repositories/controller.repository';
import { useNetInterfaceRepository } from '@/domains/equipment/repositories/netInterface.repository';
import { useGetEquipmentWindowQuery } from '@/shared/api/auth/equipment/client';
import { Helpers } from '@/shared/lib';
import { yupResolver } from '@hookform/resolvers/yup';
import { QueryStatus } from '@reduxjs/toolkit/query';
import {
  DeviceModel,
  useGetApiV1SecondaryInterfaceValueOwnerByOwnerIdQuery,
  useGetControllerByIdQuery,
} from '@shared/api/controller/client';
import {
  Button,
  Drawer,
  DrawerActions,
  DrawerProps,
  Loader,
} from '@shared/components';
import { useCloseDrawer } from '@shared/hooks/drawer/useCloseDrawer';
import { useSelectDrawerType } from '@shared/hooks/drawer/useSelectDrawerType';
import {
  EventActionTypes,
  useSendClickStreamEvent,
} from '@shared/hooks/useSendClickStreamEvent';
import { DrawerTypes } from '@shared/model/drawer/drawer.types';
import { useControllerActions } from '@shared/store/сontrollers/controllers.actions';
import {
  useSelectActiveControllerId,
  useSelectControllerLocationId,
} from '@shared/store/сontrollers/сontrollers.selectors';
import { useSyncControlPage } from '@widgets/Monitoring/hooks';
import { memo, useEffect, useState } from 'react';
import {
  FormProvider,
  Resolver,
  SubmitHandler,
  useForm,
} from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { ConfirmIpChangeModal } from './ConfirmIpChangeModal';
import { UpdateControllerForm } from './UpdateControllerForm';
import { OTHER_NAMES } from './model/formNames';
import { UpdateControllerFormData } from './model/types';
import { updateControllerFormSchema } from './validation/validator';

const FIVE_SECONDS = 5000;

export const UpdateControllerDrawer = memo(() => {
  const { t } = useTranslation();

  const { sendEvent } = useSendClickStreamEvent(
    EventActionTypes.createController,
  );

  const { cleanControllerLocationId } = useControllerActions();

  const closeDrawer = useCloseDrawer();
  const drawerType = useSelectDrawerType();
  const open = drawerType[DrawerTypes.updateController];

  const controllerId = useSelectActiveControllerId();
  const locationId = useSelectControllerLocationId();

  const {
    updateController,
    isUpdateControllerLoading,
    controllerUpdateStatus,
    resetUpdateController,
  } = useControllerRepository();

  const { data: controller } = useGetControllerByIdQuery(
    { controllerId: controllerId ?? '' },
    {
      skip: !controllerId,
      refetchOnMountOrArgChange: true,
    },
  );

  const id = controller?.id;
  const currentIp = controller?.ip?.trim() ?? '';

  const { data: equipmentWindowData } = useGetEquipmentWindowQuery(
    { ip: currentIp },
    {
      skip: !currentIp || !open,
      pollingInterval: FIVE_SECONDS,
    },
  );

  const hasActiveWindows =
    !!equipmentWindowData?.status && equipmentWindowData.status !== 'absent';

  const {
    data: secondaryInterfaceData,
    isSuccess: isSecondaryInterfaceSuccess,
  } = useGetApiV1SecondaryInterfaceValueOwnerByOwnerIdQuery(
    { ownerId: id! },
    { skip: !id },
  );

  const [model, setModel] = useState<DeviceModel | null>(null);
  const [pendingValues, setPendingValues] =
    useState<UpdateControllerFormData | null>(null);

  const defaultValues: UpdateControllerFormData = {
    name: controller?.name,
    description: controller?.description,
    locationId: controller?.locationId,
    firmware: controller?.firmware,
    controlPage: controller?.controlPage,
    subnetMask: controller?.subnetMask,
    gateway: controller?.gateway,
    telnetPort: controller?.telnetPort,
    port: controller?.interface?.port,
    timeZone: controller?.timeZone,
    sendNotifications: controller?.sendNotifications,
    ip: controller?.ip,
    mac: controller?.mac,
    typeName: controller?.model?.type?.name,
    manufacturerName: controller?.model?.manufacturer?.name,
    modelId: controller?.model?.id,
    segment: controller?.segment,
    serialNumber: controller?.serialNumber,
    inventoryNumber: controller?.inventoryNumber,
    protocolIp: controller?.interface?.protocolIp,
    localAddress: controller?.interface?.localAddress,
    ieee8021x: controller?.interface?.ieee8021x,
    isMain: controller?.isMain,
    netInterface: !!secondaryInterfaceData?.length,
    netInterfaceInfo: secondaryInterfaceData?.map((secondaryInterface) => {
      return {
        gateway: secondaryInterface.gateway,
        mac: secondaryInterface.mac,
        ip: secondaryInterface.ip,
        subnetMask: secondaryInterface.subnetMask,
        nameId: secondaryInterface.name?.id,
        id: secondaryInterface.id,
      };
    }),
    disableMonitoring: controller?.disableMonitoring ?? false,
    qrCodeType: controller?.qrCodeType,
  };

  const methods = useForm<UpdateControllerFormData>({
    mode: 'onBlur',
    reValidateMode: 'onChange',
    defaultValues,
    resolver: yupResolver(
      updateControllerFormSchema,
    ) as Resolver<UpdateControllerFormData>,
  });

  const handleClearModel = () => {
    methods.setValue('modelId', '');
    methods.setValue('manufacturerName', '');
    methods.setValue('typeName', '');
  };

  useEffect(() => {
    if (controller?.model) {
      setModel(controller.model);
    }
  }, [controller]);

  useEffect(() => {
    if (model) {
      methods.setValue('manufacturerName', model?.manufacturer?.name);
      methods.setValue('typeName', model?.type?.name);
    }
  }, [model]);

  useEffect(() => {
    if (isSecondaryInterfaceSuccess && secondaryInterfaceData) {
      methods.setValue(
        OTHER_VALUES.netInterface,
        !!secondaryInterfaceData?.length,
      );

      methods.setValue(
        OTHER_VALUES.netInterfaceInfo,
        secondaryInterfaceData?.map((secondaryInterface) => {
          return {
            gateway: secondaryInterface.gateway,
            mac: secondaryInterface.mac,
            ip: secondaryInterface.ip,
            subnetMask: secondaryInterface.subnetMask,
            nameId: secondaryInterface.name?.id,
            id: secondaryInterface.id,
          };
        }),
      );
    }
  }, [isSecondaryInterfaceSuccess, secondaryInterfaceData]);

  const netInterfaceInfo = methods.watch(OTHER_NAMES.netInterfaceInfo);
  const netInterface = methods.watch(OTHER_NAMES.netInterface);

  const { deleteNetInterfaces } = useNetInterfaceRepository();

  useEffect(() => {
    (async () => {
      if (
        !netInterface &&
        isSecondaryInterfaceSuccess &&
        netInterfaceInfo &&
        !!netInterfaceInfo.length
      ) {
        await deleteNetInterfaces(
          netInterfaceInfo?.map((netInterface) => netInterface.id),
        );

        methods.setValue(OTHER_VALUES.netInterfaceInfo, []);
      }
    })();
  }, [netInterface, netInterfaceInfo, isSecondaryInterfaceSuccess]);

  useSyncControlPage(controller?.ip, methods);

  useEffect(() => {
    if (!Helpers.isEmpty(locationId)) {
      methods.setValue(OTHER_NAMES.locationId, locationId);
      methods.reset(methods.getValues());
    }
  }, [locationId]);

  useEffect(() => {
    if (controllerUpdateStatus === QueryStatus.fulfilled && controller?.id) {
      closeDrawer(DrawerTypes.updateController);
      resetUpdateController();
      sendEvent();
    }
  }, [controller?.id, controllerUpdateStatus]);

  const updateNetworkDevice = async (values: UpdateControllerFormData) => {
    if (!id) {
      return;
    }

    await updateController(values, id);
    cleanControllerLocationId();
  };

  const submitHandler: SubmitHandler<UpdateControllerFormData> = async (
    values,
  ) => {
    const oldIp = controller?.ip?.trim() ?? '';
    const newIp = values.ip?.trim() ?? '';

    if (oldIp !== newIp && hasActiveWindows) {
      setPendingValues(values);
      return;
    }

    await updateNetworkDevice(values);
  };

  const handleConfirmIpChange = async () => {
    if (!pendingValues) {
      return;
    }

    const values = pendingValues;

    setPendingValues(null);
    await updateNetworkDevice(values);
  };

  const handleCancelIpChange = () => {
    setPendingValues(null);
  };

  useEffect(() => {
    methods.reset(defaultValues);
  }, [controller, open]);

  const { isValid, dirtyFields } = methods.formState;

  const isDirtyLocationId =
    methods.getValues().locationId !== defaultValues.locationId;

  const isDirtyFields = !Helpers.isEmpty(dirtyFields) || isDirtyLocationId;
  const isSaveButtonDisabled = !isDirtyFields || !isValid;

  const actions: DrawerActions = {
    actionPrimary: (
      <Button
        onClick={methods.handleSubmit(submitHandler)}
        disabled={isSaveButtonDisabled}
        loading={isUpdateControllerLoading}
      >
        {controllerUpdateStatus === QueryStatus.pending ? (
          <Loader />
        ) : (
          t('common:buttons.save')
        )}
      </Button>
    ),
  };

  const handleClose: DrawerProps['onClose'] = () => {
    cleanControllerLocationId();
    methods.reset(defaultValues);
    setModel(null);
    setPendingValues(null);
  };

  if (!controller) {
    return null;
  }

  return (
    <>
      <FormProvider {...methods}>
        <Drawer
          type={DrawerTypes.updateController}
          actions={actions}
          title={t('common:drawerTitle.updateController')}
          onClose={handleClose}
        >
          <UpdateControllerForm
            initialModel={controller?.model}
            setModel={setModel}
            handleClearModel={handleClearModel}
          />
        </Drawer>
      </FormProvider>

      <ConfirmIpChangeModal
        open={!!pendingValues}
        oldIp={controller?.ip ?? ''}
        newIp={pendingValues?.ip ?? ''}
        onConfirm={handleConfirmIpChange}
        onClose={handleCancelIpChange}
      />
    </>
  );
});