import { TouchStartEvent } from "../runtime/touch"
import { _descOf, _navSupported, Presentable } from "./presentable"
import { ContainerElement, buildUI, type BaseStyle, type Color, type ContainerStyle, type DrawableStyle, type PaddingStyle, type PositionStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn } from "./UINode"


export type UIWidgetStyle = ContainerStyle & DrawableStyle & PaddingStyle & BaseStyle & PositionStyle & {
  /** A layer behind the widget that intercepts clicks — `"transparent"` still intercepts, `null`
   *  removes the layer. A tap on it fires `onOverlayTap`. */
  overlayColor?: Color | null
}


/** A floating overlay — absolutely positioned in device coordinates, mounted above the page as
 *  its own layout root (never a child of what's underneath). The base of dialogs, toasts,
 *  mini-players and scene HUDs; `UIModal`/`UIPopover`/`UIBottomSheet` build on it. */
export interface UIWidget extends UIContainerBase<UIWidgetStyle, DrawableStyle & BaseStyle & { overlayColor?: Color | null }> {
  readonly type: "widget",

  /**
   * Attach this widget to a destination (a `Scene`, a `UIScreen`, …): the widget lives on that
   * page's layer — it shows/hides with the page and rides its transition, and a screen pushed
   * on top covers it (a scene HUD behaves like part of the scene). Sticky until `attachTo(null)`
   * (= back to a global overlay). Set it while the widget is hidden. The widget is always its own
   * overlay root mounted into the owner's page view — it never becomes a child of the owner, so it
   * stays topmost and its updates never trigger the owner's relayout. Works with a `UIScreen` that
   * is a page inside a `UIPager`: the widget mounts into that tab and rides its swipe. (Web shows
   * and hides it with the owner but does not yet ride the transition.)
   */
  attachTo(owner: Presentable | null): this,

  /** Show the widget. By default it is a global overlay above every destination — it stays
   *  visible across navigation until `hide()` (dialogs, mini-player, global loader). If the
   *  widget was attached via `attachTo(owner)`, it mounts on that page's layer instead and is
   *  only visible while the owner is presented. */
  show(): void,
  /** Unmount the widget. */
  hide(): void,

  /** Mounted right now (`show()` ran, `hide()` hasn't). */
  readonly isShow: boolean,

  /** Touch began on the widget; `ev.track(...)` takes over the rest of the gesture. */
  onTouchStart(callback: (ev: TouchStartEvent<UIWidget>) => void): this,
  /** Tap on the overlay layer (requires an `overlayColor`). */
  onOverlayTap(callback: () => void): this,
  /** Hardware/system back while the widget is shown. */
  onBackPressed(callback: () => void): this
}


export class WidgetElement extends ContainerElement<"widget"> {
  scrollable = false as const
  _owner?: Presentable
  constructor(type: "widget", style: UIWidgetStyle, children: UINodeChild[] | ChildrenFn) {
    // Widgets are absolutely positioned in device coordinates — in the constructor (not the
    // factory) so subclasses like ModalElement inherit it.
    super(type, { position: "absolute", ...style }, children)
  }
  attachTo(owner: Presentable | null): this {
    this._owner = owner ?? undefined
    return this
  }
  show(): void {
    // A widget is always its OWN overlay root (a separate Yoga tree mounted above the owner's
    // content) — never a child of the owner, so it stays topmost, its updates never relayout the
    // owner, and it doesn't pollute the owner's children. `attachTo(owner)` just tells the host
    // which page view to mount that root into; ownerless widgets are global overlays.
    if (this._owner && _navSupported()) {
      _creatorUI.showWidget(this, _descOf(this._owner))
    } else {
      // Unattached (or legacy host): global overlay above navigation.
      _creatorUI.showWidget(this)
    }
  }
  hide(): void {
    _creatorUI.hideWidget(this)
  }
  get isShow(): boolean {
    return this._id !== 0
  }

  _interactive = false
  readonly touchStartListeners: any[] = []
  onTouchStart (callback: any): this {
    this._interactive = true
    this.touchStartListeners.push(callback)
    return this
  }
  _emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
    const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
    touchEvent.target = this
    for (let callback of this.touchStartListeners) {
      callback(touchEvent)
      if ((touchEvent as any)._trackHandler) {
        return (touchEvent as any)._trackHandler
      }
    }
    return null
  }
  readonly otl: (() => void)[] = []
  onOverlayTap(callback: () => void): this {
    this.otl.push(callback)
    return this
  }

  _backButtonCallback?: () => void
  onBackPressed(callback: any): this {
    this._backButtonCallback = callback
    return this
  }
}


const makeWidget = (style: any, children: any) => new WidgetElement("widget", style, children)

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

/** Create a widget (same argument forms as `UIColumn`). Position it with absolute-style props
 *  (`top`/`left`/`bottom`/`right`), then `show()`/`hide()`. */
export function UIWidget(...children: UIChildArg[]): UIWidget;
export function UIWidget(children: UINodeChild[] | ChildrenFn): UIWidget;
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
export function UIWidget(style: UIWidgetStyle, children?: UINodeChild[] | ChildrenFn): UIWidget;
export function UIWidget(...args: any[]): UIWidget {
  return buildUI(args, null, makeWidget)
}