import React, {
  forwardRef,
  Fragment,
  useEffect,
  useMemo,
  useRef,
  useState,
  useCallback,
} from "react";
import { LicenseManager } from "ag-grid-enterprise";
import { AgGridReact } from "ag-grid-react"; // React Data Grid Component
import { useForm } from "react-hook-form";
import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the Data Grid
import "ag-grid-community/styles/ag-theme-alpine.css"; // Optional Theme applied to the Data Grid
import "./Table.styles.scss";

import TableHeader from "./tableHeader";
import AgGridHeader from "./AgGridHeader";
import TablePagination from "./tablePagination";
import {
  getMainMenuItems,
  Columns,
  updateFontSizeOnToolPanel,
} from "./tableFunctions";
import TableAdvanceSearchModal from "./advanceSearchModal";
import { convertToOptions } from "../../utils/helper";
import { withCommentIcon } from "./withCommentIcon";
import CommentsPortal from "./CommentsPortal";
import { Menu } from "../Menu";
import { Prompt } from "../Prompt";
import { ReactComponent as EditIcon } from "../../assets/edit-black.svg";
import { ReactComponent as DeleteIcon } from "../../assets/delete-black.svg";
import { ReactComponent as UnreadIcon } from "../../assets/Comment.svg";
import { ReactComponent as ReadIcon } from "../../assets/CheckTickAll.svg";
import { ReactComponent as EmptyStateImage} from "../../assets/emptyStateImage.svg"
import CommentPanel from "./commentPanel";
import NewAdvanceSearchModal from "./NewAdvanceSearchModal/NewAdvanceSearchModal";
import { buildAdvanceSearchFilterModel } from "./filterUtils";
import BulkActionBar from "./actionBar";
import AdvanceSortModal from "./AdvanceSortModal";

