import React, { ReactNode } from 'react';
import { BoxProps } from 'ink';

/**
 * Props for the ScrollView component.
 *
 * @remarks
 * Extends standard BoxProps from Ink.
 */
interface ScrollViewProps extends BoxProps {
    /**
     * Callback fired when the scroll position changes.
     *
     * @param scrollOffset - The new scroll offset (distance from top).
     *
     * @remarks
     * Use this to sync external state or UI (e.g., scrollbars) with the current scroll position.
     */
    onScroll?: (scrollOffset: number) => void;
    /**
     * Callback fired when the ScrollView viewport (visible area) dimensions change.
     *
     * @param size - The new dimensions of the viewport (width, height).
     * @param previousSize - The previous dimensions of the viewport (width, height).
     *
     * @remarks
     * Fired whenever the outer container size changes (e.g., terminal resize or layout update).
     */
    onViewportSizeChange?: (size: {
        width: number;
        height: number;
    }, previousSize: {
        width: number;
        height: number;
    }) => void;
    /**
     * Callback fired when the total height of the content changes.
     *
     * @param height - The new total content height.
     * @param previousHeight - The previous total content height.
     *
     * @remarks
     * Useful for debug logging or adjusting external layouts based on content size.
     */
    onContentHeightChange?: (height: number, previousHeight: number) => void;
    /**
     * Callback fired when an individual child item's height changes.
     *
     * @param index - The index of the item.
     * @param height - The new height of the item.
     * @param previousHeight - The previous height of the item.
     *
     * @remarks
     * This is triggered whenever an item is re-measured and its height differs from the previous value.
     */
    onItemHeightChange?: (index: number, height: number, previousHeight: number) => void;
    /**
     * Enable debug mode to visualize the ScrollView internals.
     *
     * @remarks
     * When enabled, the viewport overflow is not hidden, allowing the full content
     * to be visible. This is useful for inspecting the layout and verifying
     * that content is being rendered correctly off-screen.
     */
    debug?: boolean;
    /**
     * The content to be scrolled.
     *
     * @remarks
     * Accepts an array of React elements. Each element should have a unique `key`
     * prop, which will be preserved during rendering for proper reconciliation.
     */
    children?: ReactNode;
}
/**
 * Ref interface for controlling the ScrollView programmatically.
 */
interface ScrollViewRef {
    /**
     * Scrolls to a specific vertical position.
     *
     * @param offset - The target Y offset (distance from top).
     *
     * @example
     * ```tsx
     * // Scroll to the very top
     * ref.current?.scrollTo(0);
     * ```
     */
    scrollTo: (offset: number) => void;
    /**
     * Scrolls by a relative amount.
     *
     * @param delta - Positive for down, negative for up.
     *
     * @example
     * ```tsx
     * useInput((input, key) => {
     *   if (key.downArrow) ref.current?.scrollBy(1);
     *   if (key.upArrow) ref.current?.scrollBy(-1);
     * });
     * ```
     */
    scrollBy: (delta: number) => void;
    /**
     * Scrolls to the very top (position 0).
     */
    scrollToTop: () => void;
    /**
     * Scrolls to the very bottom.
     *
     * @remarks
     * This calculates the target offset as `contentHeight - viewportHeight`.
     */
    scrollToBottom: () => void;
    /**
     * Gets the current scroll offset (distance scrolled from the top).
     *
     * @remarks
     * The scroll offset represents how many terminal rows the content has been
     * scrolled up from its initial position. A value of 0 means the content is
     * at the very top (no scrolling has occurred).
     *
     * @returns The current scroll offset in terminal rows.
     */
    getScrollOffset: () => number;
    /**
     * Gets the total height of the content.
     *
     * @remarks
     * This is the sum of the heights of all child items.
     *
     * @returns The total content height in terminal rows.
     */
    getContentHeight: () => number;
    /**
     * Gets the current height of the visible viewport.
     *
     * @returns The viewport height in terminal rows.
     */
    getViewportHeight: () => number;
    /**
     * Gets the scroll offset when the content is scrolled to the very bottom.
     *
     * @remarks
     * This is calculated as `contentHeight - viewportHeight`. When the scroll
     * offset equals this value, the last item of the content is visible at the
     * bottom of the viewport.
     *
     * @returns The bottom scroll offset in terminal rows.
     */
    getBottomOffset: () => number;
    /**
     * Gets the height of a specific item by its index.
     *
     * @param index - The index of the item.
     * @returns The height of the item in terminal rows, or 0 if not found.
     */
    getItemHeight: (index: number) => number;
    /**
     * Gets the position of a specific item.
     *
     * @param index - The index of the item.
     * @returns The position (top offset) and height of the item, or null if not found.
     */
    getItemPosition: (index: number) => {
        top: number;
        height: number;
    } | null;
    /**
     * Re-measures the ScrollView viewport dimensions.
     *
     * @remarks
     * Checks the current dimensions of the viewport and updates state if they have changed.
     * This is crucial for handling terminal resizes, as Ink does not automatically propagate resize events to components.
     *
     * @example
     * ```tsx
     * // Handle terminal resize manually
     * useEffect(() => {
     *   const onResize = () => ref.current?.remeasure();
     *   process.stdout.on('resize', onResize);
     *   return () => process.stdout.off('resize', onResize);
     * }, []);
     * ```
     */
    remeasure: () => void;
    /**
     * Triggers re-measurement of a specific child item.
     *
     * @param index - The index of the child to re-measure.
     *
     * @remarks
     * Use this if a child's internal content changes size in a way that doesn't trigger a standard React render cycle update
     * (e.g., internal state change within the child that affects its height).
     */
    remeasureItem: (index: number) => void;
}
/**
 * A ScrollView component for Ink applications.
 *
 * @remarks
 * Allows scrolling through content that exceeds the visible area of the terminal.
 * It manages a virtual viewport and renders all children, but strictly controls
 * their visibility using `overflow="hidden"` and `marginTop` offsets.
 *
 * **Features:**
 * - ↕️ Vertical scrolling
 * - 📏 Auto-measurement of child heights
 * - 🎯 Imperative scrolling methods via ref
 * - 🔁 Dynamic content support (adding/removing children)
 * - 🖥️ Viewport resize handling (via manual `remeasure`)
 *
 * **Important Notes:**
 * - This component does NOT automatically capture keyboard input. You must use `useInput` in a parent component and control the scroll via the `onInput` hook or similar.
 * - Children MUST generally have specific keys if you plan to dynamically update them, to ensure correct height tracking across renders.
 *
 *
 * @example
 * ```tsx
 * import React, { useRef } from 'react';
 * import { Box, Text, useInput } from 'ink';
 * import { ScrollView, ScrollViewRef } from 'ink-scroll-view';
 *
 * const Demo = () => {
 *   const scrollRef = useRef<ScrollViewRef>(null);
 *
 *   useInput((input, key) => {
 *     if (key.downArrow) {
 *       scrollRef.current?.scrollBy(1);
 *     }
 *     if (key.upArrow) {
 *       scrollRef.current?.scrollBy(-1);
 *     }
 *   });
 *
 *   return (
 *     <Box height={10} borderStyle="single">
 *       <ScrollView ref={scrollRef}>
 *         {items.map(item => (
 *           <Text key={item.id}>{item.label}</Text>
 *         ))}
 *       </ScrollView>
 *     </Box>
 *   );
 * };
 * ```
 */
