import * as popoverMachine from "@zag-js/popover";
import type { MachineInput } from "marko-zag";
import { positionerStyle } from "marko-zag";
import { cn } from "#lib/utils.ts";
import { popover as styles } from "./classes.ts";

// MachineInput = native <div> attributes ∩ the machine's full Props, with
// `id` made optional (generated below, overridable). `openChange` is
// additive sugar enabling Marko's two-way bind shorthand
// (<Popover open:=myState/>); Zag's own `onOpenChange` also works and
// receives the details object. Align/sideOffset are NOT separate props —
// zag exposes them via `positioning.align` / `positioning.gutter`
// (@zag-js/popper `PositioningOptions`), already forwarded through
// `popoverMachine.props`.
export type Input = MachineInput<"div", popoverMachine.Props & Own> & {
  /** Trigger render-prop — the element that opens the popover. */
  trigger: Marko.AttrTag<{ content: Marko.Body<[Record<string, unknown>]> }>;
  /** Popover content shown when open. Optional — a title/description-only popover is legitimate. */
  content?: Marko.Body;
  /** Extra class for the content panel. */
  class?: string;
  openChange?: (open: boolean) => void;
};

// Folded into the Props argument (not intersected after MachineInput) so
// marko-zag 3.0.0's automatic native-attribute shadowing applies: title
// collides with the native div title attribute and would otherwise become
// an unsatisfiable AttrString-by-AttrTag intersection. aria-label needs no
// declaration — it is a native div attribute and is forwarded below as
// input["aria-label"].
export type Own = {
  /** Title of the popover, automatically wired to aria-labelledby. */
  title?: Marko.AttrTag<{ content: Marko.Body }>;
  /** Description of the popover. */
  description?: Marko.AttrTag<{ content: Marko.Body }>;
};

<zag/api=() => popoverMachine from=input/>

<div data-slot="popover">
  <${input.trigger.content}({ ...api().getTriggerProps(), "data-slot": "popover-trigger" })/>
  // NOTE: upstream shadcn sets data-slot="popover-portal" on Radix's Portal,
  // which renders a real element. marko-zag's <zag-portal> renders a
  // `display:contents` host and does not forward attributes, so the
  // marker never reached the DOM — it was inert, and nothing in this
  // repo's CSS, tests or tooling reads it. Dropped rather than left as
  // dead markup; restore it if <zag-portal> gains attribute forwarding.
  <zag-portal>
    <if=api().open>
      <div ...api().getPositionerProps() style=positionerStyle data-slot="popover-positioner">
        <div
          ...api().getContentProps()
          aria-labelledby=(input.title ? api().getTitleProps().id : undefined)
          aria-label=input["aria-label"]
          data-slot="popover-content"
          class=cn(styles.content, input.class)
        >
          <if=input.title>
            <div ...api().getTitleProps() data-slot="popover-title">
              <${input.title.content}/>
            </div>
          </if>
          <if=input.description>
            <div ...api().getDescriptionProps() data-slot="popover-description">
              <${input.description.content}/>
            </div>
          </if>
          <if=input.content>
            <${input.content}/>
          </if>
        </div>
      </div>
    </if>
  </zag-portal>
</div>

<return=api>
