"use client";

import { useMemo, useRef } from "react";
import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect";
import type {
  Unstable_TriggerAdapter,
  Unstable_TriggerItem,
} from "@assistant-ui/core";
import type { Unstable_IconComponent } from "./useMentionAdapter";
import { matchesTriggerItemQuery } from "../primitives/composer/trigger/matchesTriggerItemQuery";

/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
export type Unstable_SlashCommand = {
  readonly id: string;
  readonly label?: string | undefined;
  readonly description?: string | undefined;
  readonly icon?: string | undefined;
  readonly execute: () => void;
};

/** @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release. */
export type Unstable_UseSlashCommandAdapterOptions = {
  readonly commands: readonly Unstable_SlashCommand[];
  /** Strip the trigger text from the composer after executing. @default false */
  readonly removeOnExecute?: boolean | undefined;
  /** Maps `metadata.icon` / `category.id` string keys to React components. */
  readonly iconMap?: Record<string, Unstable_IconComponent>;
  /** Fallback icon when no entry in `iconMap` matches. */
  readonly fallbackIcon?: Unstable_IconComponent;
};

/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
export type Unstable_SlashCommandAction = {
  readonly onExecute: (item: Unstable_TriggerItem) => void;
  readonly removeOnExecute?: boolean | undefined;
};

/**
 * Bundles slash command definitions (with inline `execute` callbacks) into
 * `{adapter, action}` that plug directly into `ComposerTriggerPopover`.
 * `execute` stays in the hook closure and is never attached to the returned
 * `TriggerItem`, keeping items serializable.
 *
 * @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release.
 *
 * @example
 * ```tsx
 * const slash = unstable_useSlashCommandAdapter({
 *   commands: [
 *     { id: "summarize", execute: () => runSummarize(), icon: "FileText" },
 *     { id: "translate", execute: () => runTranslate(), icon: "Languages" },
 *   ],
 * });
 *
 * <ComposerTriggerPopover char="/" {...slash} />
 * ```
 */
export function unstable_useSlashCommandAdapter(
  options: Unstable_UseSlashCommandAdapterOptions,
): {
  adapter: Unstable_TriggerAdapter;
  action: Unstable_SlashCommandAction;
  iconMap?: Record<string, Unstable_IconComponent>;
  fallbackIcon?: Unstable_IconComponent;
} {
  const { commands, removeOnExecute } = options;

  const commandsRef = useRef(commands);
  const committedItemsRef = useRef<readonly Unstable_TriggerItem[]>(undefined);
  const nextItems = commands.map(toItem);
  // A referential cache, never state: `items` is always structurally equal to
  // `nextItems`, so the render-time read cannot change what the adapter shows.
  const items = areTriggerItemsEqual(committedItemsRef.current, nextItems)
    ? committedItemsRef.current
    : nextItems;

  // The adapter's callbacks must only observe commands from committed renders.
  useIsomorphicLayoutEffect(() => {
    commandsRef.current = commands;
    committedItemsRef.current = items;
  }, [commands, items]);

  const adapter = useMemo<Unstable_TriggerAdapter>(
    () => ({
      categories: () => [],
      categoryItems: () => [],
      search: (query: string) => {
        const lower = query.toLowerCase();
        return items.filter((item) => matchesTriggerItemQuery(item, lower));
      },
    }),
    [items],
  );

  const action = useMemo<Unstable_SlashCommandAction>(
    () => ({
      onExecute: (item) => {
        commandsRef.current.find((c) => c.id === item.id)?.execute();
      },
      ...(removeOnExecute !== undefined ? { removeOnExecute } : {}),
    }),
    [removeOnExecute],
  );

  return useMemo(
    () => ({
      adapter,
      action,
      ...(options.iconMap ? { iconMap: options.iconMap } : {}),
      ...(options.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),
    }),
    [adapter, action, options.iconMap, options.fallbackIcon],
  );
}

function toItem(cmd: Unstable_SlashCommand): Unstable_TriggerItem {
  return {
    id: cmd.id,
    type: "command",
    label: cmd.label ?? `/${cmd.id}`,
    ...(cmd.description !== undefined ? { description: cmd.description } : {}),
    ...(cmd.icon !== undefined ? { metadata: { icon: cmd.icon } } : {}),
  };
}

function areTriggerItemsEqual(
  previous: readonly Unstable_TriggerItem[] | undefined,
  next: readonly Unstable_TriggerItem[],
): previous is readonly Unstable_TriggerItem[] {
  if (!previous || previous.length !== next.length) return false;

  return previous.every((item, index) => {
    const nextItem = next[index];
    return (
      nextItem !== undefined &&
      item.id === nextItem.id &&
      item.label === nextItem.label &&
      item.description === nextItem.description &&
      item.metadata?.icon === nextItem.metadata?.icon
    );
  });
}
