/*
 * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import classNames from "classnames";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { PortalContainerProvider } from "../shared/PortalContainerContext.js";
import { useFocusedRow } from "./hooks/useFocusedRow.js";
import { LoadingStateTable } from "./LoadingStateTable.js";
import { NonIdealState } from "./NonIdealState.js";
import { ObjectTableLabelsProvider, useObjectTableLabels } from "./ObjectTableLabels.js";
import { TableBody } from "./TableBody.js";
import { TableEditContainer } from "./TableEditContainer.js";
import { TableHeader } from "./TableHeader.js";
import { SCROLL_FETCH_THRESHOLD } from "./utils/constants.js";
import { isColumnDeclaredEditable } from "./utils/editableUtils.js";
import { PortalTrackerProvider, usePortalTracker } from "./utils/PortalTracker.js";
import styles from "./Table.module.css.js";
export function BaseTable(props) {
  return /*#__PURE__*/React.createElement(ObjectTableLabelsProvider, {
    labels: props.labels
  }, /*#__PURE__*/React.createElement(PortalTrackerProvider, null, /*#__PURE__*/React.createElement(BaseTableInner, props)));
}
function BaseTableInner({
  table,
  isLoading,
  fetchNextPage,
  onRowClick,
  onColumnHeaderClick,
  rowHeight,
  renderCellContextMenu,
  className,
  error,
  headerMenuFeatureFlags,
  editableConfig,
  getRowAttributes,
  showEditFooter = true,
  renderEmptyState,
  focusedRowId: focusedRowIdProp,
  onFocusedRowChanged
}) {
  const tableContainerRef = useRef(null);
  const objectTablePortalRef = useRef(null);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const labels = useObjectTableLabels();
  const getRowById = useCallback(id => {
    try {
      return table.getRow(id, true)?.original ?? null;
    } catch {
      // table.getRow throws when the id no longer matches a row
      // (e.g. the row was removed or has not loaded yet). Treat a
      // missing row as "no focus" rather than crashing.
      return null;
    }
  }, [table]);
  const {
    focusedRowId,
    setFocusedRowId
  } = useFocusedRow({
    focusedRowId: focusedRowIdProp,
    onFocusedRowChanged,
    getRowById
  });
  const portalTracker = usePortalTracker();

  // Sync focusedRowId into table meta so cell renderers (which only
  // receive `table`) can read it without extra prop drilling.
  // Assigned synchronously so children see the current value in the
  // same render pass. This is safe because meta is a mutable bag that
  // TanStack Table never snapshots or shallow-compares.
  if (table.options.meta) {
    table.options.meta.focusedRowId = focusedRowId;
  }

  // Using a ref to prevent duplicate fetches from rapid scroll events while a fetch is in-flight
  const fetchingRef = useRef(false);
  useEffect(() => {
    if (!isLoading || fetchNextPage == null) {
      setIsLoadingMore(false);
    }
  }, [isLoading, fetchNextPage]);
  const fetchMoreOnEndReached = useCallback(async containerRefElement => {
    if (containerRefElement && !fetchingRef.current && !isLoadingMore) {
      const {
        scrollHeight,
        scrollTop,
        clientHeight
      } = containerRefElement;
      if (scrollHeight - scrollTop - clientHeight < SCROLL_FETCH_THRESHOLD && !isLoading && fetchNextPage != null) {
        fetchingRef.current = true;
        setIsLoadingMore(true);
        try {
          await fetchNextPage();
        } finally {
          fetchingRef.current = false;
        }
      }
    }
  }, [fetchNextPage, isLoading, isLoadingMore]);
  const handleScroll = useCallback(async e => {
    await fetchMoreOnEndReached(e.currentTarget);
  }, [fetchMoreOnEndReached]);
  const rows = table.getRowModel().rows;
  const headerGroups = table.getHeaderGroups();
  const hasData = rows.length > 0;
  const hasEditableColumns = table.getAllColumns().some(column => isColumnDeclaredEditable(column.columnDef.meta?.editable));

  // Use pointerdown instead of click to detect outside interactions.
  // base-ui's Select renders a full-screen backdrop that intercepts
  // pointerdown to close the popup. By the time the click event fires,
  // the backdrop is unmounted and event.target falls through to <body>,
  // which would incorrectly trigger the outside-click handler.
  // At pointerdown time the backdrop is still in the DOM, so
  // portalTracker.containsElement correctly identifies it.
  useEffect(() => {
    const handleClickOutside = event => {
      const target = event.target;
      if (tableContainerRef.current && !tableContainerRef.current.contains(target) && !portalTracker?.containsElement(target)) {
        setFocusedRowId(null);
      }
    };
    document.addEventListener("pointerdown", handleClickOutside);
    return () => {
      document.removeEventListener("pointerdown", handleClickOutside);
    };
  }, [portalTracker, setFocusedRowId]);
  return /*#__PURE__*/React.createElement(PortalContainerProvider, {
    container: objectTablePortalRef
  }, /*#__PURE__*/React.createElement("div", {
    ref: objectTablePortalRef,
    className: classNames(styles.osdkTableWrapper, className)
  }, /*#__PURE__*/React.createElement("div", {
    ref: tableContainerRef,
    className: styles.osdkTableContainer,
    onScroll: handleScroll
  }, /*#__PURE__*/React.createElement("table", null, isLoading && !hasData ? /*#__PURE__*/React.createElement(LoadingStateTable, {
    table: table,
    headerGroups: headerGroups,
    rowHeight: rowHeight,
    tableContainerRef: tableContainerRef
  }) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TableHeader, {
    table: table,
    headerMenuFeatureFlags: headerMenuFeatureFlags,
    onColumnHeaderClick: onColumnHeaderClick
  }), /*#__PURE__*/React.createElement(TableBody, {
    rows: rows,
    tableContainerRef: tableContainerRef,
    onRowClick: onRowClick,
    rowHeight: rowHeight,
    renderCellContextMenu: renderCellContextMenu,
    isLoadingMore: isLoadingMore,
    headerGroups: headerGroups,
    focusedRowId: focusedRowId,
    setFocusedRowId: setFocusedRowId,
    isInEditMode: editableConfig?.editModeState.isActive,
    getRowAttributes: getRowAttributes
  }))), !hasData && error == null && (renderEmptyState != null ? renderEmptyState() : /*#__PURE__*/React.createElement(NonIdealState, {
    message: labels.noData
  })), error != null && /*#__PURE__*/React.createElement(NonIdealState, {
    message: labels.errorLoadingData(error.message)
  })), showEditFooter && hasEditableColumns && editableConfig && /*#__PURE__*/React.createElement(TableEditContainer, {
    editableConfig: editableConfig,
    hasFocusedRow: focusedRowId != null
  })));
}
//# sourceMappingURL=Table.js.map