import { PagerElement, type UIPager } from "./UIPager"
import { ScreenElement, UIScreen } from "./UIScreen"
import { UIButton } from "./UIButton"
import { UIRow, UIColumn, type UIRow as UIRowType } from "./UIContainer"
import { UIText } from "./UIText"
import { UIImage, type ImageSource } from "./UIImage"
import type { UIChildArg } from "./UINode"

// UITabs — the standard bottom-tab shell, prewired: a UIScreen composing [ UIPager (flexGrow: 1),
// tab bar ]. Pure SDK composition over the pager — zero new ABI, works wherever UIPager does.
// `defineTabs` (below) is its design-stage twin (lecodes-design/tabbar-surface.md): the same keyed
// config without screens, mounted per-file with a literal tab id. They share the constants and bar
// geometry in this file, so a designed mockup and the built app render identically.

/** Active/inactive/bar colors ride the theme — one `theme({ primaryColor, mutedColor, tabbarBg })`
 *  call restyles the bar app-wide; the fallbacks keep an unthemed app looking right. */
const ACTIVE = "var(--primaryColor, #5b8cff)"
const INACTIVE = "var(--mutedColor, #8a919e)"
const BAR_BG = "var(--tabbarBg, #15171c)"
const BAR_BORDER = "var(--tabbarBorder, #23262e)"
const SCREEN_BG = "var(--screenBg, #101114)"
const BADGE_BG = "var(--badgeColor, #ff453a)"

export type UITabDef = {
  label: string,
  /** Tab icon — typically `assetIcon("lucide:…")`. Tinted active/inactive by the bar. */
  icon?: ImageSource,
  /** The tab's root screen. Tabs are fixed at construction (like UIPager's). */
  screen: UIScreen,
}

/**
 * A bottom-tab app shell: swipeable tabs (a `UIPager`) plus a themed tab bar, as one
 * `UIScreen` — present it directly (`Router.init(tabs)`). Keys are tab ids, in tab order:
 *
 * ```ts
 * const tabs = UITabs({
 *   home:    { label: "Home",    icon: assetIcon("lucide:house"), screen: homeScreen },
 *   profile: { label: "Profile", icon: assetIcon("lucide:user"),  screen: profileScreen },
 * })
 * Router.init(tabs)
 * ```
 *
 * In-tab navigation stays the pager's: `UIPager.push(detail)` from any screen (bar stays);
 * `Router.push` opens above the shell (bar covered). Restyle via `theme()` (primaryColor,
 * mutedColor, tabbarBg, …) or the `bar` handle; for a fully custom bar build on `UIPager`.
 */
export interface UITabs extends UIScreen {
  /** The active tab id. */
  readonly tab: string,
  /** Switch tabs (instant, the tab-bar convention; `animated: true` slides). */
  select(id: string, animated?: boolean): this,
  /** Fires when the active tab changes and has settled — a bar tap, swipe, or `select()`. */
  onSelect(callback: (id: string, index: number) => void): this,
  /** Show a badge on a tab: `true` = dot, a number/string = count pill, `false`/`null`/`0` clears. */
  badge(id: string, value: number | string | boolean | null): this,
  /** The pager under the bar — in-tab stacks (`push`/`pop`/`depth`). */
  readonly pager: UIPager,
  /** The bar row (named "tabbar") — for restyling beyond the theme variables. */
  readonly bar: UIRowType,
}

export class TabsElement extends ScreenElement {
  private readonly ids: string[]
  private readonly _pagerEl: PagerElement
  private readonly _bar: any
  private readonly _icons: (any | null)[] = []
  private readonly _labels: any[] = []
  private readonly _badges: any[] = []
  private readonly _badgeTexts: any[] = []
  private readonly _buttons: any[] = []
  private readonly _selectListeners: ((id: string, index: number) => void)[] = []

  constructor(defs: Record<string, UITabDef>) {
    super("screen", { bgColor: SCREEN_BG }, [])
    this.ids = Object.keys(defs)
    if (this.ids.length === 0) console.warn("UITabs: no tabs declared")

    this._pagerEl = new PagerElement({ flexGrow: 1 }, this.ids.map((id) => defs[id].screen))

    const buttons = this.ids.map((id, i) => {
      const def = defs[id]
      const icon = def.icon ? UIImage(def.icon).style({ width: 22, height: 22, tintColor: INACTIVE }) : null
      const label = UIText(def.label).style({ fontSize: 10, color: INACTIVE })
      const badgeText = UIText("").style({ fontSize: 9, fontWeight: 700, color: "white" })
      const badge = UIRow(badgeText).style({
        position: "absolute", top: -3, right: -6, display: "none",
        bgColor: BADGE_BG, borderRadius: 99, justifyContent: "center", alignItems: "center",
      })
      // The badge anchors to the icon (or the label when there is none) — its wrap must not clip.
      const anchor = UIColumn(icon ?? label, badge).style({ overflow: "visible", alignItems: "center" })
      this._icons.push(icon)
      this._labels.push(label)
      this._badges.push(badge)
      this._badgeTexts.push(badgeText)
      const button = UIButton(anchor, icon ? label : null)
        .style({ name: `tab-${id}`, flexDirection: "column", gap: 3, flex: 1, pt: 8, pb: 6 })
        .onClick(() => this.select(id))
      this._buttons.push(button)
      return button
    })

    this._bar = UIRow(buttons).style({
      name: "tabbar", bgColor: BAR_BG, pb: "comfort-bottom",
      borderTopWidth: 1, borderTopColor: BAR_BORDER,
    })

    this._pagerEl.onSelect((i) => {
      this._applyActive(i)
      for (const cb of this._selectListeners) cb(this.ids[i], i)
    })
    this._applyActive(0)
    this.append(this._pagerEl as any, this._bar)
  }

