"use client";

import { composeEventHandlers } from "radix-ui/internal";
import { useAuiState } from "@assistant-ui/store";
import { Primitive } from "../../utils/Primitive";
import {
  type ComponentRef,
  type FormEvent,
  forwardRef,
  type ComponentPropsWithoutRef,
  type MouseEvent,
  useMemo,
  useState,
  version,
} from "react";
import { useComposerSend } from "./ComposerSend";
import { ComposerCompactContext } from "./ComposerCompactContext";
import {
  COMPOSER_INPUT_SELECTOR,
  CONTENT_EDITABLE_SELECTOR,
} from "./composerInputSelector";

// Keeps tabindex="-1": roving-tabindex widgets click-focus items at -1.
const INTERACTIVE_ELEMENT_SELECTOR = [
  "a",
  "audio[controls]",
  "button",
  "details",
  "embed",
  "iframe",
  "input",
  "label",
  "select",
  "summary",
  "textarea",
  "video[controls]",
  CONTENT_EDITABLE_SELECTOR,
  "[tabindex]",
].join(", ");

export namespace ComposerPrimitiveRoot {
  export type Element = ComponentRef<typeof Primitive.form>;
  /**
   * Props for the ComposerPrimitive.Root component.
   * Accepts all standard form element props.
   */
  export type Props = ComponentPropsWithoutRef<typeof Primitive.form> & {
    /**
     * Opt the composer into compact mode. While the input holds at most a single
     * line of text and there are no attachments, quote, queued messages, or
     * active dictation, the root renders a `data-compact` attribute so styles
     * can collapse the composer into a single row. It expands automatically
     * when any of those conditions stop holding.
     * @default false
     */
    compact?: boolean | undefined;
  };
}

// React 18 drops a boolean `inert`, so it receives the attribute's string form.
const isReact18 = version.startsWith("18.");

/**
 * The root form container for message composition.
 *
 * This component provides a form wrapper that handles message submission when the form
 * is submitted (e.g., via Enter key or submit button). It automatically prevents the
 * default form submission and triggers the composer's send functionality.
 *
 * Clicking blank form space focuses the composer input (the first textarea or
 * contenteditable inside the form). Text selection cannot start from blank
 * areas; descendants that need native mousedown defaults can call
 * `stopPropagation()` in their own `onMouseDown`.
 *
 * @example
 * ```tsx
 * <ComposerPrimitive.Root>
 *   <ComposerPrimitive.Input placeholder="Type your message..." />
 *   <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
 * </ComposerPrimitive.Root>
 * ```
 */
export const ComposerPrimitiveRoot = forwardRef<
  ComposerPrimitiveRoot.Element,
  ComposerPrimitiveRoot.Props
>(({ onSubmit, onMouseDown, compact, inert, ...rest }, forwardedRef) => {
  const send = useComposerSend();
  const composerType = useAuiState((s) => s.composer.type);

  const [multiline, setMultiline] = useState(false);
  const compactContext = useMemo(() => ({ setMultiline }), []);
  const stateAllowsCompact = useAuiState((s) =>
    compact
      ? s.composer.attachments.length === 0 &&
        s.composer.quote == null &&
        s.composer.queue.length === 0 &&
        s.composer.dictation == null &&
        !s.composer.text.includes("\n")
      : false,
  );
  const isCompact = stateAllowsCompact && !multiline;
  const inertAttribute =
    isReact18 && typeof inert === "boolean" ? (inert ? "" : undefined) : inert;

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();

    if (!send) return;
    send();
  };

  const handleMouseDown = (e: MouseEvent<HTMLFormElement>) => {
    if (e.button !== 0) return;
    const target = e.target;
    // Portaled descendants (e.g. a dialog overlay) propagate mousedown through
    // the React tree while their DOM nodes live outside the form.
    if (!(target instanceof Element) || !e.currentTarget.contains(target))
      return;
    // Bound the ancestor walk to the form: hosts like AssistantModal render
    // the composer inside a wrapper carrying tabindex="-1" (Radix FocusScope).
    const interactive = target.closest(INTERACTIVE_ELEMENT_SELECTOR);
    if (interactive && e.currentTarget.contains(interactive)) return;
    const input = e.currentTarget.querySelector<HTMLElement>(
      COMPOSER_INPUT_SELECTOR,
    );
    if (!input) return;
    e.preventDefault();
    input.focus();
  };

  return (
    <ComposerCompactContext.Provider value={compact ? compactContext : null}>
      <Primitive.form
        {...rest}
        inert={inertAttribute as boolean | undefined}
        data-aui-composer-type={composerType}
        data-compact={isCompact ? "" : undefined}
        ref={forwardedRef}
        onSubmit={composeEventHandlers(onSubmit, handleSubmit)}
        onMouseDown={composeEventHandlers(onMouseDown, handleMouseDown)}
      />
    </ComposerCompactContext.Provider>
  );
});

ComposerPrimitiveRoot.displayName = "ComposerPrimitive.Root";
