import * as marqueeMachine from "@zag-js/marquee";
import type { MachineInput } from "marko-zag";
import { cn } from "#lib/utils.ts";
import { stripOwnProps } from "#lib/native-attrs.ts";
import { marquee as styles } from "./classes.ts";

// `content` SHADOWs the native `content` attribute (folded into
// MachineInput's Props argument) instead of intersecting with it.
export type Own<T = unknown> = {
  items?: T[];
  /**
   * Render prop for `items=` entries.
   *
   * The item is `T | undefined`, not `T`: a bodyless `<@item/>` produces an
   * entry with no datum, and the render path below still reaches this body for
   * it (the `<else if=input.content>` branch). Declaring it as `T` promised a
   * value this component does not always pass.
   */
  content?: Marko.Body<[T | undefined, number]>;
  item?: Marko.AttrTag<{ content?: Marko.Body }>;
  pausedChange?: (paused: boolean) => void;
};

export type Input<T = unknown> = MachineInput<"div", marqueeMachine.Props & Own<T>>;

<zag/api=() => marqueeMachine from=input
  onPauseChange(details: marqueeMachine.PauseStatusDetails) {
    input.onPauseChange?.(details);
    input.pausedChange?.(details.paused);
  }
/>

<const/nativeAttrs=() =>
  stripOwnProps(
    marqueeMachine.splitProps(input)[1],
    "class",
    "items",
    "content",
    "item",
    "pausedChange",
  )
>

// Normalize BOTH sources into ONE explicit common shape (mirrors
// carousel.marko / radio-group.marko) so the render path below never
// branches on the source and needs no casts: `body` carries the attr-tag
// markup body, `item` the items=-derived datum handed to the `content`
// render prop (or rendered as text). Attr tags win if both are present
// (compound is primary per the compound-tag-dx report).
// `item` is `T | undefined`, not `unknown`: the attr-tag branch carries no
// datum, but the items= branch carries a real `T` that must survive into the
// `content` render prop's parameter. Erasing it to `unknown` made the
// `<${input.content}(entry.item, itemIndex)>` call below a type error.
<const/itemTags=[...(input.item ?? [])].map((tag) => ({ body: tag.content, item: undefined as T | undefined }))/>
<const/entries=(itemTags.length > 0 ? itemTags : (input.items ?? []).map((item) => ({ body: undefined as Marko.Body | undefined, item: item as T | undefined })))/>

<style>
  @keyframes marko-ui-marquee-scroll-x {
    from { transform: translateX(0); }
    to { transform: translateX(var(--marquee-translate)); }
  }
  @keyframes marko-ui-marquee-scroll-y {
    from { transform: translateY(0); }
    to { transform: translateY(var(--marquee-translate)); }
  }
  [data-scope="marquee"][data-part="content"] {
    animation-name: marko-ui-marquee-scroll-x;
    animation-duration: var(--marquee-duration);
    animation-delay: var(--marquee-delay);
    animation-timing-function: linear;
    animation-iteration-count: var(--marquee-loop-count);
    animation-direction: normal;
  }
  [data-scope="marquee"][data-part="content"][data-orientation="vertical"] {
    animation-name: marko-ui-marquee-scroll-y;
  }
  [data-scope="marquee"][data-part="content"][data-reverse] {
    animation-direction: reverse;
  }
  [data-scope="marquee"][data-state="paused"] [data-part="content"] {
    animation-play-state: paused;
  }
  @media (prefers-reduced-motion: reduce) {
    [data-scope="marquee"][data-part="content"] {
      animation-play-state: paused;
    }
  }
</style>

<div
  ...nativeAttrs()
  ...api().getRootProps()
  data-slot="marquee"
  class=cn(styles.root, input.class)
>
  <div ...api().getEdgeProps({ side: "start" }) data-slot="marquee-edge-start" class=styles.edgeStart/>
  <div ...api().getEdgeProps({ side: "end" }) data-slot="marquee-edge-end" class=styles.edgeEnd/>
  <div ...api().getViewportProps() data-slot="marquee-viewport" class=styles.viewport>
    <for|_, index| of=Array.from({ length: api().contentCount })>
      <div ...api().getContentProps({ index }) data-slot="marquee-content" class=styles.content>
        <for|entry, itemIndex| of=entries>
          <div ...api().getItemProps() data-slot="marquee-item" class=styles.item>
            <if=entry.body>
              <${entry.body}/>
            </if>
            <else if=input.content>
              <${input.content}(entry.item, itemIndex)/>
            </else>
            <else>
              ${entry.item}
            </else>
          </div>
        </for>
      </div>
    </for>
  </div>
</div>

<return=api>
