// Guide: /docs/creating-components · adapter internals: /docs/zag-adapter
// Authoring rules: notes/component-authoring.md (v2 pattern)
import * as dialogMachine from "@zag-js/dialog";
import type { MachineInput } from "marko-zag";
import Button from "../button/button.marko";
import type { ButtonVariants } from "../button/variants.ts";
import { cn } from "#lib/utils.ts";
import { stripOwnProps } from "#lib/native-attrs.ts";
import { alertDialog 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
// (<AlertDialog open:=myState/>); Zag's own `onOpenChange` also works and
// receives the details object.
// `Own` members SHADOW native attributes of the same name (folded into
// MachineInput's Props argument) instead of intersecting with them: `title`
// on a div is `string | false | null`, so a plain `&` would turn our
// attr-tag `title` into an unsatisfiable type.
export type Own = {
  openChange?: (open: boolean) => void;
  trigger?: Marko.AttrTag<{ content: Marko.Body<[Record<string, unknown>]> }>;
  media?: Marko.AttrTag<{ content: Marko.Body }>;
  title?: Marko.AttrTag<{ content: Marko.Body }>;
  description?: Marko.AttrTag<{ content: Marko.Body }>;
  /** `"default" | "sm"` — mirrors upstream's `AlertDialogContent` size prop. */
  size?: "default" | "sm";
  cancelText?: string;
  actionText?: string;
  action?: () => void;
  cancelVariant?: ButtonVariants["variant"];
  actionVariant?: ButtonVariants["variant"];
};

export type Input = MachineInput<"div", dialogMachine.Props & Own>;

// <zag> wires the machine in one tag and returns `api` as a GETTER —
// call it at every use site. Getters are the serializable currency of
// Marko's resumability: the api object itself must never become reactive
// state. See /docs/creating-components.
<zag/api=() => dialogMachine from=input
  role="alertdialog"
  closeOnInteractOutside=false
  closeOnEscape=false
/>

// Whatever is neither machine-owned (splitProps) nor component-owned
// (class, openChange, trigger, media, title, description, size, cancelText,
// actionText, action, cancelVariant, actionVariant) passes through to the
// content element: aria-*, data-*...
<const/nativeAttrs=() =>
  stripOwnProps(
    dialogMachine.splitProps(input)[1],
    "class",
    "openChange",
    "trigger",
    "media",
    "title",
    "description",
    "size",
    "cancelText",
    "actionText",
    "action",
    "cancelVariant",
    "actionVariant",
  )
>
<const/size=input.size ?? "default"/>

<if=input.trigger>
  <${input.trigger.content}({ ...api().getTriggerProps(), "data-slot": "alert-dialog-trigger" })/>
</if>
<zag-portal>
  <if=api().open>
    <div
      ...api().getBackdropProps()
      data-slot="alert-dialog-overlay"
      class=styles.overlay
    />
    <div ...api().getPositionerProps() data-slot="alert-dialog-positioner" class=styles.positioner>
      <div
        ...nativeAttrs()
        ...api().getContentProps()
        data-slot="alert-dialog-content"
        data-size=size
        class=cn(styles.content, input.class)
      >
        <div data-slot="alert-dialog-header" class=styles.header>
          <if=input.media>
            <div data-slot="alert-dialog-media" class=styles.media>
              <${input.media.content}/>
            </div>
          </if>
          <h2 ...api().getTitleProps() data-slot="alert-dialog-title" class=styles.title>
            <${input.title?.content}/>
          </h2>
          <p ...api().getDescriptionProps() data-slot="alert-dialog-description" class=styles.description>
            <${input.description?.content}/>
          </p>
        </div>
        <div data-slot="alert-dialog-footer" class=styles.footer>
          <Button type="button"
            ...api().getCloseTriggerProps()
            variant=(input.cancelVariant ?? "outline")
            data-slot="alert-dialog-cancel"
            class=styles.cancel
          >
            ${input.cancelText ?? "Cancel"}
          </Button>
          <Button type="button"
            ...api().getCloseTriggerProps()
            variant=(input.actionVariant ?? "default")
            data-slot="alert-dialog-action"
            class=styles.action
            // Unannotated: Marko infers the real <button> click signature
            // (PointerEvent + bound element). The previous `Event`/`Element`
            // annotations were wider than what Zag's own handler accepts.
            onClick(event, element) {
              input.action?.();
              // Marko types event attrs as `handler | false | undefined`
              // (`false` disables the handler), so `?.` is not enough — guard
              // on callability before invoking Zag's own close handler.
              const closeHandler = api().getCloseTriggerProps().onClick;
              if (typeof closeHandler === "function") closeHandler(event, element);
            }
          >
            ${input.actionText ?? "Continue"}
          </Button>
        </div>
      </div>
    </div>
  </if>
</zag-portal>

// Parents can receive the api getter: <AlertDialog/alertDialogApi/> then
// `alertDialogApi().open` — it stays live across machine updates.
<return=api>
