/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

import type { LayoutEvent } from '../../../types';
import type { ScrollEvent } from '../Types/CoreEventTypes';
import type { ViewToken } from '../ViewabilityHelper';
import type { FrameMetricProps, Item, Props, RenderItemProps, RenderItemType, Separators } from './VirtualizedListProps';
import RefreshControl from '../../../exports/RefreshControl';
import ScrollView from '../../../exports/ScrollView';
import View, { type ViewProps } from '../../../exports/View';
import StyleSheet from '../../../exports/StyleSheet';
import Batchinator from '../Batchinator';
import clamp from '../Utilities/clamp';
import infoLog from '../infoLog';
import { CellRenderMask } from './CellRenderMask';
import ChildListCollection from './ChildListCollection';
import FillRateHelper from '../FillRateHelper';
import StateSafePureComponent from './StateSafePureComponent';
import ViewabilityHelper from '../ViewabilityHelper';
import CellRenderer from './VirtualizedListCellRenderer';
import { VirtualizedListCellContextProvider, VirtualizedListContext, VirtualizedListContextProvider } from './VirtualizedListContext.js';
import { computeWindowedRenderLimits, keyExtractor as defaultKeyExtractor } from '../VirtualizeUtils';
import invariant from 'fbjs/lib/invariant';
import nullthrows from 'nullthrows';
import * as React from 'react';
export type { RenderItemProps, RenderItemType, Separators };
const __DEV__ = process.env.NODE_ENV !== 'production';
const ON_EDGE_REACHED_EPSILON = 0.001;
let _usedIndexForKey = false;
let _keylessItemComponentName: string = '';
type ScrollResponderType = any;
type ViewStyleProp = $PropertyType<ViewProps, 'style'>;
type ViewabilityHelperCallbackTuple = {
  viewabilityHelper: ViewabilityHelper,
  onViewableItemsChanged: (info: {
    viewableItems: Array<ViewToken>,
    changed: Array<ViewToken>,
    ...
  }) => void,
  ...
};
type State = {
  renderMask: CellRenderMask,
  cellsAroundViewport: {
    first: number,
    last: number,
  },
};

/**
 * Default Props Helper Functions
 * Use the following helper functions for default values
 */

// horizontalOrDefault(this.props.horizontal)
declare function horizontalOrDefault(horizontal: ?boolean): any; // initialNumToRenderOrDefault(this.props.initialNumToRender)
declare function initialNumToRenderOrDefault(initialNumToRender: ?number): any; // maxToRenderPerBatchOrDefault(this.props.maxToRenderPerBatch)
declare function maxToRenderPerBatchOrDefault(maxToRenderPerBatch: ?number): any; // onStartReachedThresholdOrDefault(this.props.onStartReachedThreshold)
declare function onStartReachedThresholdOrDefault(onStartReachedThreshold: ?number): any; // onEndReachedThresholdOrDefault(this.props.onEndReachedThreshold)
declare function onEndReachedThresholdOrDefault(onEndReachedThreshold: ?number): any; // getScrollingThreshold(visibleLength, onEndReachedThreshold)
declare function getScrollingThreshold(threshold: number, visibleLength: number): any; // scrollEventThrottleOrDefault(this.props.scrollEventThrottle)
declare function scrollEventThrottleOrDefault(scrollEventThrottle: ?number): any; // windowSizeOrDefault(this.props.windowSize)
declare function windowSizeOrDefault(windowSize: ?number): any;
declare function findLastWhere<T>(arr: $ReadOnlyArray<T>, predicate: (element: T) => boolean): T | null;
/**
 * Base implementation for the more convenient [`<FlatList>`](https://reactnative.dev/docs/flatlist)
 * and [`<SectionList>`](https://reactnative.dev/docs/sectionlist) components, which are also better
 * documented. In general, this should only really be used if you need more flexibility than
 * `FlatList` provides, e.g. for use with immutable data instead of plain arrays.
 *
 * Virtualization massively improves memory consumption and performance of large lists by
 * maintaining a finite render window of active items and replacing all items outside of the render
 * window with appropriately sized blank space. The window adapts to scrolling behavior, and items
 * are rendered incrementally with low-pri (after any running interactions) if they are far from the
 * visible area, or with hi-pri otherwise to minimize the potential of seeing blank space.
 *
 * Some caveats:
 *
 * - Internal state is not preserved when content scrolls out of the render window. Make sure all
 *   your data is captured in the item data or external stores like Flux, Redux, or Relay.
 * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow-
 *   equal. Make sure that everything your `renderItem` function depends on is passed as a prop
 *   (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on
 *   changes. This includes the `data` prop and parent component state.
 * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously
 *   offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see
 *   blank content. This is a tradeoff that can be adjusted to suit the needs of each application,
 *   and we are working on improving it behind the scenes.
 * - By default, the list looks for a `key` or `id` prop on each item and uses that for the React key.
 *   Alternatively, you can provide a custom `keyExtractor` prop.
 * - As an effort to remove defaultProps, use helper functions when referencing certain props
 *
 */