  private _applyActive(index: number): void {
    for (let i = 0; i < this.ids.length; i++) {
      const on = i === index
      const color = on ? ACTIVE : INACTIVE
      // Imperative writes carry the visual on every host; the class is the styling hook — custom
      // content inside a tab button can declare `$active` blocks (class state cascades down).
      this._buttons[i].class.active = on
      if (this._icons[i]) this._icons[i].style.tintColor = color
      this._labels[i].style.color = color
    }
  }

  get tab(): string {
    return this.ids[this._pagerEl.index]
  }
  get pager(): any {
    return this._pagerEl
  }
  get bar(): any {
    return this._bar
  }

  select(id: string, animated = false): this {
    const index = this.ids.indexOf(id)
    if (index < 0) {
      console.warn(`UITabs.select: unknown tab "${id}"`)
      return this
    }
    this._pagerEl.select(index, animated)
    return this
  }

  onSelect(callback: (id: string, index: number) => void): this {
    this._selectListeners.push(callback)
    return this
  }

  badge(id: string, value: number | string | boolean | null | undefined): this {
    const i = this.ids.indexOf(id)
    if (i < 0) {
      console.warn(`UITabs.badge: unknown tab "${id}"`)
      return this
    }
    if (value === null || value === undefined || value === false || value === 0 || value === "") {
      this._badges[i].style.display = "none"
    } else if (value === true) {
      this._badgeTexts[i].text = ""
      this._badges[i].style({ display: "flex", width: 9, height: 9, minWidth: 0, px: 0 })
    } else {
      this._badgeTexts[i].text = String(value)
      this._badges[i].style({ display: "flex", width: "auto", height: 14, minWidth: 14, px: 4 })
    }
    return this
  }
}

/** Create the standard bottom-tab shell — see {@link UITabs}. Keys are tab ids in tab order;
 *  each tab needs a `label` and its root `screen` (plus an optional `icon`). */
export function UITabs(tabs: Record<string, UITabDef>): UITabs {
  return new TabsElement(tabs) as unknown as UITabs
}

// ---- defineTabs — the design-stage twin ---------------------------------------------------------

/** A `defineTabs` tab: `label` + optional `icon` (an `assetIcon("lucide:…")` call — never a
 *  hand-written source object). `UITabDef` is this plus the tab's root `screen`. */
export type TabDef = Omit<UITabDef, "screen">

/** What `defineTabs` returns: the bar (mount it yourself when the screen owns its whole layout)
 *  and the standard screen shell around it. `active` must be a declared tab id, as a literal. */
export type TabsHandle<T extends Record<string, TabDef>> = {
  /** Tab ids in declaration order. */
  ids: (keyof T & string)[]
  /** The bar alone — one button per tab, the `active` one highlighted. */
  bar(active: keyof T & string): UIRowType
  /** A screen sitting ON the bar: `[ padded content column, bar ]`. */
  screen(active: keyof T & string, ...children: UIChildArg[]): UIScreen
}

/**
 * Declare a tab bar WITHOUT screens — the design-stage twin of {@link UITabs} (same keys, same
 * `tab-<id>` button names, same theme variables), for mockups where every screen is its own file
 * and mounts the bar with a literal tab id:
 *
 * ```ts
 * export const mainTabs = defineTabs({
 *   home:    { label: "Home",    icon: assetIcon("lucide:house") },
 *   profile: { label: "Profile", icon: assetIcon("lucide:user") },
 * })
 * // in a screen file:
 * export default () => mainTabs.screen("home", [ ...content ])
 * ```
 *
 * Restyle through the theme — `theme({ primaryColor, mutedColor, tabbarBg, screenBg })` recolors
 * every bar at once. The built app passes the same keys (plus each tab's root screen) to `UITabs`,
 * which renders the identical bar interactively.
 */
export function defineTabs<T extends Record<string, TabDef>>(tabs: T): TabsHandle<T> {
  const ids = Object.keys(tabs) as (keyof T & string)[]

  const bar = (active: keyof T & string) =>
    UIRow(ids.map((id) => {
      const def = tabs[id]
      const color = id === active ? ACTIVE : INACTIVE
      return UIButton(
        def.icon ? UIImage(def.icon).style({ width: 22, height: 22, tintColor: color }) : null,
        UIText(def.label).style({ fontSize: 10, color }),
      ).style({ name: `tab-${id}`, flexDirection: "column", gap: 3, flex: 1, pt: 8, pb: 6 })
    })).style({
      name: "tabbar", bgColor: BAR_BG, pb: "comfort-bottom",
      borderTopWidth: 1, borderTopColor: BAR_BORDER,
    })

  const screen = (active: keyof T & string, ...children: UIChildArg[]) =>
    UIScreen(
      UIColumn(...children).style({ flexGrow: 1, p: 16, pt: "safe-top" }),
      bar(active),
    ).style({ bgColor: SCREEN_BG })

  return { ids, bar, screen }
}