declare const ScrollView: React.ForwardRefExoticComponent<ScrollViewProps & React.RefAttributes<ScrollViewRef>>;

interface ControlledScrollViewProps extends BoxProps {
    /**
     * The current scroll offset (distance from top).
     * Controlled by the parent.
     */
    scrollOffset: number;
    /**
     * Callback fired when the ScrollView viewport (visible area) dimensions change.
     */
    onViewportSizeChange?: (size: {
        width: number;
        height: number;
    }, previousSize: {
        width: number;
        height: number;
    }) => void;
    /**
     * Callback fired when the total height of the content changes.
     */
    onContentHeightChange?: (height: number, previousHeight: number) => void;
    /**
     * Callback fired when an individual child item's height changes.
     */
    onItemHeightChange?: (index: number, height: number, previousHeight: number) => void;
    /**
     * Enable debug mode to visualize the ScrollView internals.
     */
    debug?: boolean;
    children?: ReactNode;
}
interface ControlledScrollViewRef {
    /**
     * Gets the total height of the content.
     *
     * @returns The sum of heights of all child items.
     */
    getContentHeight: () => number;
    /**
     * Gets the current height of the visible viewport.
     *
     * @returns The height of the viewport container.
     */
    getViewportHeight: () => number;
    /**
     * Gets the maximum scroll offset (content height - viewport height).
     *
     * @remarks
     * This represents the scroll offset required to view the very bottom of the content.
     * It is clamped to 0 if the content fits entirely within the viewport.
     */
    getBottomOffset: () => number;
    /**
     * Gets the height of a specific item by its index.
     *
     * @param index - The index of the child item.
     * @returns The measured height of the item.
     */
    getItemHeight: (index: number) => number;
    /**
     * Gets the absolute position and dimensions of a specific item.
     *
     * @param index - The index of the child item.
     * @returns Object containing `top` (offset from content start) and `height`, or `null` if the index is invalid.
     *
     * @remarks
     * This method uses a cached offset calculation system (`itemOffsetsRef`) for performance.
     * It calculates offsets lazily and caches them until the underlying measurements change.
     */
    getItemPosition: (index: number) => {
        top: number;
        height: number;
    } | null;
    /**
     * Re-measures the ScrollView viewport dimensions.
     *
     * @remarks
     * Explicitly triggers a measurement of the viewport Box. This is necessary because
     * Ink does not automatically detect terminal window resizes or parent layout changes
     * that might affect the viewport size.
     */
    remeasure: () => void;
    /**
     * Triggers re-measurement of a specific child item.
     *
     * @param index - The index of the child to re-measure.
     *
     * @remarks
     * Forces the `MeasurableItem` wrapper for the specified index to re-run `measureElement`.
     * Use this when a child's content changes internally (e.g., expanding text) without changing props.
     */
    remeasureItem: (index: number) => void;
}
/**
 * A ControlledScrollView component for Ink applications.
 *
 * @remarks
 * This is a lower-level component that handles the complex logic of:
 * 1. Rendering children within a virtual viewport.
 * 2. Continuously measuring child heights.
 * 3. Calculating total content height.
 * 4. Managing viewport wrapping adjustments (`marginTop`).
 *
 * It is "controlled" because it does not maintain its own scroll state; it purely renders
 * based on the provided `scrollOffset` prop. This allows for flexible parent-controlled behavior.
 */
declare const ControlledScrollView: React.ForwardRefExoticComponent<ControlledScrollViewProps & React.RefAttributes<ControlledScrollViewRef>>;

export { ControlledScrollView, type ControlledScrollViewProps, type ControlledScrollViewRef, ScrollView, type ScrollViewProps, type ScrollViewRef };
