import { Machine, EventObject, Service } from '@zag-js/core';
import { PropTypes, RequiredBy, DirectionProperty, CommonProperties } from '@zag-js/types';
import { SplitterRegistry } from './utils/registry.mjs';

type ResizeEvent = PointerEvent | KeyboardEvent;
type PanelId = string;
type ResizeTriggerId = `${PanelId}:${PanelId}` | `${PanelId}:` | `:${PanelId}`;
type PanelSize = number | string;
type PanelResizeBehavior = "preserve-relative-size" | "preserve-pixel-size";
interface PanelData {
    /**
     * The id of the panel.
     */
    id: PanelId;
    /**
     * The order of the panel. useful of you intend to conditionally render the panel.
     */
    order?: number | undefined;
    /**
     * The minimum size of the panel.
     */
    minSize?: PanelSize | undefined;
    /**
     * The maximum size of the panel.
     */
    maxSize?: PanelSize | undefined;
    /**
     * Whether the panel is collapsible.
     */
    collapsible?: boolean | undefined;
    /**
     * How the panel should behave when the parent group is resized.
     */
    resizeBehavior?: PanelResizeBehavior | undefined;
    /**
     * The size of the panel when collapsed.
     */
    collapsedSize?: PanelSize | undefined;
}
type NormalizedPanelData = Omit<PanelData, "minSize" | "maxSize" | "collapsedSize"> & {
    minSize?: number | undefined;
    maxSize?: number | undefined;
    collapsedSize?: number | undefined;
};
interface CursorState {
    isAtMin: boolean;
    isAtMax: boolean;
}
interface ResizeDetails {
    size: number[];
    resizeTriggerId: string | null;
    layout: string;
    expandToSizes: Record<string, number>;
}
interface ResizeEndDetails {
    size: number[];
    resizeTriggerId: string | null;
}
interface ExpandCollapseDetails {
    panelId: string;
    size: number;
}
type ElementIds = Partial<{
    root: string;
    resizeTrigger: (id: string) => string;
    label: (id: string) => string;
    panel: (id: string | number) => string;
}>;
interface SplitterProps extends DirectionProperty, CommonProperties {
    /**
     * The orientation of the splitter. Can be `horizontal` or `vertical`
     * @default "horizontal"
     */
    orientation?: "horizontal" | "vertical" | undefined;
    /**
     * The controlled size data of the panels
     */
    size?: PanelSize[] | undefined;
    /**
     * The initial size of the panels when rendered.
     * Use when you don't need to control the size of the panels.
     */
    defaultSize?: PanelSize[] | undefined;
    /**
     * The size constraints of the panels.
     */
    panels: PanelData[];
    /**
     * Function called when the splitter is resized.
     */
    onResize?: ((details: ResizeDetails) => void) | undefined;
    /**
     * Function called when the splitter resize starts.
     */
    onResizeStart?: (() => void) | undefined;
    /**
     * Function called when the splitter resize ends.
     */
    onResizeEnd?: ((details: ResizeEndDetails) => void) | undefined;
    /**
     * The ids of the elements in the splitter. Useful for composition.
     */
    ids?: ElementIds | undefined;
    /**
     * The number of pixels to resize the panel by when the keyboard is used.
     */
    keyboardResizeBy?: number | null | undefined;
    /**
     * The nonce for the injected splitter cursor stylesheet.
     */
    nonce?: string | undefined;
    /**
     * Function called when a panel is collapsed.
     */
    onCollapse?: ((details: ExpandCollapseDetails) => void) | undefined;
    /**
     * Function called when a panel is expanded.
     */
    onExpand?: ((details: ExpandCollapseDetails) => void) | undefined;
    /**
     * The splitter registry to use for multi-drag support.
     * When provided, enables dragging at the intersection of multiple splitters.
     */
    registry?: SplitterRegistry | undefined;
}
type PropsWithDefault = "orientation" | "panels";
interface DragState {
    resizeTriggerId: string;
    resolvedResizeTriggerId: `${PanelId}:${PanelId}`;
    resizeTriggerRect: DOMRect;
    initialCursorPosition: number;
    initialSize: number[];
}
interface KeyboardState {
    resizeTriggerId: string;
    resolvedResizeTriggerId: `${PanelId}:${PanelId}` | null;
}
interface Context {
    dragState: DragState | null;
    keyboardState: KeyboardState | null;
    size: number[];
    panels: NormalizedPanelData[];
}
interface Refs {
    panelSizeBeforeCollapse: Map<string, number>;
    panelIdToLastNotifiedSizeMap: Map<string, number>;
    prevDelta: number;
    initialSize: number[] | null;
    prevInitialLayout: string | null;
    prevGroupSize: number | null;
    lastRequestedSize: number[] | null;
    suppressOnResize: boolean;
}
interface SplitterSchema {
    state: "idle" | "hover:temp" | "hover" | "dragging" | "focused";
    tag: "focus";
    props: RequiredBy<SplitterProps, PropsWithDefault>;
    context: Context;
    computed: {
        horizontal: boolean;
    };
    refs: Refs;
    action: string;
    event: EventObject;
    effect: string;
    guard: string;
}
type SplitterService = Service<SplitterSchema>;
type SplitterMachine = Machine<SplitterSchema>;
interface PanelProps {
    id: PanelId;
}
interface ResizeTriggerProps {
    id: ResizeTriggerId;
    disabled?: boolean | undefined;
}
interface ResizeTriggerState {
    dragging: boolean;
    focused: boolean;
    disabled: boolean;
}
interface PanelItem {
    type: "panel";
    id: PanelId;
}
interface ResizeTriggerItem {
    type: "handle";
    id: ResizeTriggerId;
}
type SplitterItem = PanelItem | ResizeTriggerItem;
interface SplitterApi<T extends PropTypes = PropTypes> {
    /**
     * Whether the splitter is currently being resized.
     */
    dragging: boolean;
    /**
     * The orientation of the splitter.
     */
    orientation: "horizontal" | "vertical";
    /**
     * Returns the current sizes of the panels.
     */
    getSizes: () => number[];
    /**
     * Sets the sizes of the panels.
     */
    setSizes: (size: PanelSize[]) => void;
    /**
     * Returns the items of the splitter.
     */
    getItems: () => SplitterItem[];
    /**
     * Returns the panels of the splitter.
     */
    getPanels: () => PanelData[];
    /**
     * Returns the panel with the specified id.
     */
    getPanelById: (id: PanelId) => PanelData;
    /**
     * Returns the size of the specified panel.
     */
    getPanelSize: (id: PanelId) => number;
    /**
     * Returns whether the specified panel is collapsed.
     */
    isPanelCollapsed: (id: PanelId) => boolean;
    /**
     * Returns whether the specified panel is expanded.
     */
    isPanelExpanded: (id: PanelId) => boolean;
    /**
     * Collapses the specified panel.
     */
    collapsePanel: (id: PanelId) => void;
    /**
     * Expands the specified panel.
     */
    expandPanel: (id: PanelId, minSize?: number) => void;
    /**
     * Resizes the specified panel.
     */
    resizePanel: (id: PanelId, unsafePanelSize: number) => void;
    /**
     * Returns the layout of the splitter.
     */
    getLayout: () => string;
    /**
     * Resets the splitter to its initial state.
     */
    resetSizes: VoidFunction;
    /**
     * Returns the state of the resize trigger.
     */
    getResizeTriggerState: (props: ResizeTriggerProps) => ResizeTriggerState;
    getRootProps: () => T["element"];
    getPanelProps: (props: PanelProps) => T["element"];
    getResizeTriggerProps: (props: ResizeTriggerProps) => T["element"];
    getResizeTriggerIndicator: (props: ResizeTriggerProps) => T["element"];
}

export type { CursorState, DragState, ElementIds, ExpandCollapseDetails, KeyboardState, NormalizedPanelData, PanelData, PanelId, PanelItem, PanelProps, PanelResizeBehavior, PanelSize, ResizeDetails, ResizeEndDetails, ResizeEvent, ResizeTriggerId, ResizeTriggerItem, ResizeTriggerProps, ResizeTriggerState, SplitterApi, SplitterItem, SplitterMachine, SplitterProps, SplitterSchema, SplitterService };
