import { useCallback, useEffect, useState } from 'react';
import {
  FileParameter,
  ObjectsClient,
  ObjectWithPropertiesEntity,
  PropertyEntity,
  SolutionEntity,
} from '../../../../shared-objects-client/src/shared/g';
import { instance } from '../../../../shared-objects-client/src/api.config';
import { usePropertyTable } from './propertyTable-hook';
import { GroupedObjectProperties } from '../../types';
import { DataTable, DataTableRowToggleEvent } from 'primereact/datatable';
import { Column } from 'primereact/column';
import EditProperty from '../../../../editors/src/components/editProperty/EditProperty';
import '../../property-index.css';
import { LocalStorageService, StorageKeys, TransparentLoader } from '../../../../../index';
import { useImmer } from 'use-immer';
import { useSynchronizationContext } from '../../../../../synchronization/hooks/useSynchronizationContext';
import { Categories } from '../../constants';

const ObjectsService = new ObjectsClient(undefined, instance);

interface Props {
  solution: undefined | SolutionEntity;
  selectedObjectId: string | undefined;
}

/**
 * Компонент таблицы свойств
 * @constructor
 */
const PropertiesTable = ({ solution, selectedObjectId }: Props) => {
  const [objectProperty, updateObjectProperty] = useImmer<ObjectWithPropertiesEntity | null>(null);
  const [expandedRows, setExpandedRows] = useState<PropertyEntity[]>([]);
  const [closedRowCategory, setClosedRowCategory] = useState<string[]>([]);
  const [isLoading, setIsLoading] = useState(false);

  const propertyTable = usePropertyTable();
  const { objectUpdated$ } = useSynchronizationContext();

  /**
   * Загрузка свойств объекта
   */
  const fetchObjectWithProperties = useCallback(async () => {
    if (!solution || !selectedObjectId) {
      return;
    }

    // Устанавливаем лоадер
    setIsLoading(true);

    // Получаем данные
    ObjectsService.getObjectWithPropertiesById(solution.id, selectedObjectId)
      .then(prepareDataToTable)
      .catch(console.error)
      .finally(() => {
        setIsLoading(false);
      });
  }, [solution, selectedObjectId]);

  useEffect(() => {
    fetchObjectWithProperties().catch(console.error);
  }, [fetchObjectWithProperties]);

  /**
   * Подписка на обновления объекта
   */
  useEffect(() => {
    const subscription = objectUpdated$.subscribe((entity) => {
      if (selectedObjectId && selectedObjectId[0] !== entity?.id) {
        return;
      }

      updateObjectProperty((draft) => {
        if (!draft) {
          return;
        }

        for (const property of entity.properties) {
          const propertyIndex = draft.properties.findIndex((x) => x.id === property.id);
          if (propertyIndex === -1) {
            continue;
          }
          draft.properties[propertyIndex] = property;
        }
      });
    });

    return () => subscription.unsubscribe();
  }, [objectUpdated$, updateObjectProperty, selectedObjectId]);

  /**
   * Инициализация свернутых строк
   */
  const resetClosedRows = useCallback(() => {
    if (!solution) {
      return;
    }

    // Получаем ранее сохраненные свернутые категории из LocalStorage
    const savedClosedRows = LocalStorageService.load<string[]>(StorageKeys.CLOSED_PROPERTY_TABLE_ROWS) || [];

    // Добавляем "базовые" в закрытые категории по умолчанию, если они не были сохранены ранее
    const defaultClosedRows = savedClosedRows?.includes(Categories.Basic)
      ? savedClosedRows
      : [...savedClosedRows, Categories.Basic];

    // Устанавливаем свернутые категории
    setClosedRowCategory(defaultClosedRows);
  }, [solution]);

  useEffect(() => {
    resetClosedRows();
  }, [resetClosedRows]);

  /**
   * Преобразует данные после запроса в подходящие для DataTable
   * @param objectWithProperties - объект со свойствами
   */
  const prepareDataToTable = (objectWithProperties: ObjectWithPropertiesEntity | undefined) => {
    if (!objectWithProperties) {
      return;
    }

    const objectWithSortedProperties = propertyTable.sortedByGroupProperty(objectWithProperties);
    updateObjectProperty(objectWithSortedProperties);
    extendRows(objectWithSortedProperties);
  };

  /**
   * Раскрывает все строки таблицы
   */
  const extendRows = (object: ObjectWithPropertiesEntity | undefined) => {
    const groupedProperties = object?.properties ? propertyTable.groupProperties(object?.properties) : undefined;
    const firstGroupElements = groupedProperties
      ?.filter((group: GroupedObjectProperties) => group.category && !closedRowCategory.includes(group.category))
      ?.map((group: GroupedObjectProperties) => group.properties[0]);

    firstGroupElements && setExpandedRows(firstGroupElements);
  };

  /**
   * Обрабатывает изменения свойства(всех, кроме файла) при редактировании
   * @param propertyId - id свойства
   * @param newValue - новое значение
   */
  const onPropertyValueChange = async (propertyId: string, newValue: string | undefined | null) => {
    if (!solution || !selectedObjectId) {
      return;
    }

    ObjectsService.updatePropertyValue(solution.id, selectedObjectId, propertyId, newValue ?? undefined)
      .catch(console.error)
      .finally(fetchObjectWithProperties);
  };

  /**
   * Обновление свойства "Файл"
   */
  const onFilePropertyChange = async (propertyId: string, file: FileParameter | undefined | null) => {
    if (!solution || !selectedObjectId) {
      return;
    }

    ObjectsService.updatePropertyFileValue(solution.id, selectedObjectId, propertyId, file)
      .catch(console.error)
      .finally(fetchObjectWithProperties);
  };

  /**
   * Устанавливает данные развернутых узлов
   * @param e
   */
  const handleRowToggle = (e: DataTableRowToggleEvent) => {
    if (!Array.isArray(e.data)) return;

    setClosedRows(e.data);
    setExpandedRows(e.data);
  };

  const setClosedRows = (newClosedRows: PropertyEntity[]) => {
    if (!solution) {
      return;
    }

    // Определяем, была группа свернута или развернута
    const wasCollapsed = newClosedRows.length < expandedRows.length;

    if (wasCollapsed) {
      // Находим свернутую категорию
      const closedProperty = expandedRows.find(
        (expandedRow) => !newClosedRows.some((newClosedRow) => newClosedRow.id === expandedRow.id),
      );

      if (closedProperty) {
        const closedRow = objectProperty?.properties.find((p) => p.id === closedProperty.id);
        if (closedRow?.category) {
          // Сворачиваем
          setClosedRowCategory([...closedRowCategory, closedRow.category]);
          LocalStorageService.save(StorageKeys.CLOSED_PROPERTY_TABLE_ROWS, [...closedRowCategory, closedRow.category]);
        }
      }
    } else {
      // Находим развернутую категорию
      const newId = newClosedRows.find((row) => !expandedRows.includes(row))?.id;

      if (!newId) {
        return;
      }
      const newRow = objectProperty?.properties.find((p) => p.id === newId);
      if (newRow?.category && closedRowCategory.includes(newRow.category)) {
        // Разворачиваем
        const updated = closedRowCategory.filter((x) => x !== newRow.category);
        setClosedRowCategory(updated);
        LocalStorageService.save(StorageKeys.CLOSED_PROPERTY_TABLE_ROWS, updated);
      }
    }
  };

  /**
   * Рендерит компонент для редактирования значения свойства в ячейку таблицы
   * @param objectProperty
   */
  const renderEditableCell = useCallback(
    (objectProperty: PropertyEntity) => (
      <EditProperty
        objectProperty={objectProperty}
        onPropertyChange={onPropertyValueChange}
        onFilePropertyChange={onFilePropertyChange}
        solution={solution}
        selectedObjectId={selectedObjectId}
      />
    ),
    [solution, selectedObjectId],
  );

  /**
   * Рендерит заголовок группы свойств
   * @param data
   */
  const renderGroupHeader = useCallback(
    (data: PropertyEntity) => <span className='font-bold text-gray-700 text-xs'>{data.category}</span>,
    [],
  );

  /**
   * Рендерит название свойства
   * @param objectProperty
   */
  const renderPropertyName = useCallback(
    (objectProperty: PropertyEntity) => (
      <div
        className={`${objectProperty.isReadOnly ? 'opacity-70' : ''}`}
        draggable={false}
        style={{ userSelect: 'text' }}
      >
        {objectProperty.name}
      </div>
    ),
    [],
  );

  /**
   * Рендерит развернутое содержимое строки таблицы.
   * Отображает все свойства той же категории, кроме текущего (rowData), т.к. текущее свойство уже отображается как заголовок группы.
   */
  const rowExpansionTemplate = useCallback(
    (rowData: PropertyEntity) => {
      // Фильтруем все свойства объекта, оставляя только те, которые принадлежат той же категории, что и текущая строка
      const categoryProperties = objectProperty?.properties.filter((prop) => prop.category === rowData.category) || [];

      // Исключаем из отображения текущее свойство (rowData), т.к. как оно уже показано как заголовок группы
      const otherProperties = categoryProperties.filter((prop) => prop.id !== rowData.id);

      // Рендерим развернутое содержимое группы
      return otherProperties.map((property) => (
        <div key={property.id}>
          <div>{renderPropertyName(property)}</div>
          <div>{renderEditableCell(property)}</div>
        </div>
      ));
    },
    [objectProperty?.properties, renderPropertyName, renderEditableCell],
  );

  const preventDrag = (e: any) => {
    e.preventDefault();
  };

  return (
    <div className='relative min-h-full max-h-full min-w-full w-full flex flex-column overflow-y-auto overflow-x-hidden flex-grow-1 flex-shrink-1'>
      {isLoading && <TransparentLoader />}
      {objectProperty && (
        <div className='border-1 border-gray-200 max-w-full property-table'>
          <DataTable
            value={objectProperty?.properties}
            rowGroupMode='subheader'
            groupRowsBy='category'
            sortMode='single'
            dataKey='id'
            sortOrder={1}
            expandableRowGroups
            expandedRows={expandedRows}
            onRowToggle={handleRowToggle}
            showHeaders={false}
            showGridlines
            rowGroupHeaderTemplate={renderGroupHeader}
            rowExpansionTemplate={rowExpansionTemplate}
            emptyMessage='Результаты не найдены'
            onDragStart={preventDrag}
          >
            <Column field='name' style={{ width: '10%', userSelect: 'text' }} body={renderPropertyName}></Column>
            <Column field='value' style={{ width: '10%', userSelect: 'text' }} body={renderEditableCell}></Column>
          </DataTable>
        </div>
      )}
    </div>
  );
};

export default PropertiesTable;


допиши сюда коммнетарии на каждую отдельную функцию чтобы каждая из них была опсиана