export const Table = forwardRef(
  (
    {
      defaultColDef: defColDef,
      columnDefs: colDefs,
      tableHeader = "",
      rowHeight: rowHei,
      height,
      topRightOptions,
      topLeftOptions,
      onGlobalSearchClick,
      paginationPageSizeSelector = [10, 20, 50, 100],
      defaultPageSize,
      onNumberFormatChange = () => {},
      onColumnSearchClick,
      tableActionOptions,
      bottomLeftOptions,
      cardContainer = true,
      hideRowHeightOptionMenu = false,
      onPaginationChanged = () => {},
      showDownloadButton = false,
      onDownloadButtonClick = () => {},
      nestedTable = false,
      nestedTableComponent = null,
      closeButton = false,
      handleCloseButtonClick,
      additionalButtons,
      applySort,
      onFilterChanged,
      sortModel,
      onSearchApplyClick,
      gridId,
      showHideEditableCells = false,
      handleShowHideEditableCells = () => {},
      topCenterOptions,
      bottomCenterOptions,
      toShowDuplicateColumnInAdvanceSearch = false,
      savedAdvanceSearchConfig,
      handleInlineSearch,
      showContentualFilter = false,
      showContentualFilterBadge = false,
      onContentualFilterClick = () => {},
      contentualFilterProps,
      contentualFilterComponent,
      onAdvanceSearchClick,
      tableActions,
      customGetMainMenuItems,
      hidePaginationPageSizeSelector = false,
      onTableActionsTabClick,
      hideTableFormat = false,
      hideTableActions = false,
      handleClearSearchInline,
      aboveTableComponent: belowHeaderComponent,
      hideTableSetting = false,
      explicitlyCloseTableSetting = false,
      handleCloseInputField,
      customHeaderComponent,
      debounceTime = 1500,
      hideMarginBottom = false,
      customSystemButton,
      isChatEnabled = false,
      handleChatClick = () => {},
      isChatOpen = false,
      handleChatClose = () => {},
      chatConfiguration,
      savedViewsOptions,
      handleSaveAndApply = () => {},
      maxSavedViews = 50,
      handleSavedViewClick = () => {},
      handleEditSaveView = () => {},
      isSaveViewEnabled = false,
      isSaveViewLoading = false,
      handleDeleteViewClick = () => {},
      isDeleteViewLoading = false,
      handleSetDefaultView = () => {},
      isCommentFeatureEnabled = false,
      commentProps,
      hideFontSize = false,
      emptyStateText = "No data to display",
      hideNumericFormat = false,
      onContentDensityChange,
      handleCommentNotification,
      customTabInTableSettings = null,
      closeTableSettingOnOutsideClick = false,
      saveViewCustomFilter = {
        options: [
          { label: "Global", value: "global" },
          { label: "Personal", value: "personal" },
        ],
        selectedOption: "global",
      },
      customRenderSaveViewContent = null,
      hideTableBody = false,
      onTableSettingClick = () => {},
      deleteViewPromptText,
      contentualFilterOpenByDefault = false,
      tableSettingsTitle,
      enableHeaderTextWrap = true,
      numericFormatDefault = "full_no",
      defaultFontSize = "medium",
      viewPoint = "",
      disableContextualFilter = false,
      disableTableSettings = false,
      showBulkActionBar = false,
      bulkActionBarProps = {
        actionBarLabel: "",
        enableSelectAll: true,
        totalRowCount: 0,
      },
      onSelectAllRows = () => {},
      onClearSelection = () => {},
      onSortChanged = () => {},
      advanceSortEnabled = false,
      ...args
    },
    ref
  ) => {
    LicenseManager.setLicenseKey(
      "Using_this_{AG_Grid}_Enterprise_key_{AG-072820}_in_excess_of_the_licence_granted_is_not_permitted___Please_report_misuse_to_legal@ag-grid.com___For_help_with_changing_this_key_please_contact_info@ag-grid.com___{Impact_Analytics}_is_granted_a_{Multiple_Applications}_Developer_License_for_{1}_Front-End_JavaScript_developer___All_Front-End_JavaScript_developers_need_to_be_licensed_in_addition_to_the_ones_working_with_{AG_Grid}_Enterprise___This_key_has_not_been_granted_a_Deployment_License_Add-on___This_key_works_with_{AG_Grid}_Enterprise_versions_released_before_{31_December_2025}____[v3]_[01]_MTc2NzEzOTIwMDAwMA==acb63fc998feb69e3a3ca9ad323b2577"
    );

    const tempRef = useRef();
    const actualRef = ref || tempRef;
    const [localPageSize, setLocalPageSize] = useState(
      defaultPageSize || paginationPageSizeSelector[0]
    );
    const [initialFilters, setInitialFilters] = useState([]);
    const [tableSettingOpen, setTableSettingOpen] = useState(false);

    const [rowHeight, setRowHeight] = useState(
      rowHei === "comfort" ? 52 : rowHei === "compact" ? 30 : 46
    );

    useEffect(() => {
      let newRowHeight;
      if (typeof rowHei === "number") {
        newRowHeight = rowHei;
      } else if (rowHei === "comfort") {
        newRowHeight = 52;
      } else if (rowHei === "compact") {
        newRowHeight = 30;
      } else {
        newRowHeight = 46;
      }
      setRowHeight(newRowHeight);
      if (actualRef?.current?.api) {
        setTimeout(() => {
          actualRef.current.api.resetRowHeights();
        }, 0);
      }
    }, [rowHei, actualRef]);

    const [tableFontSize, setTableFontSize] = useState(
      defaultFontSize || "medium"
    );
    const buttonRef = useRef(null);
    const nestedTableRef = useRef(null);
    const [isAdvanceSearch, setAdvanceSearch] = useState(false);
    const [isAdvanceSortOpen, setIsAdvanceSortOpen] = useState(false);
    const [advanceSortState, setAdvanceSortState] = useState([]);
    const [currentColumnId, setCurrentColumnId] = useState(null);
    const [draftFilter, setDraftFilter] = useState([]);
    const [showCommentPanel, setShowCommentPanel] = useState(false);
    const [
      selectedThirdOptionDropdownValue,
      setSelectedThirdOptionDropdownValue,
    ] = useState(null);
    const filteredColumnsInitialOptions = useMemo(
      () => convertToOptions(colDefs || []),
      [colDefs]
    );
    const formMethods = useForm({
      defaultValues: { filters: initialFilters },
    });
    const { getValues, setValue, reset } = formMethods;

    const toggleAdvanceSearch = (colId, searchTxt) => {
      setAdvanceSearch((prev) => !prev);

      if (savedAdvanceSearchConfig?.length > 0) {
        setInitialFilters(savedAdvanceSearchConfig);
        return;
      }

      if (Array.isArray(colId)) {
        // Handle multiple fields
        setCurrentColumnId(colId[0] || null); // You can pick the first one for UI reference

        setInitialFilters((prev) => {
          const newFilters = [...prev];

          colId.forEach((id) => {
            const isColumnHave = newFilters.find(
              (filter) =>
                filter?.column?.value === id && filter?.column !== null
            );

            if (!isColumnHave) {
              const columnOption = filteredColumnsInitialOptions?.find(
                (opt) => opt?.value === id
              );

              if (columnOption) {
                newFilters.push({
                  column: columnOption,
                  operation: null,
                  value: "",
                });
              }
            }
          });

          return newFilters;
        });
      } else if (typeof colId === "string" || typeof colId === "number") {
        // Handle single field
        setCurrentColumnId(colId || null);

        setInitialFilters((prev) => {
          const cleaned = prev.filter((f) => f.column !== null); // 🧹 clean up leftover blank fields

          const isColumnHave = cleaned.find(
            (filter) => filter?.column?.value === colId
          );

          if (isColumnHave) return cleaned;

          const columnOption = filteredColumnsInitialOptions?.find(
            (opt) => opt?.value === colId
          );
          return [
            ...cleaned,
            columnOption
              ? {
                  column: columnOption,
                  operation:
                    searchTxt?.length > 0
                      ? { label: "Contains", value: "contains" }
                      : "",
                  value: searchTxt,
                }
              : {},
          ];
        });
      }
    };

    const toggleNewAdvanceSearch = (colId, searchTxt) => {
      setAdvanceSearch((prev) => !prev);

      if (savedAdvanceSearchConfig?.length > 0) {
        setInitialFilters(savedAdvanceSearchConfig);
        return;
      }

      if (Array.isArray(colId)) {
        // Handle multiple fields
        setCurrentColumnId(colId[0] || null); // You can pick the first one for UI reference

        setInitialFilters((prev) => {
          const newFilters = [...prev];

          colId.forEach((id) => {
            const isColumnHave = newFilters.find(
              (filter) =>
                filter?.column?.value === id && filter?.column !== null
            );

            if (!isColumnHave) {
              const columnOption = filteredColumnsInitialOptions?.find(
                (opt) => opt?.value === id
              );

              if (columnOption) {
                newFilters.push({
                  column: columnOption,
                  operation: null,
                  value: "",
                });
              }
            }
          });

          return newFilters;
        });
      } else if (typeof colId === "string" || typeof colId === "number") {
        setCurrentColumnId(colId || null);
      }
    };

    useEffect(() => {
      if (defaultFontSize) setTableFontSize(defaultFontSize);
    }, [defaultFontSize]);

    const updateExpandState = useCallback(
      (params) => {
        let allExpanded = true;
        let hasGroups = false;

        params.api.forEachNode((node) => {
          if (node.group) {
            hasGroups = true;
            if (!node.expanded) {
              allExpanded = false;
            }
          }
        });

        if (hasGroups) {
          actualRef.current.api.getModel().isExpand = allExpanded;
        }
      },
      [actualRef]
    );

    const selectColumn = useCallback(
      (cols) => {
        actualRef?.current.api.clearRangeSelection();
        actualRef?.current.api.addCellRange({ columns: cols });
      },
      [actualRef]
    );

    const AgGridHeaderWrapper = useCallback(
      (props) => {
        const {
          selectColumn,
          onColumnSearchClick,
          applySort,
          onFilterChanged,
          sortModel,
          toggleAdvanceSearch,
          onAdvanceSearchClick,
          handleInlineSearch,
          handleClearSearchInline,
          handleCloseInputField,
          debounceTime,
          headerComponent,
          ...rest
        } = props;
        return (
          <AgGridHeader
            {...rest}
            sortModel={sortModel}
            onClick={() => selectColumn?.([rest?.column?.colId])}
            onColumnSearchClick={onColumnSearchClick}
            applySort={applySort}
            onFilterChanged={onFilterChanged}
            toggleAdvanceSearch={toggleAdvanceSearch}
            onAdvanceSearchClick={onAdvanceSearchClick}
            handleInlineSearch={handleInlineSearch}
            handleClearSearchInline={handleClearSearchInline}
            handleCloseInputField={handleCloseInputField}
            debounceTime={debounceTime}
            getValues={getValues}
            setValue={setValue}
            initialFilters={initialFilters}
            setInitialFilters={setInitialFilters}
            colDefs={colDefs}
            enableHeaderTextWrap={enableHeaderTextWrap}
          >
            {headerComponent ? headerComponent(props) : rest?.displayName}
          </AgGridHeader>
        );
      },
      [
        getValues,
        setValue,
        initialFilters,
        setInitialFilters,
        colDefs,
        onColumnSearchClick,
        applySort,
        onFilterChanged,
        toggleAdvanceSearch,
        onAdvanceSearchClick,
        handleInlineSearch,
        handleClearSearchInline,
        handleCloseInputField,
        debounceTime,
        selectColumn,
        enableHeaderTextWrap,
      ]
    );

    const defaultColDef = useMemo(() => {
      return {
        ...defColDef,
        filter: true,
        resizable: true,
        cellClassRules: {
          "editable-ag-cell": (params) => params.colDef.editable,
          "number-cell": (params) => params.colDef.type === "number",
          "default-cell": () => rowHeight === 46,
          "compact-cell": () => rowHeight === 30,
          "comfort-cell": () => rowHeight !== 46 && rowHeight !== 30,
        },
      };
    }, [defColDef, rowHeight]);

    const addSpecialAttributes = useCallback(
      (children) => {
        return children.map((child) => {
          const obj = {
            ...child,
            headerClass:
              (child.headerClass || "") +
              (child.type === "number" ? " number-cell" : ""),
            width:
              child.checkboxSelection || child.headerCheckboxSelection
                ? viewPoint === "list"
                  ? "48px"
                  : "40px"
                : child.width || undefined,
            headerComponentParams: {
              ...child?.headerComponentParams,
              viewPoint,
              selectColumn,
              onColumnSearchClick,
              applySort,
              onFilterChanged,
              sortModel,
              toggleAdvanceSearch,
              onAdvanceSearchClick,
              handleInlineSearch,
              handleClearSearchInline,
              handleCloseInputField,
              debounceTime,
              getValues,
              setValue,
              initialFilters,
              setInitialFilters,
              colDefs,
              headerComponent: child?.headerComponent,
            },
            headerComponent: customHeaderComponent
              ? customHeaderComponent
              : AgGridHeaderWrapper,
          };
          // Automatically add filterParams comparator for date columns if not already present
          if (
            child.filter === "agDateColumnFilter" &&
            !child.filterParams?.comparator
          ) {
            obj.filterParams = {
              ...child.filterParams,
              // Enable all filter buttons/operators
              buttons: ["apply", "clear", "reset"],
              // Explicitly enable all date filter options
              filterOptions: [
                "equals",
                "notEqual",
                "lessThan",
                "lessThanOrEqual",
                "greaterThan",
                "greaterThanOrEqual",
                "inRange",
              ],
              // Suppress the filter options UI (we're using advance search)
              suppressAndOrCondition: true,
              // Add comparator for date comparison
              comparator: (filterLocalDateAtMidnight, cellValue) => {
                if (!cellValue) return -1;
                const cellDate = new Date(cellValue);
                cellDate.setHours(0, 0, 0, 0);
                if (cellDate < filterLocalDateAtMidnight) return -1;
                if (cellDate > filterLocalDateAtMidnight) return 1;
                return 0;
              },
            };
          }

          if (child.children) {
            obj.children = addSpecialAttributes(child.children);
          }
          return obj;
        });
      },
      [rowHeight]
    );

    const [commentMode, setCommentMode] = useState(false);

    // Function to wrap components in the components prop with withCommentIcon
    const wrapComponentsWithCommentIcon = (components) => {
      // Defensive check to ensure components is a valid object
      if (!components || typeof components !== "object") {
        return components;
      }

      const wrappedComponents = {};
      Object.keys(components).forEach((key) => {
        const Component = components[key];
        if (typeof Component === "function") {
          wrappedComponents[key] = withCommentIcon(Component);
        } else {
          wrappedComponents[key] = Component;
        }
      });

      return wrappedComponents;
    };

    // Recursive function to apply withCommentIcon to all columns, including nested ones
    const applyCommentIconToColumns = (colDefs) => {
      // Only apply comment icon wrapping if the comment feature is enabled
      if (!isCommentFeatureEnabled) {
        return colDefs;
      }

      return colDefs?.map((col) => {
        if (col.children) {
          // If it has children, recursively apply to nested columns
          return {
            ...col,
            children: applyCommentIconToColumns(col.children),
          };
        } else {
          // If it's a leaf node, apply withCommentIcon wrapper to ALL cells
          // Handle different cellRenderer scenarios:
          // 1. No cellRenderer (normal cells) → wrap with withCommentIcon(undefined)
          // 2. Function/object cellRenderer → wrap with withCommentIcon(cellRenderer)
          // 3. String cellRenderer → leave untouched (handled by components prop)

          const isStringRenderer = typeof col.cellRenderer === "string";

          return {
            ...col,
            cellRenderer: isStringRenderer
              ? col.cellRenderer // Leave string references untouched for AG Grid to resolve
              : withCommentIcon(col.cellRenderer), // Wrap all others (including undefined/null)
          };
        }
      });
    };

    const columnDefs = useMemo(() => {
      // Removed imperative setGridOption here as it conflicts with standard React flow
      // and defaultColDef now handles the context injection.
      return addSpecialAttributes(colDefs);
    }, [colDefs, addSpecialAttributes]);

    // Compute final column definitions once, based on props and local settings
    const finalColumnDefs = useMemo(() => {
      if (!columnDefs || columnDefs.length === 0) {
        return null;
      }
      const applyStyleRecursively = (columns) =>
        columns?.map((column) => {
          if (Array.isArray(column.children)) {
            return {
              ...column,
              children: applyStyleRecursively(column.children),
            };
          }
          if (column?.field === "Selection") {
            return column;
          }
          const originalCellStyle = column.cellStyle;
          return {
            ...column,
            cellStyle: (params) => {
              const baseStyle =
                typeof originalCellStyle === "function"
                  ? originalCellStyle(params)
                  : originalCellStyle || {};
              return {
                ...baseStyle,
                fontSize: updateFontSizeOnToolPanel(tableFontSize),
              };
            },
          };
        });
      let computed = actualRef?.current?.api?.getColumnDefs() || columnDefs;
      if (tableFontSize) {
        computed = applyStyleRecursively(columnDefs);
        const existingAutoGroup =
          actualRef?.current?.api?.getGridOption?.("autoGroupColumnDef") || {};
        const existingCellStyle = existingAutoGroup?.cellStyle;
        actualRef?.current?.api?.setGridOption("autoGroupColumnDef", {
          ...existingAutoGroup,
          cellStyle: (params) => {
            const baseStyle =
              typeof existingCellStyle === "function"
                ? existingCellStyle(params)
                : existingCellStyle || {};
            return {
              ...baseStyle,
              fontSize: updateFontSizeOnToolPanel(tableFontSize),
            };
          },
        });
      }
      computed = applyCommentIconToColumns(computed);
      if (actualRef?.current?.api)
        actualRef.current.api.getModel().appliedFontSize = tableFontSize;
      return computed;
    }, [columnDefs, tableFontSize]);

    const onFirstDataRendered = (params) => {
      if (params?.api) params.api.getModel().appliedFontSize = tableFontSize;
      args?.onFirstDataRendered?.(params);
      args?.gridOptions?.onFirstDataRendered?.(params); 
    };

    // Memoize wrapped components to avoid unnecessary re-renders
    const wrappedComponents = useMemo(() => {
      // Only process components if comment feature is enabled
      if (!isCommentFeatureEnabled || !args?.components) {
        return args?.components;
      }
      return wrapComponentsWithCommentIcon(args?.components);
    }, [args?.components, isCommentFeatureEnabled]);

    const setDynmaicStyleToButtonComponent = useCallback(() => {
      const span1 = document.querySelector(".ag-paging-row-summary-panel");
      const span2 = document.querySelector(".ag-paging-page-summary-panel");
      if (span1 || span2) {
        let actualWidth =
          (span1?.offsetWidth || 0) + (span2?.offsetWidth || 0) + 20;
        if (buttonRef?.current) {
          buttonRef.current.style.right = `${actualWidth}px`;
          if (tableSettingOpen) {
            buttonRef.current.style.right = `${actualWidth + 375}px`;
          } else {
            buttonRef.current.style.right = `${actualWidth}px`;
          }
        }
      }
    }, [buttonRef, tableSettingOpen]);

    const paginationChanged = useCallback(
      (params) => {
        setDynmaicStyleToButtonComponent();
        onPaginationChanged(params);
      },
      [setDynmaicStyleToButtonComponent, onPaginationChanged]
    );

    useEffect(() => {
      setDynmaicStyleToButtonComponent();
      const div = document.querySelector(".ag-root-wrapper-body");
      if (div) {
        let actualHeight = div.offsetHeight;
        let tableContainer = document.querySelector(`#${gridId || "myGrid"}`);
        if (tableSettingOpen || commentMode) {
          if (actualHeight < 480) {
            if (tableContainer) {
              tableContainer.style.height = `551px`;
              if (buttonRef && buttonRef.current)
                buttonRef.current.style.bottom = `${480 - actualHeight - 37}px`;
            }
          } else {
            const panelEl = document.querySelector(
              tableSettingOpen
                ? ".display-table-setting"
                : ".display-comment-panel"
            );
            if (panelEl) {
              panelEl.style.height = `${actualHeight}px`;
            }
          }
        } else {
          if (tableContainer) tableContainer.style.height = `unset`;
          if (buttonRef && buttonRef.current)
            buttonRef.current.style.bottom = `13px`;
        }
      }
    }, [tableSettingOpen, commentMode, cardContainer]);

    useEffect(() => {
      if (nestedTable && nestedTableRef.current) {
        const rect = nestedTableRef.current.getBoundingClientRect();
        setTimeout(() => {
          window.scrollTo({
            top: rect.top,
            behavior: "smooth",
          });
        }, 100);
      }
    }, [nestedTable]);

    const onSearchApplyClicks = (formValues) => {
      // Guard against undefined formValues
      if (!formValues || !Array.isArray(formValues)) {
        console.warn(
          "onSearchApplyClicks: formValues is undefined or not an array"
        );
        return;
      }

      // If formValues is empty (cleared all), set to empty filter format
      // Otherwise, set to the actual filters
      if (formValues?.length === 0) {
        setInitialFilters([{ column: null, operation: null, value: "" }]);
      } else {
        setInitialFilters([...formValues]);
      }

      const rest = actualRef?.current;
      if (!rest?.api) {
        console.warn("onSearchApplyClicks: AG Grid API not available");
        return;
      }

      // Build filter model using utility function
      const filterModel = buildAdvanceSearchFilterModel(formValues, colDefs);

      // Set filter model on AG Grid
      rest.api.setFilterModel(filterModel);

      // For server-side tables, trigger callback with formValues
      if (onSearchApplyClick) {
        onSearchApplyClick(formValues);
      } else {
        // For client-side tables, trigger filter changed event
        rest.api.onFilterChanged();
      }

      // Handle empty state overlay after filters are applied
      setTimeout(() => {
        if (rest?.api) {
          const displayedRowCount = rest.api.getDisplayedRowCount();
          if (displayedRowCount === 0) {
            rest.api.showNoRowsOverlay();
          } else {
            rest.api.hideOverlay();
          }
        }
      }, 100); // Small delay to ensure filters are processed

      toggleAdvanceSearch();
    };

    const handleAdvanceSortApply = useCallback(
      (rows) => {
        const state = rows.map((row, i) => ({
          colId: row.colId,
          sort: row.order,
          sortIndex: i,
        }));
        setAdvanceSortState(state);
        actualRef.current?.api?.applyColumnState({
          state,
        });
        setIsAdvanceSortOpen(false);
      },
      [actualRef]
    );

    const onGridSortChanged = useCallback((params) => {
      const colState = params?.api?.getColumnState() ?? [];
      const sorted = colState
        .filter((c) => c.sort != null)
        .sort((a, b) => (a.sortIndex ?? 0) - (b.sortIndex ?? 0))
        .map((c) => ({ colId: c.colId, sort: c.sort, sortIndex: c.sortIndex }));
      setAdvanceSortState(sorted);
      onSortChanged?.(params);
    }, []);

    const [comments, setComments] = useState(commentProps?.comments || {});
    const [resolvedComments, setResolvedComments] = useState(
      commentProps?.resolvedComments || []
    );
    const [isCommentResolvedAlertOpen, setIsCommentResolvedAlertOpen] =
      useState(false);
    // Store original comments and resolvedComments before switching to "Show resolved comments"
    // so we can restore them when user switches to another sort option
    const commentsBeforeResolvedViewRef = useRef(null);
    const resolvedCommentsBeforeResolvedViewRef = useRef(null);
    useEffect(() => {
      if (commentProps?.comments) {
        setComments(commentProps.comments);
      }
      if (commentProps?.resolvedComments) {
        setResolvedComments(commentProps.resolvedComments);
      }
    }, [commentProps]);

    // Create a memoized function to prevent infinite re-renders
    const triggerCommentClick = useCallback(
      (cellId, messageId = null) => {
        handleCommentItemClick(cellId, comments[cellId], messageId);
      },
      [comments]
    );

    // Expose handleCommentItemClick to outside through handleCommentNotification prop
    useEffect(() => {
      if (
        handleCommentNotification &&
        typeof handleCommentNotification === "function"
      ) {
        // Call the prop function and pass our memoized function
        handleCommentNotification(triggerCommentClick);
      }
    }, [handleCommentNotification, triggerCommentClick]);

    const [popup, setPopup] = useState(null);

    useEffect(() => {
      // Force AG Grid to refresh cell renderers when popup state changes
      if (actualRef.current?.api) {
        actualRef.current.api.refreshCells({
          force: true,
          suppressFlash: true,
        });
      }
    }, [popup, resolvedComments]);

    useEffect(() => {
      const scrollableElement = document.querySelector(".ag-body-viewport");
      const bodyElement = document.querySelector("body");

      const preventScroll = (e) => {
        // Allow scrolling inside specific scrollable areas but prevent scroll chaining
        const portalElement = e.target.closest(".comments-portal");
        const portalListElement = e.target.closest(".comments-portal-list");
        const panelListElement = e.target.closest(".comment-panel-content");
        const textareaElement = e.target.closest(".comments-textarea");
        const tiptapEditor =
          e.target.closest(".ProseMirror") ||
          e.target.closest(".tiptap-editor");

        // Choose the most relevant scrollable element (prioritize actual scrollable elements)
        const scrollEl =
          tiptapEditor ||
          portalListElement ||
          panelListElement ||
          textareaElement ||
          portalElement;

        if (scrollEl) {
          // Determine scroll direction
          const deltaY = typeof e.deltaY === "number" ? e.deltaY : 0;
          const scrollTop = scrollEl.scrollTop;
          const maxScrollTop = scrollEl.scrollHeight - scrollEl.clientHeight;
          const atTop = scrollTop <= 0;
          const atBottom = scrollTop >= maxScrollTop - 1; // -1 as a tolerance

          // If at the boundary and trying to scroll beyond, prevent default to stop bubbling to table/body
          if ((deltaY < 0 && atTop) || (deltaY > 0 && atBottom)) {
            e.preventDefault();
            return;
          }

          // Otherwise, keep the scroll within the element and stop it from bubbling further
          e.stopPropagation();
          return;
        }

        // Outside designated areas: prevent body/table scrolling when popup is open
        e.preventDefault();
      };

      if (popup && scrollableElement) {
        // Disable scroll by preventing the wheel event
        scrollableElement.addEventListener("wheel", preventScroll, {
          passive: false,
        });
      }

      if (popup && bodyElement) {
        // Disable scroll by preventing the wheel event
        bodyElement.addEventListener("wheel", preventScroll, {
          passive: false,
        });
      }

      // Cleanup function to re-enable scroll
      return () => {
        if (scrollableElement) {
          scrollableElement.removeEventListener("wheel", preventScroll);
        }
        if (bodyElement) {
          bodyElement.removeEventListener("wheel", preventScroll);
        }
      };
    }, [popup]);

    const calculatePopupPosition = useCallback(
      (rect, comments = [], openedBy = "hover") => {
        const gridEl = document.querySelector(".ag-root");
        const popupWidth = 350; // Estimated width of the comments portal
        const margin = 0; // No space between cell and popup as requested
        const maxPopupHeight = 500; // Cap height for comments portal when estimating

        // Use a different width when there are no comments (NoComments banner)
        const effectivePopupWidth =
          !comments || comments.length === 0 ? 200 : popupWidth;
        if (!gridEl) {
          // Fallback positioning if grid element isn't found
          if (window.innerWidth - rect.right < effectivePopupWidth) {
            return {
              top: rect.top,
              left: rect.left - effectivePopupWidth - margin,
              side: "left",
            };
          }
          return { top: rect.top, left: rect.right + margin, side: "right" };
        }

        const gridRect = gridEl.getBoundingClientRect();

        // --- Vertical size estimation ---
        // Calculate popup height based on three states
        let popupHeight;
        if (!comments || comments.length === 0) {
          // Case 1: No comments - NoComments banner height (approximate from styles)
          popupHeight = 20;
        } else if (openedBy === "hover") {
          // Case 2: Collapsed state (hover) - shows only first comment preview
          const firstComment = comments[0];
          const baseHeight = 30; // Minimum height for avatar, author, timestamp
          const maxHeight = 150; // Maximum height cap for hover
          if (firstComment && firstComment.text) {
            // Estimate text height based on character count (~45 chars/line, ~20px per line)
            const textLength = firstComment.text.length;
            const estimatedLines = Math.ceil(textLength / 45);
            const textHeight = estimatedLines * 20;
            popupHeight = Math.min(baseHeight + textHeight, maxHeight);
          } else {
            popupHeight = baseHeight;
          }
        } else {
          // Case 3: Expanded state (click) - shows all comments + header + input
          const headerHeight = 50; // Header with title and buttons
          const inputSectionHeight = 60; // Add comment input section
          const commentHeight = 70; // Height per comment item
          const estimatedHeight =
            headerHeight + comments.length * commentHeight + inputSectionHeight;
          popupHeight = Math.min(estimatedHeight, maxPopupHeight);
        }

        // Horizontal Positioning only with clamping to grid bounds
        let left;
        let side = "right"; // track which side we positioned the popup
        const spaceRight = gridRect.right - rect.right - margin;
        const spaceLeft = rect.left - gridRect.left - margin;

        // Prefer right side, but if not enough space, go left
        if (spaceRight >= effectivePopupWidth) {
          left = rect.right + margin;
          side = "right";
        } else if (spaceLeft >= effectivePopupWidth) {
          left = rect.left - effectivePopupWidth - margin;
          side = "left";
        } else {
          // Not enough space on either side, clamp to grid bounds
          left = Math.min(
            rect.right + margin,
            gridRect.right - effectivePopupWidth - margin
          );
          left = Math.max(left, gridRect.left + margin);
          side = "clamped";
        }

        // Vertical positioning: keep within viewport for both comments and no-comments cases
        let top = rect.top;
        if (
          !comments ||
          comments.length === 0 ||
          (comments && comments.length == 1)
        ) {
          const viewportHeight =
            window.innerHeight || document.documentElement.clientHeight;
          const safePad = 8; // keep a small margin from viewport edges
          // Use a conservative clamp height and cap it to viewport height minus padding
          const rawClampHeight =
            !comments || comments.length === 0
              ? popupHeight // no-comments banner
              : openedBy === "hover"
              ? popupHeight // hover preview uses its smaller estimated height
              : maxPopupHeight; // expanded view can grow up to max
          const clampHeight = Math.max(
            0,
            Math.min(rawClampHeight, Math.max(0, viewportHeight - safePad))
          );
          const maxTop = Math.max(safePad, viewportHeight - clampHeight);
          top = Math.min(Math.max(safePad, rect.top), maxTop);
          return { top, left, side };
        }
        if (
          top + popupHeight > gridRect.bottom &&
          comments &&
          comments.length > 0
        ) {
          // Try positioning above the cell first
          const topAboveCell = rect.top - popupHeight - margin;

          if (topAboveCell >= gridRect.top) {
            // Enough space above the cell, position there
            top = topAboveCell;
          } else {
            // Not enough space above either, position at the top of grid
            // and let the portal handle scrolling internally
            top = gridRect.top + margin;
          }
        }

        // Ensure we don't go above the grid
        top = Math.max(gridRect.top + margin, top);

        return { top, left, side };
      },
      []
    );
    const hoverTimeoutRef = useRef(null);
    const [inputValue, setInputValue] = useState("");
    const [menuAnchorEl, setMenuAnchorEl] = useState(null);
    const [editingComment, setEditingComment] = useState(null);
    const [selectedComment, setSelectedComment] = useState(null);
    const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
    const [commentToDelete, setCommentToDelete] = useState(null);
    const [threadToDelete, setThreadToDelete] = useState(null);
    const [isDeleteConfirmationLoading, setIsDeleteConfirmationLoading] =
      useState(false);
    const [isCommentLoading, setIsCommentLoading] = useState(false);
    const [commentError, setCommentError] = useState(null);
    const [currentMenuType, setCurrentMenuType] = useState("commentMenu");

    const getMenuOptions = (menuType) => {
      const config = commentProps?.configuration || {};

      if (menuType === "topMenu") {
        const isObj = selectedComment && typeof selectedComment === "object";
        const isRead = isObj ? selectedComment[0]?.read : selectedComment?.read;
        const author = isObj
          ? selectedComment[0]?.author
          : selectedComment?.author;
        let options = [];

        // Add Mark as Read/Unread option if not disabled
        if (!config.disableMarkAsRead) {
          options.push({
            label: isRead ? "Mark as Unread" : "Mark as Read",
            // icon: <DeleteIcon />,
            onClick: async () => {
              let response;
              if (selectedComment?.read) {
                response = await commentProps?.handleMarkAsUnread(
                  selectedComment.id
                );
              } else {
                response = await commentProps?.handleMarkAsRead(
                  selectedComment.id
                );
              }
              handleMenuClose();
            },
            value: "mark-as-unread",
            icon: isRead ? <UnreadIcon /> : <ReadIcon />,
          });
        }

        // Add Delete Thread option if not disabled and user is author
        if (!config.disableDeleteThread && author == commentProps?.author) {
          options.push({
            label: "Delete Thread",
            icon: <DeleteIcon />,
            onClick: handleThreadDeleteClick,
            value: "delete-thread",
          });
        }

        return options;
      } else {
        let options = [];

        // Add Edit option if not disabled
        if (!config.disableEditComment) {
          options.push({
            label: "Edit",
            icon: <EditIcon />,
            onClick: () => {
              // Set the editing state only when Edit is actually clicked
              if (selectedComment) {
                setEditingComment(selectedComment);
              }
              handleMenuClose();
            },
            value: "edit",
          });
        }

        // Add Delete option if not disabled
        if (!config.disableDeleteComment) {
          options.push({
            label: "Delete",
            icon: <DeleteIcon />,
            onClick: handleDeleteClick,
            value: "delete",
          });
        }

        return options;
      }
    };

    const handleMenuClick = useCallback(
      (event, comment = null, type = "commentMenu") => {
        setMenuAnchorEl(event.currentTarget);
        setCurrentMenuType(type);
        // Store the comment reference for potential editing (but don't set editing state yet)
        if (typeof comment === "string") {
          const val = comments[comment];
          setSelectedComment({ ...val, id: comment });
        } else {
          setSelectedComment(comment);
        }
      },
      [comments]
    );

    const handleMenuClose = useCallback(() => {
      setMenuAnchorEl(null);
      // Only clear selected comment, preserve editing state if it was set
      setSelectedComment(null);
    }, []);

    useMemo(() => {
      if (!popup) {
        setMenuAnchorEl(null);
        setEditingComment(null);
        setSelectedComment(null);
      }
    }, [popup]);

    const toggleCommentMode = () => {
      setCommentMode((prev) => !prev);
      setPopup(null);
      setEditingComment(null);
      setSelectedComment(null);
    };

    useEffect(() => {
      // This is the single source of truth for synchronization.
      // If the table settings are opened, it must turn off comment mode.
      if (tableSettingOpen && commentMode) {
        setCommentMode(false);
      }
    }, [tableSettingOpen]);

    // Helper to compute effective cellId supporting multiple threads per cell
    const getEffectiveCellId = useCallback(
      (baseCellId) => {
        const isResolved = Array.isArray(resolvedComments)
          ? resolvedComments.includes(baseCellId)
          : false;

        if (!isResolved) return baseCellId;

        const prefix = `${baseCellId}__t`;
        const allKeys = Object.keys(comments || {});
        const threadIndices = allKeys
          .filter((k) => k === baseCellId || k.startsWith(prefix))
          .map((k) => {
            const m = k.match(new RegExp(`^${baseCellId}__t(\\d+)$`));
            return m ? parseInt(m[1], 10) : 0;
          })
          .filter((n) => !Number.isNaN(n));
        const nextIndex = threadIndices.length
          ? Math.max(...threadIndices) + 1
          : 1;
        return `${baseCellId}__t${nextIndex}`;
      },
      [comments, resolvedComments]
    );

    // Helper to find the latest unresolved thread id for hover display
    const getLatestUnresolvedThreadId = useCallback(
      (baseCellId) => {
        const prefix = `${baseCellId}__t`;
        const allKeys = Object.keys(comments || {});
        // Collect all candidate thread ids for this cell
        const candidates = allKeys.filter(
          (k) => k === baseCellId || k.startsWith(prefix)
        );
        if (candidates.length === 0) return null;
        // Filter out resolved threads
        const unresolved = candidates.filter(
          (k) => !resolvedComments?.includes?.(k)
        );
        if (unresolved.length === 0) return null;
        // Sort by index so that the highest (latest) index is chosen; base has index 0
        const withIndex = unresolved.map((k) => {
          const m = k.match(new RegExp(`^${baseCellId}__t(\\d+)$`));
          return { id: k, idx: m ? parseInt(m[1], 10) : 0 };
        });
        withIndex.sort((a, b) => b.idx - a.idx);
        return withIndex[0].id;
      },
      [comments, resolvedComments]
    );

    const onCellClicked = useCallback(
      (params) => {
        if (!commentMode) return;
        // TODO: support cellClick passed from props
        const cellEl = params.event.target?.closest(".ag-cell");
        if (cellEl) {
          // Do not show popup for group rows or group cells
          // const rowEl = cellEl.closest(".ag-row");
          // if (
          //   rowEl?.classList?.contains("ag-row-group") ||
          //   cellEl.classList?.contains("ag-group-cell")
          // ) {
          //   return;
          // }
          // // Also ignore AG Grid auto column by colId
          // if (params?.column?.getColId?.() === "ag-Grid-AutoColumn") {
          //   return;
          // }
          const rect = cellEl.getBoundingClientRect();
          // Use colIdentifierKey if provided, otherwise fallback to params.node.id
          const rowIdentifier = commentProps?.colIdentifierKey
            ? params.data?.[commentProps.colIdentifierKey] || params.node.id
            : params.node.id;
          const baseCellId = `${rowIdentifier}__${params.column.getColId()}`;
          // If base is resolved, prefer latest unresolved thread; otherwise create a new thread
          let effectiveCellId = baseCellId;
          if (!commentProps?.configuration?.disableMultiCommentThread) {
            const baseResolved = Array.isArray(resolvedComments)
              ? resolvedComments.includes(baseCellId)
              : false;
            if (baseResolved) {
              const latestUnresolved = getLatestUnresolvedThreadId(baseCellId);
              effectiveCellId =
                latestUnresolved || getEffectiveCellId(baseCellId);
            }
          }
          const commentForCell =
            params.data?.comment || comments[effectiveCellId];
          const position = calculatePopupPosition(
            rect,
            commentForCell,
            "click"
          );
          setPopup({
            position,
            rowId: rowIdentifier,
            colId: params.column.getColId(),
            cellId: effectiveCellId,
            comment: commentForCell,
            author: commentProps?.author || "Current User", // TODO: You can replace this with actual user info
            openedBy: "click",
            data: params.node?.data,
          });
        }
      },
      [
        commentMode,
        commentProps?.configuration?.disableMultiCommentThread,
        resolvedComments,
        getLatestUnresolvedThreadId,
        getEffectiveCellId,
        comments,
        calculatePopupPosition,
      ]
    );

    // Refresh cells when comments change to show/hide comment icons
    useEffect(() => {
      if (actualRef?.current?.api) {
        actualRef.current.api.refreshCells({
          force: true, // Force refresh even if data hasn't changed
        });
      }
    }, [comments]);

    const handleCommentIconHover = useCallback(
      (params) => {
        if (params.eGridCell) {
          const rect = params.eGridCell.getBoundingClientRect();
          // Use colIdentifierKey if provided, otherwise fallback to params.node.id
          const rowIdentifier = commentProps?.colIdentifierKey
            ? params.data?.[commentProps.colIdentifierKey] || params.node.id
            : params.node.id;
          const baseCellId = `${rowIdentifier}__${params.column.getColId()}`;
          // Hover should show latest unresolved thread if available
          const cellId = getLatestUnresolvedThreadId(baseCellId);
          if (!cellId) return; // No unresolved threads to show
          const position = calculatePopupPosition(
            rect,
            comments[cellId],
            "hover"
          );
          setPopup({
            position,
            // { top: rect.top, left: rect.right + 5 },
            rowId: rowIdentifier,
            colId: params.column.getColId(),
            cellId,
            comment: comments[cellId],
            author: commentProps?.author || "Current User",
            openedBy: "hover",
            data: params.node?.data,
          });
        }
      },
      [
        getLatestUnresolvedThreadId,
        calculatePopupPosition,
        comments,
        commentProps?.author,
      ]
    );

    const handleMouseLeave = useCallback(() => {
      if (popup?.openedBy === "hover") {
        hoverTimeoutRef.current = setTimeout(() => {
          if (!menuAnchorEl) {
            setPopup(null);
            setEditingComment(null);
            setSelectedComment(null);
          }
        }, 200);
      } else {
        setPopup(null);
        setEditingComment(null);
        setSelectedComment(null);
      }
    }, [popup, menuAnchorEl]);

    const handleMouseEnter = useCallback(() => {
      if (popup?.openedBy === "hover") {
        clearTimeout(hoverTimeoutRef.current);
      }
    }, [popup]);

    const handleRegisterTableSettingToggle = useCallback(
      (fn) => {
        const api = actualRef?.current?.api;
        if (api && typeof fn === "function") {
          api.showTableSetting = (tabName, options) =>
            fn(true, tabName, options);
          api.hideTableSetting = (options) => fn(false, undefined, options);
          api.toggleTableSetting = (open, tabName, options) =>
            fn(open, tabName, options);
        }
      },
      [actualRef]
    );

    function getCellElementById(gridId, rowNode, colId) {
      const containerEl = document.querySelector(
        ".impact-table-main-container"
      );
      if (!containerEl || !rowNode) return null;

      // Try row-id first
      let cell = containerEl.querySelector(
        `[role="row"][row-id="${rowNode.id}"] [col-id="${colId}"]`
      );

      // Fallback to row-index
      if (!cell) {
        const rowIndex = rowNode.rowIndex ?? rowNode.childIndex;
        if (rowIndex != null) {
          cell = containerEl.querySelector(
            `[role="row"][row-index="${rowIndex}"] [col-id="${colId}"]`
          );
        }
      }

      return cell;
    }

    const handleCommentItemClick = useCallback(
      (cellId, comments, messageId = null) => {
        const [rowId, colId, threadId] = cellId.split("__");
        const api = actualRef.current?.api;
        if (!api) {
          console.error("AG Grid API not available.");
          return;
        }

        // Find row node by searching for the colIdentifierKey value or fallback to node ID
        let rowNode;
        if (commentProps?.colIdentifierKey) {
          // Search through all nodes to find the one with matching colIdentifierKey value.
          // Use String() so rowId from cellId (always string) matches data id when number (e.g. strategy_id: 47).
          api.forEachNode((node) => {
            const dataId = node.data?.[commentProps.colIdentifierKey];
            if (dataId != null && String(dataId) === String(rowId)) {
              rowNode = node;
            }
          });
        } else {
          // Fallback to original logic for backward compatibility
          rowNode = api.getRowNode(rowId);
        }

        if (!rowNode) {
          console.error("Could not find row node for rowId:", rowId);
          return;
        }

        // Make sure row + col are visible
        api.ensureColumnVisible(colId);
        api.ensureNodeVisible(rowNode, "middle");

        // Wait for DOM rendering after scroll
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            const cellElement = getCellElementById(
              gridId || "my-grid",
              rowNode,
              colId
            );

            if (!cellElement) {
              console.error("Could not find cell element for:", cellId);
              return;
            }

            const rect = cellElement.getBoundingClientRect();
            const position = calculatePopupPosition(rect, comments, "click");
            setPopup({
              position,
              //{ top: rect.top + 11, left: rect.right - 12 },
              cellId,
              comment: comments,
              author: commentProps?.author,
              openedBy: "click",
              data: rowNode?.data || {},
              messageId: messageId,
            });
          });
        });
      },
      [actualRef, gridId, calculatePopupPosition, commentProps?.author]
    );

    const handleUpdateComment = useCallback(
      async (commentId, newText) => {
        if (commentId === null) {
          // Cancel editing
          setEditingComment(null);
          return;
        }

        if (!newText || !newText.trim()) return;

        setIsCommentLoading(true);
        setCommentError(null);

        try {
          const commentToUpdate = editingComment;
          if (!commentToUpdate) return;

          let response;
          // Call handleCommentEdit for updating existing comments
          if (commentProps?.handleCommentEdit) {
            response = await commentProps?.handleCommentEdit(
              newText.trim(),
              commentToUpdate,
              commentId
            );
          }

          // Update local state with backend response
          if (response) {
            setComments(response);
          }

          // Clear editing state
          setEditingComment(null);
        } catch (error) {
          console.error("Error updating comment:", error);
          setCommentError("Failed to update comment. Please try again.");
        } finally {
          setIsCommentLoading(false);
        }
      },
      [editingComment, commentProps, popup?.cellId]
    );

    const handleAddComment = useCallback(
      async (cellId, inputValue, taggedUsers) => {
        if (!inputValue?.trim()) {
          setCommentError("Comment cannot be empty");
          return;
        }

        const isEditing = editingComment !== null;

        try {
          setIsCommentLoading(true);
          setCommentError(null);

          let response;

          if (isEditing) {
            // Call handleCommentEdit for updating existing comments
            response = await commentProps?.handleCommentEdit(
              inputValue.trim(),
              editingComment,
              editingComment?.id,
              taggedUsers
            );
          } else {
            // Call handleSaveComment for creating new comments
            response = await commentProps?.handleSaveComment(
              cellId,
              inputValue.trim(),
              popup?.data,
              taggedUsers
            );
          }

          // Update local state with backend response
          if (response) {
            setComments(response);
          } else {
            // Fallback to local state update if no backend response
            if (isEditing) {
              // Update existing comment
              setComments((prev) => ({
                ...prev,
                [cellId]:
                  prev[cellId]?.map((comment) =>
                    comment.id === editingComment.id
                      ? {
                          ...comment,
                          text: inputValue.trim(),
                          timestamp: new Date().toISOString(),
                          isEdited: true,
                        }
                      : comment
                  ) || [],
              }));
            } else {
              // Add new comment
              const newComment = {
                id: Date.now(), // Simple ID generation
                text: inputValue.trim(),
                timestamp: new Date().toISOString(),
                author: commentProps?.author || "Current User",
                isEdited: false,
              };
              setComments((prev) => ({
                ...prev,
                [cellId]: [...(prev[cellId] || []), newComment],
              }));
            }
          }

          // Close popup and clear editing state only on success
          if (!comments[cellId]) setPopup(null);
          setEditingComment(null);
          setSelectedComment(null);
        } catch (error) {
          console.error("Error saving comment:", error);
          setCommentError(
            error?.message ||
              (isEditing
                ? "Failed to update comment"
                : "Failed to save comment")
          );
        } finally {
          setIsCommentLoading(false);
        }
      },
      [
        popup?.data,
        editingComment,
        commentProps?.handleSaveComment,
        commentProps?.handleCommentEdit,
        commentProps?.author,
      ]
    );

    const handleMarkAsResolved = useCallback(
      async (cellId, comments) => {
        // Check if mark as resolved is disabled
        const config = commentProps?.configuration || {};
        if (config.disableMarkAsResolved) {
          return;
        }

        let response;
        if (commentProps?.handleMarkAsResolved) {
          response = await commentProps?.handleMarkAsResolved(cellId, comments);
        }
        // Update local state with backend response
        if (response) {
          setResolvedComments(response);
        } else {
          // Fallback to local state update if no backend response
          setResolvedComments((prev) => [...prev, cellId]);
        }
        setIsCommentResolvedAlertOpen(true);
        setTimeout(() => {
          setIsCommentResolvedAlertOpen(false);
        }, 2000);
        // Close popup and clear editing state only on success
        setPopup(null);
        setEditingComment(null);
        setSelectedComment(null);
      },
      [commentProps?.handleMarkAsResolved, commentProps?.configuration]
    );

    const handleThreadDeleteClick = () => {
      setThreadToDelete({
        cellId: selectedComment.id,
        data: selectedComment,
      });
      setShowDeleteConfirmation(true);
      handleMenuClose();
    };
    const handleDeleteClick = () => {
      setCommentToDelete({
        cellId: popup?.cellId,
        data: popup?.data,
        comment: selectedComment,
      });
      setShowDeleteConfirmation(true);
      handleMenuClose();
    };

    const handleDeleteConfirm = async () => {
      if (commentToDelete) {
        try {
          setIsDeleteConfirmationLoading(true);
          const updatedComment = await commentProps?.handleCommentDelete?.(
            commentToDelete.comment?.id,
            commentToDelete.comment
          );
          if (updatedComment) {
            setComments(updatedComment);
          }
        } catch (error) {
          console.error("Error deleting comment:", error);
        } finally {
          setIsDeleteConfirmationLoading(false);
        }
      } else if (threadToDelete) {
        try {
          setIsDeleteConfirmationLoading(true);
          const updatedComment = await commentProps?.handleThreadDelete?.(
            threadToDelete.cellId,
            threadToDelete.data
          );
          if (updatedComment) {
            setComments(updatedComment);
          }
        } catch (error) {
          console.error("Error deleting thread:", error);
        } finally {
          setIsDeleteConfirmationLoading(false);
        }
      }
      setShowDeleteConfirmation(false);
      setCommentToDelete(null);
    };

    const handleDeleteCancel = (e) => {
      e.stopPropagation();
      setShowDeleteConfirmation(false);
      setCommentToDelete(null);
    };

    const clearCommentError = useCallback(() => {
      setCommentError(null);
    }, []);

    const gridContext = useMemo(
      () => ({
        comments,
        popup,
        handleCommentIconHover,
        handleMouseEnter,
        handleMouseLeave,
        resolvedComments,
        getLatestUnresolvedThreadId,
        configuration: commentProps?.configuration,
        colIdentifierKey: commentProps?.colIdentifierKey,
      }),
      [
        comments,
        popup,
        handleCommentIconHover,
        handleMouseEnter,
        handleMouseLeave,
        resolvedComments,
        getLatestUnresolvedThreadId,
        commentProps?.configuration,
        commentProps?.colIdentifierKey,
      ]
    );

    // Calculate base heights for CSS variables
    const LIST_ROW_SPACING = 12;
    const BASE_HEIGHT_WITH_IMAGE = 72;
    const BASE_HEIGHT_WITHOUT_IMAGE = 48;

    const getRowHeightWrapper = (params) => {
      if (viewPoint === "list") {
        const visibleColumns = columnDefs.filter(
          (col) =>
            col.field &&
            !col.hide &&
            (col.field === "image" ||
              (typeof col.cellRenderer === "string" &&
                col.cellRenderer.includes("Image")))
        );

        const hasImage = visibleColumns.some((col) => {
          const value = params?.data?.[col.field];
          return (
            typeof value === "string" &&
            (value.startsWith("http") ||
              value.match(/\.(jpg|jpeg|png|gif|webp|svg)$/i))
          );
        });
        const baseHeight = hasImage
          ? BASE_HEIGHT_WITH_IMAGE
          : BASE_HEIGHT_WITHOUT_IMAGE;
        return baseHeight + LIST_ROW_SPACING;
      }
      return args?.getRowHeight?.(params);
    };

    const getRowClassWrapper = (params) => {
      if (viewPoint === "list") {
        const visibleColumns = columnDefs.filter(
          (col) =>
            col.field &&
            !col.hide &&
            (col.field === "image" ||
              (typeof col.cellRenderer === "string" &&
                col.cellRenderer.includes("Image")))
        );

        const hasImage = visibleColumns.some((col) => {
          const value = params?.data?.[col.field];
          return (
            typeof value === "string" &&
            (value.startsWith("http") ||
              value.match(/\.(jpg|jpeg|png|gif|webp|svg)$/i))
          );
        });

        const className = hasImage
          ? "list-row-with-image"
          : "list-row-without-image";

        // Combine with any existing row class
        const existingClass = args?.getRowClass?.(params) || "";
        return existingClass ? `${existingClass} ${className}` : className;
      }
      return args?.getRowClass?.(params) || "";
    };

    const EmptyStateOverlay = (params) => {
      if(args.noRowsOverlayComponent){
        return args.noRowsOverlayComponent(params);
      }
      return (
        <div className="table-empty-state">
          <EmptyStateImage />
          <span>{emptyStateText}</span>
        </div>
      );
    };

    return (
      <div
        className={`ag-theme-alpine ia-basic-table-layout table-v32 ${
          (height && !viewPoint === "list") ? "custom-height-override" : ""
        } ${enableHeaderTextWrap ? "header-wrap-enabled" : ""} ${
          viewPoint === "list" ? "list-view list-mode-enabled" : ""
        }`}
        style={{
          width: "100%",
          "--ag-grid-height": height || "500px",
        }}
      >
        <div
          className={`impact-table-main-container  ${
            cardContainer ? "card-container" : ""
          } ${tableSettingOpen ? "table-setting-open" : ""} ${
            bottomLeftOptions || bottomCenterOptions
              ? "footer-button-with-bottom-options"
              : ""
          } ${hideMarginBottom ? "hide-margin-bottom" : ""} ${
            commentMode ? "comment-mode" : ""
          }`}
          id={gridId || "myGrid"}
        >
          <TableHeader
            actualRef={actualRef}
            colDefs={colDefs}
            Columns={Columns}
            rowHeight={rowHeight}
            setRowHeight={setRowHeight}
            tableFontSize={tableFontSize}
            setTableFontSize={setTableFontSize}
            tableHeader={tableHeader}
            topRightOptions={topRightOptions}
            topLeftOptions={topLeftOptions}
            onGlobalSearchClick={onGlobalSearchClick}
            cardContainer={cardContainer}
            onNumberFormatChange={onNumberFormatChange}
            hideRowHeightOptionMenu={hideRowHeightOptionMenu}
            showDownloadButton={showDownloadButton}
            onDownloadButtonClick={onDownloadButtonClick}
            closeButton={closeButton}
            handleCloseButtonClick={handleCloseButtonClick}
            additionalButtons={additionalButtons}
            setTableSettingOpen={setTableSettingOpen}
            showHideEditableCells={showHideEditableCells}
            handleShowHideEditableCells={handleShowHideEditableCells}
            topCenterOptions={topCenterOptions}
            showContentualFilter={showContentualFilter}
            showContentualFilterBadge={showContentualFilterBadge}
            onContentualFilterClick={onContentualFilterClick}
            contentualFilterProps={contentualFilterProps}
            contentualFilterComponent={contentualFilterComponent}
            tableActions={tableActions}
            onTableActionsTabClick={onTableActionsTabClick}
            hideTableFormat={hideTableFormat}
            hideTableActions={hideTableActions}
            hideTableSetting={hideTableSetting}
            explicitlyCloseTableSetting={explicitlyCloseTableSetting}
            customSystemButton={customSystemButton}
            isChatEnabled={isChatEnabled}
            handleChatClick={handleChatClick}
            isChatOpen={isChatOpen}
            handleChatClose={handleChatClose}
            chatConfiguration={chatConfiguration}
            savedViewsOptions={savedViewsOptions}
            handleSaveAndApply={handleSaveAndApply}
            maxSavedViews={maxSavedViews}
            handleSavedViewClick={handleSavedViewClick}
            handleEditSaveView={handleEditSaveView}
            isSaveViewEnabled={isSaveViewEnabled}
            isSavingViewLoading={isSaveViewLoading}
            handleDeleteViewClick={handleDeleteViewClick}
            isDeleteViewLoading={isDeleteViewLoading}
            handleSetDefaultView={handleSetDefaultView}
            isCommentFeatureEnabled={isCommentFeatureEnabled}
            showCommentBadge={commentProps?.showCommentBadge}
            toggleCommentMode={toggleCommentMode}
            commentMode={commentMode}
            hideFontSize={hideFontSize}
            hideNumericFormat={hideNumericFormat}
            onContentDensityChange={onContentDensityChange}
            isCommentResolvedAlertOpen={isCommentResolvedAlertOpen}
            setIsCommentResolvedAlertOpen={setIsCommentResolvedAlertOpen}
            showCommentPanel={showCommentPanel}
            setShowCommentPanel={setShowCommentPanel}
            customTabInTableSettings={customTabInTableSettings}
            closeTableSettingOnOutsideClick={closeTableSettingOnOutsideClick}
            saveViewCustomFilter={saveViewCustomFilter}
            customRenderSaveViewContent={customRenderSaveViewContent}
            onTableSettingClick={onTableSettingClick}
            deleteViewPromptText={deleteViewPromptText}
            contentualFilterOpenByDefault={contentualFilterOpenByDefault}
            tableSettingsTitle={tableSettingsTitle}
            registerTableSettingToggle={handleRegisterTableSettingToggle}
            numericFormatDefault={numericFormatDefault}
            viewPoint={viewPoint}
            disableContextualFilter={disableContextualFilter}
            disableTableSettings={disableTableSettings}
            onAdvanceSortClick={() => setIsAdvanceSortOpen(true)}
            advanceSortEnabled={advanceSortEnabled}
          />
          <BulkActionBar
            showBulkActionBar={showBulkActionBar}
            onSelectAllRows={onSelectAllRows}
            onClearSelection={onClearSelection}
            bulkActionBarProps={bulkActionBarProps}
          />

          {belowHeaderComponent && belowHeaderComponent}
          {!hideTableBody && finalColumnDefs?.length > 0 && (
            <div
              className={`impact-ag-grid-table ${
                viewPoint === "list" ? "list-view-wrapper" : ""
              }`}
            >
              <AgGridReact
                {...(({ components, ...rest }) => rest)(args)}
                components={wrappedComponents}
                ref={actualRef}
                suppressRowTransform={
                  viewPoint === "list" ? false : args?.suppressRowTransform
                }
                suppressRowVirtualisation={
                  viewPoint === "list" ? false : args?.suppressRowVirtualisation
                }
                domLayout={viewPoint === "list" ? "normal" : "autoHeight"}
                // domLayout="autoHeight"
                rowHeight={rowHeight}
                columnDefs={finalColumnDefs}
                headerHeight={40}
                defaultColDef={defaultColDef}
                enableRangeSelection
                suppressMenuHide
                getMainMenuItems={customGetMainMenuItems || getMainMenuItems}
                paginationPageSize={localPageSize}
                onPaginationChanged={paginationChanged}
                popupParent={document.body}
                onRowGroupOpened={updateExpandState}
                pagination={args.pagination}
                onCellClicked={onCellClicked}
                context={gridContext}
                onFirstDataRendered={onFirstDataRendered}
                animateRows={viewPoint === "list" ? true : args?.animateRows}
                ensureDomOrder={
                  viewPoint === "list" ? false : args?.ensureDomOrder
                }
                enableCellTextSelection={
                  viewPoint === "list" ? true : args?.enableCellTextSelection
                }
                getRowHeight={getRowHeightWrapper}
                getRowClass={getRowClassWrapper}
                noRowsOverlayComponent={EmptyStateOverlay}
                onSortChanged={onGridSortChanged}
              />
              <div
                className={`${
                  showCommentPanel ? "show-comment-panel" : ""
                } comment-panel-wrapper`}
              >
                <CommentPanel
                  showCommentPanel={showCommentPanel}
                  setShowCommentPanel={setShowCommentPanel}
                  comments={comments}
                  onCommentItemClick={handleCommentItemClick}
                  onMenuClick={handleMenuClick}
                  onMarkAsReadClick={commentProps?.handleMarkAsRead}
                  onMarkAsResolvedClick={handleMarkAsResolved}
                  commentProps={commentProps}
                  resolvedComments={resolvedComments}
                  popup={popup}
                  onShowResolvedCommentsRequest={
                    commentProps?.handleFetchResolvedComments
                      ? async () => {
                          // Save current state before replacing with resolved-only comments
                          commentsBeforeResolvedViewRef.current = comments;
                          resolvedCommentsBeforeResolvedViewRef.current =
                            resolvedComments;
                          const result =
                            await commentProps.handleFetchResolvedComments();
                          if (result) {
                            if (result.comments !== undefined) {
                              setComments(result.comments);
                            }
                            if (result.resolvedComments !== undefined) {
                              setResolvedComments(result.resolvedComments);
                            }
                          }
                        }
                      : undefined
                  }
                  onExitResolvedCommentsView={
                    commentProps?.handleFetchResolvedComments
                      ? () => {
                          // Restore original comments when user switches away from "Show resolved comments"
                          if (
                            commentsBeforeResolvedViewRef.current != null &&
                            resolvedCommentsBeforeResolvedViewRef.current !=
                              null
                          ) {
                            setComments(commentsBeforeResolvedViewRef.current);
                            setResolvedComments(
                              resolvedCommentsBeforeResolvedViewRef.current
                            );
                            // Clear refs after restoring
                            commentsBeforeResolvedViewRef.current = null;
                            resolvedCommentsBeforeResolvedViewRef.current =
                              null;
                          }
                        }
                      : undefined
                  }
                />
              </div>
            </div>
          )}
          {(bottomLeftOptions || bottomCenterOptions || args.pagination) && (
            <div
              className={`table-footer-section ${
                !bottomLeftOptions && !bottomCenterOptions
                  ? "table-footer-section-with-only-pagination"
                  : ""
              } ${
                !args.pagination
                  ? "table-footer-section-without-pagination"
                  : ""
              }`}
              style={{
                width: hidePaginationPageSizeSelector && "60%",
              }}
              ref={buttonRef}
            >
              {bottomLeftOptions && (
                <div className="table-footer-left-section">
                  {bottomLeftOptions}
                </div>
              )}
              {bottomCenterOptions && (
                <div className="table-footer-center-section">
                  {bottomCenterOptions}
                </div>
              )}
              {!hidePaginationPageSizeSelector && args.pagination ? (
                <TablePagination
                  actualRef={actualRef}
                  localPageSize={localPageSize}
                  setLocalPageSize={setLocalPageSize}
                  paginationPageSizeSelector={
                    paginationPageSizeSelector.length > 0
                      ? paginationPageSizeSelector
                      : [10]
                  }
                  defaultPageSize={defaultPageSize}
                />
              ) : null}
            </div>
          )}
          {nestedTable && (
            <div className="nested-table-container" ref={nestedTableRef}>
              {nestedTableComponent}
            </div>
          )}
          {advanceSortEnabled && (
            <AdvanceSortModal
              open={isAdvanceSortOpen}
              onClose={() => setIsAdvanceSortOpen(false)}
              colDefs={actualRef.current?.api?.getColumnDefs() ?? colDefs}
              sortState={advanceSortState}
              onApply={handleAdvanceSortApply}
            />
          )}
          {isAdvanceSearch && (
            <NewAdvanceSearchModal
              isAdvanceSearch={isAdvanceSearch}
              toggleAdvanceSearch={toggleNewAdvanceSearch}
              initialFilters={initialFilters}
              colDefs={colDefs}
              actualRef={actualRef}
              onSearchApplyClick={onSearchApplyClicks}
              setInitialFilters={setInitialFilters}
              currentColumnId={currentColumnId}
            />
          )}
          {/* {isAdvanceSearch && (
            <TableAdvanceSearchModal
              isAdvanceSearch={isAdvanceSearch}
              draftFilter={draftFilter}
              setDraftFilter={setDraftFilter}
              toggleAdvanceSearch={toggleAdvanceSearch}
              colDefs={colDefs}
              actualRef={actualRef}
              onSearchApplyClick={onSearchApplyClicks}
              currentColumnId={currentColumnId}
              initialFilters={initialFilters}
              setInitialFilters={setInitialFilters}
              savedAdvanceSearchConfig={savedAdvanceSearchConfig}
              selectedThirdOptionDropdownValue={
                selectedThirdOptionDropdownValue
              }
              setSelectedThirdOptionDropdownValue={
                setSelectedThirdOptionDropdownValue
              }
              {...args}
            />
          )} */}
          <CommentsPortal
            key={popup?.cellId || "comments-portal"}
            isOpen={!!popup}
            onClose={() => {
              setPopup(null);
              setEditingComment(null);
              setSelectedComment(null);
              setCommentError(null);
            }}
            comments={comments?.[popup?.cellId] ?? popup?.comment}
            cellId={popup?.cellId}
            onAddComment={handleAddComment}
            position={popup?.position}
            author={popup?.author}
            onMouseEnter={handleMouseEnter}
            onMouseLeave={handleMouseLeave}
            openedBy={popup?.openedBy}
            onMenuClick={handleMenuClick}
            onMarkAsReadClick={
              commentProps?.configuration?.disableMarkAsRead
                ? null
                : commentProps?.handleMarkAsRead
            }
            onMarkAsResolvedClick={
              commentProps?.configuration?.disableMarkAsResolved
                ? null
                : handleMarkAsResolved
            }
            editCommentText={editingComment?.text || ""}
            isLoading={isCommentLoading}
            error={commentError}
            onClearError={clearCommentError}
            currentAuthor={commentProps?.author}
            editingComment={editingComment}
            setEditingComment={setEditingComment}
            onUpdateComment={handleUpdateComment}
            loadParticipantsAvatar={commentProps?.loadParticipantsAvatar}
            resolvedComments={resolvedComments}
            messageId={popup?.messageId}
            configuration={commentProps?.configuration}
          />
          <Menu
            anchorEl={menuAnchorEl}
            iconPlacement="left"
            onClose={handleMenuClose}
            open={Boolean(menuAnchorEl)}
            MenuProps={{
              sx: {
                zIndex: 9999,
              },
            }}
            options={getMenuOptions(currentMenuType)}
          />
          <Prompt
            isOpen={showDeleteConfirmation}
            variant="error"
            title="Delete Comment"
            primaryButtonLabel={
              isDeleteConfirmationLoading ? "Deleting..." : "Delete"
            }
            secondaryButtonLabel="Cancel"
            onPrimaryButtonClick={handleDeleteConfirm}
            onSecondaryButtonClick={handleDeleteCancel}
            handleClose={handleDeleteCancel}
            primaryButtonProps={{
              type: "destructive",
            }}
            style={{ zIndex: 10000 }}
          >
            Are you sure you want to delete this comment? This action cannot be
            undone.
          </Prompt>
        </div>
      </div>
    );
  }
);