declare class VirtualizedList extends StateSafePureComponent<Props, State> {
  static contextType: typeof VirtualizedListContext,
  scrollToEnd(params?: ?{
    animated?: ?boolean,
    ...
  }): any,
  scrollToIndex(params: {
    animated?: ?boolean,
    index: number,
    viewOffset?: number,
    viewPosition?: number,
    ...
  }): $FlowFixMe,
  scrollToItem(params: {
    animated?: ?boolean,
    item: Item,
    viewOffset?: number,
    viewPosition?: number,
    ...
  }): any,
  scrollToOffset(params: {
    animated?: ?boolean,
    offset: number,
    ...
  }): any,
  recordInteraction(): any,
  flashScrollIndicators(): any,
  getScrollResponder(): ?ScrollResponderType,
  getScrollableNode(): ?number,
  getScrollRef(): ?React.ElementRef<typeof ScrollView> | ?React.ElementRef<typeof View>,
  _getCellKey(): string,
  _getScrollMetrics: any,
  hasMore(): boolean,
  _getOutermostParentListRef: any,
  _registerAsNestedChild: any,
  _unregisterAsNestedChild: any,
  state: State,
  invertedWheelEventHandler: ?(ev: any) => void,
  constructor(props: Props): any,
  _checkProps(props: Props): any,
  static _createRenderMask(props: Props, cellsAroundViewport: {
    first: number,
    last: number,
  }, additionalRegions?: ?$ReadOnlyArray<{
    first: number,
    last: number,
  }>): CellRenderMask,
  static _initialRenderRegion(props: Props): {
    first: number,
    last: number,
  },
  static _ensureClosestStickyHeader(props: Props, stickyIndicesSet: Set<number>, renderMask: CellRenderMask, cellIdx: number): any,
  _adjustCellsAroundViewport(props: Props, cellsAroundViewport: {
    first: number,
    last: number,
  }): {
    first: number,
    last: number,
  },
  _findFirstChildWithMore(first: number, last: number): number | null,
  componentDidMount(): any,
  componentWillUnmount(): any,
  setupWebWheelHandler(): any,
  teardownWebWheelHandler(): any,
  static getDerivedStateFromProps(newProps: Props, prevState: State): State,
  _pushCells(cells: Array<Object>, stickyHeaderIndices: Array<number>, stickyIndicesFromProps: Set<number>, first: number, last: number, inversionStyle: ViewStyleProp): any,
  static _constrainToItemCount(cells: {
    first: number,
    last: number,
  }, props: Props): {
    first: number,
    last: number,
  },
  _onUpdateSeparators: any,
  _isNestedWithSameOrientation(): boolean,
  _getSpacerKey: any,
  _keyExtractor(item: Item, index: number, props: {
    keyExtractor?: ?(item: Item, index: number) => string,
    ...
  }): any,
  render(): React.Node,
  componentDidUpdate(prevProps: Props): any,
  _averageCellLength: any,
  _cellRefs: {
    [string]: null | CellRenderer<any>
  },
  _fillRateHelper: FillRateHelper,
  _frames: {
    [string]: {
      inLayout?: boolean,
      index: number,
      length: number,
      offset: number,
    }
  },
  _footerLength: any,
  _hasTriggeredInitialScrollToIndex: any,
  _hasInteracted: any,
  _hasMore: any,
  _hasWarned: {
    [string]: boolean
  },
  _headerLength: any,
  _hiPriInProgress: boolean,
  _highestMeasuredFrameIndex: any,
  _indicesToKeys: Map<number, string>,
  _lastFocusedCellKey: ?string,
  _nestedChildLists: ChildListCollection<VirtualizedList>,
  _offsetFromParentVirtualizedList: number,
  _prevParentOffset: number,
  _scrollMetrics: any,
  _scrollRef: ?React.ElementRef<any>,
  _sentStartForContentLength: any,
  _sentEndForContentLength: any,
  _totalCellLength: any,
  _totalCellsMeasured: any,
  _updateCellsToRenderBatcher: Batchinator,
  _viewabilityTuples: Array<ViewabilityHelperCallbackTuple>,
  _captureScrollRef: any,
  _computeBlankness(): any,
  _defaultRenderScrollComponent: any,
  _onCellLayout: any,
  _onCellFocusCapture(cellKey: string): any,
  _onCellUnmount: any,
  _triggerRemeasureForChildListsInCell(cellKey: string): void,
  measureLayoutRelativeToContainingList(): void,
  _onLayout: any,
  _onLayoutEmpty: any,
  _getFooterCellKey(): string,
  _onLayoutFooter: any,
  _onLayoutHeader: any,
  _renderDebugOverlay(): any,
  _selectLength(metrics: $ReadOnly<{
    height: number,
    width: number,
    ...
  }>): number,
  _selectOffset(metrics: $ReadOnly<{
    x: number,
    y: number,
    ...
  }>): number,
  _maybeCallOnEdgeReached(): any,
  _onContentSizeChange: any,
  _convertParentScrollMetrics: any,
  _onScroll: any,
  _scheduleCellsToRenderUpdate(): any,
  _onScrollBeginDrag: any,
  _onScrollEndDrag: any,
  _onMomentumScrollBegin: any,
  _onMomentumScrollEnd: any,
  _updateCellsToRender: any,
  _createViewToken: any,
  _getOffsetApprox: any,
  __getFrameMetricsApprox: (index: number, props: FrameMetricProps) => {
    length: number,
    offset: number,
    ...
  },
  _getFrameMetrics: any,
  _getNonViewportRenderRegions: any,
  _updateViewableItems(props: FrameMetricProps, cellsAroundViewport: {
    first: number,
    last: number,
  }): any,
}
const styles = StyleSheet.create({
  verticallyInverted: {
    transform: 'scaleY(-1)'
  },
  horizontallyInverted: {
    transform: 'scaleX(-1)'
  },
  debug: {
    flex: 1
  },
  debugOverlayBase: {
    position: 'absolute',
    top: 0,
    right: 0
  },
  debugOverlay: {
    bottom: 0,
    width: 20,
    borderColor: 'blue',
    borderWidth: 1
  },
  debugOverlayFrame: {
    left: 0,
    backgroundColor: 'orange'
  },
  debugOverlayFrameLast: {
    left: 0,
    borderColor: 'green',
    borderWidth: 2
  },
  debugOverlayFrameVis: {
    left: 0,
    borderColor: 'red',
    borderWidth: 2
  }
});
export default VirtualizedList;