'use client';

import { useCallback, useMemo, useRef } from 'react';

import {
  LazyMarkdownEditor as MarkdownEditor,
  type MarkdownEditorHandle,
  type MentionConfig,
  type SlashCommandInfo,
} from '@djangocfg/ui-tools/markdown-editor';

import { useRegisterComposer } from '../hooks/useAutoFocusOnStreamEnd';
import type { ComposerSize, ComposerTextareaProps } from './types';

/**
 * Single-row min-height FLOOR per composer size (px).
 *
 * This is a floor, NOT the row height — the empty `<p>`'s own line box
 * defines the real single-row height. The floor must stay BELOW one line
 * box (15px text × line-height) so it can never inflate the editor past
 * one line: when a host overrides the editor leading (e.g. cmdop pins
 * `.ProseMirror` to `leading-normal` ≈ 22.5px), a 24/28px min-height used
 * to sit ABOVE the line box and the empty composer read as having a
 * phantom extra line under the caret. Keeping every floor ≤ the tightest
 * expected single line (≈22px) means the line box always wins and the
 * single-row composer is exactly one line tall in every host.
 */
const MIN_HEIGHT: Record<ComposerSize, number> = {
  sm: 18,
  md: 20,
  lg: 22,
};

export interface ComposerRichTextareaProps extends ComposerTextareaProps {
  /** `@`-mention autocomplete config. Omit for a plain rich textarea. */
  mentions?: MentionConfig;
  /**
   * Slash-command verb list. When set, a leading `/verb` in the editor
   * is painted as an atom chip (same primary-tinted styling as the
   * plain `<SlashHighlightTextarea>` mirror). The chat composer's slash
   * menu already lives outside the editor and keeps working without
   * this prop — pass the verb list here when you also want the
   * in-editor highlight to match.
   *
   * Structurally compatible with `SlashCommand` from
   * `@djangocfg/ui-tools/chat` — passing the same array you wire into
   * `composerSlots.slashCommands` is the normal pattern.
   *
   * Note: like `mentions`, the underlying TipTap extension is captured
   * once on first render. If you may register slash commands later,
   * pass `[]` from the very first render and mutate the list in place.
   */
  slashCommands?: readonly SlashCommandInfo[];
}

/**
 * Drop-in TipTap-backed textarea for the composer's Tier B `Textarea`
 * slot. Pre-wired with chat defaults — `unstyled` (the composer surface
 * draws the frame), no toolbar, size-matched single-row height, and
 * Enter-to-send. The host passes only `mentions` if it needs `@`
 * autocomplete; everything else flows from `ComposerTextareaProps`.
 *
 * @example
 * <Composer
 *   slots={{ Textarea: (p) => <ComposerRichTextarea {...p} mentions={cfg} /> }}
 * />
 */
export function ComposerRichTextarea({
  composer,
  placeholder,
  disabled,
  size,
  className,
  mentions,
  slashCommands,
}: ComposerRichTextareaProps) {
  // Tiptap captures the mentions object once on first render — keep its
  // identity stable so the suggestion plugin stays wired.
  const stableMentions = useMemo(() => mentions, [mentions]);
  // `slashCommands` flows through every render — the editor's
  // SlashCommandNode is registered once on first mount (gated on
  // `slashCommands !== undefined`) and the current list is read via a
  // ref by the value-sync effect, so runtime additions / removals
  // still light up the chip.

  // The TipTap editor has no `<textarea>`, so the composer's default
  // `focus` handle (which targets `textareaRef`) is a no-op here.
  // Register the editor's real imperative handle with the chat context
  // so `useAutoFocusOnStreamEnd` (refocus after the assistant reply)
  // and the voice slot actually drive *this* editor.
  const editorRef = useRef<MarkdownEditorHandle>(null);
  const focus = useCallback(() => editorRef.current?.focus(), []);
  const moveCursorToEnd = useCallback(
    () => editorRef.current?.moveCursorToEnd(),
    [],
  );
  const getValue = useCallback(() => composer.value, [composer.value]);
  useRegisterComposer({
    focus,
    moveCursorToEnd,
    getValue,
    setValue: composer.setValue,
  });

  return (
    <div className={className ?? 'min-w-0 flex-1'}>
      <MarkdownEditor
        ref={editorRef}
        value={composer.value}
        onChange={composer.setValue}
        mentions={stableMentions}
        slashCommands={slashCommands}
        placeholder={placeholder}
        minHeight={MIN_HEIGHT[size]}
        showToolbar={false}
        unstyled
        disabled={disabled}
        onSubmit={() => {
          if (composer.canSubmit) void composer.submit();
          return true;
        }}
      />
    </div>
  );
}
