import type { UIContainerStyle } from "./UIContainer"
import { type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type Color, ContainerElement, buildUI } from "./UINode"

export type ScrollStyle = {
  showScrollbar?: boolean,
  overscrollMode?: "none" | "absorb" | "default",
  /** Tint of the native pull-to-refresh spinner (pairs with `onRefresh`). */
  refreshControlColor?: Color,
  /** How a scroll gesture in THIS scrollable dismisses the keyboard: `"interactive"` (default) —
   *  dragging over the keyboard slides it away (chat feel); `"scroll"` — any drag dismisses
   *  immediately; `"none"` — never (tap-outside policy still applies). */
  keyboardDismissMode?: "interactive" | "scroll" | "none",
  /** Paging: when the drag ends, rest on a direct child's boundary along the scroll axis — the
   *  value picks where the child sits in the viewport; `"none"` (default) is free scrolling. Snap
   *  targets are the children themselves, so item widths can differ (card-carousel-with-peek); a
   *  carousel is a horizontal scrollable of full-width children + `snap: "start"`. */
  snap?: "none" | "start" | "center" | "end"
}

export type UIScrollableStyle = UIContainerStyle & ScrollStyle & { scrollDirection?: "horizontal" | "vertical" }

/** A scroll container — vertical by default, `scrollDirection: "horizontal"` for carousels/chip
 *  rows. The scrolling body of a screen (screens themselves never scroll). */
export interface UIScrollable extends UIContainerBase<UIScrollableStyle> {
  readonly type: "scrollable",

  /** Scroll position changed (px from the start edge). */
  onScroll(callback: (scrollPosition: number) => void): UIScrollable,
  /** The scroll gesture ended (finger lifted). */
  onScrollRelease(callback: () => void): UIScrollable,
  /** Pulled past the content edge by `delta` px. */
  onOverscroll(callback: (delta: number) => void): UIScrollable
  /** Pull-to-refresh: the spinner stays until the returned promise settles. Attach BEFORE the
   *  node mounts (hosts read it at creation). Vertical only; native hosts — no-op on web. */
  onRefresh(callback: () => void | Promise<void>): UIScrollable
}

class ScrollableElement extends ContainerElement<"scrollable"> {

  readonly osl: any[] = []
  readonly sl: any[] = []
  readonly srl: any[] = []

  get scrollListeners() {
    return this.sl
  }

  onScroll (callback: any): this {
    this.sl.push(callback)
    return this
  }

  onScrollRelease (callback: any): this {
    this.srl.push(callback)
    return this
  }

  onOverscroll (callback: any): this {
    this.osl.push(callback)
    return this
  }

  // Read by the host at mount (same contract the scrollable screen had before 2026-07-22).
  declare _refreshCallback: any
  onRefresh (callback: any): this {
    this._refreshCallback = callback
    return this
  }
}

// flexShrink: 1 default (SDK-owned, like UIRow's flexDirection): without it a scrollable in a
// flex parent grows to its content height and overflows instead of scrolling.
const SCROLLABLE_DEFAULTS = { flexShrink: 1 } as const
const makeScrollable = (style: any, children: any) => new ScrollableElement("scrollable", style, children)

/** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
export const __UIScrollable = (children: UINodeChild[] | ChildrenFn): UIScrollable => makeScrollable({ ...SCROLLABLE_DEFAULTS }, children)

/** Create a scroll container (same argument forms as `UIColumn`). As a screen body, pair with
 *  `flexGrow: 1`. */
export function UIScrollable(...children: UIChildArg[]): UIScrollable
export function UIScrollable(children: UINodeChild[] | ChildrenFn): UIScrollable
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
export function UIScrollable(style: UIScrollableStyle, children?: UINodeChild[] | ChildrenFn): UIScrollable
export function UIScrollable(...args: any[]): UIScrollable {
  return buildUI(args, SCROLLABLE_DEFAULTS, makeScrollable)
}
