'use client';

/**
 * `<SlashHighlightTextarea>` — drop-in replacement for the composer's
 * default plain `<Textarea>` that visually highlights a leading
 * `/verb` token while preserving the native textarea editing model.
 *
 * Technique: overlay-mirror.
 *
 * - A hidden `<div role="presentation">` is rendered behind the
 *   `<textarea>` with identical typography + padding. The leading
 *   `/verb` slice is wrapped in `<span class="slash-token-highlight">`;
 *   the rest of the buffer is plain text.
 * - The real `<textarea>` sits on top with `color: transparent` and a
 *   visible `caret-color` so the caret + selection still feel native.
 * - Scroll position is mirrored on `onScroll` so the highlight
 *   tracks long drafts.
 *
 * This module is mounted by `Composer.tsx` whenever the host wires
 * `composerSlots.slashCommands` and has not overridden `slots.Textarea`.
 * Hosts that swap in `ComposerRichTextarea` keep the slash menu but lose
 * the in-input highlight for now (TipTap node is a future iteration).
 */
import { useLayoutEffect, useRef } from 'react';

import { Textarea } from '@djangocfg/ui-core/components';
import { cn } from '@djangocfg/ui-core/lib';

import { extractSlashToken } from './state';
import type { ComposerTextareaProps } from '../types';

export interface SlashHighlightTextareaProps extends ComposerTextareaProps {
  /**
   * Visual classes applied to the real textarea — passed through from
   * the composer's per-size class table so the mirror can copy them.
   */
  textareaClassName?: string;
}

/**
 * Tailwind classes the textarea, mirror, and overlay share so their
 * box geometry stays pixel-aligned. Border / shadow / focus rings live
 * on the surface wrapper, not here.
 */
const SHARED_TYPO = cn(
  'block w-full resize-none whitespace-pre-wrap break-words',
  'font-sans',
);

export function SlashHighlightTextarea({
  composer,
  placeholder,
  disabled,
  size: _size,
  className,
  textareaClassName,
}: SlashHighlightTextareaProps) {
  const mirrorRef = useRef<HTMLDivElement>(null);
  const taRef = composer.textareaRef;

  // Keep the mirror's scroll synced with the textarea's. The mirror is
  // already `overflow: hidden` but on tall drafts the textarea scrolls
  // internally — we translate the highlight slice up by the same delta
  // so it visually tracks. Cheap; ~one ref read per scroll event.
  useLayoutEffect(() => {
    const ta = taRef.current;
    const mirror = mirrorRef.current;
    if (!ta || !mirror) return;
    const sync = () => {
      mirror.scrollTop = ta.scrollTop;
      mirror.scrollLeft = ta.scrollLeft;
    };
    ta.addEventListener('scroll', sync, { passive: true });
    // Also resync on value change (caller of useLayoutEffect rerun
    // covers this) so the initial frame is aligned too.
    sync();
    return () => ta.removeEventListener('scroll', sync);
  }, [taRef, composer.value]);

  const value = composer.value;
  const slice = extractSlashToken(value);
  const token = slice?.token ?? '';
  const rest = slice ? value.slice(slice.end) : value;

  return (
    <div className={cn('relative min-w-0 flex-1', className)}>
      {/* Mirror — sits *under* the textarea (z-0) and renders the same
          text with the slash token wrapped in a highlight span. */}
      {slice ? (
        <div
          ref={mirrorRef}
          aria-hidden
          className={cn(
            'pointer-events-none absolute inset-0 z-0 overflow-hidden',
            'text-foreground',
            SHARED_TYPO,
            textareaClassName,
            // Wipe out any focus-ring / border classes the textarea
            // chain might have carried in — the mirror is decoration.
            'border-0 bg-transparent shadow-none ring-0',
          )}
        >
          {/* No padding, no font-family change — the mirror must occupy
              exactly the same pixel width as the underlying textarea so
              the caret position lines up with the visible characters.
              Decoration via color + subtle radius only. */}
          <span
            className={cn(
              'rounded-sm',
              'bg-primary/15 text-primary',
            )}
          >
            {token}
          </span>
          {/* Use a zero-width space when `rest` is empty so the mirror
              keeps a baseline row — otherwise an empty `/` row collapses
              and the highlight box loses its line height. */}
          <span>{rest.length > 0 ? rest : '​'}</span>
        </div>
      ) : null}

      <Textarea
        {...composer.textareaProps}
        rows={1}
        placeholder={placeholder}
        aria-label={placeholder}
        aria-multiline="true"
        aria-keyshortcuts="Enter"
        disabled={disabled}
        className={cn(
          'relative z-10',
          // The textarea text itself is invisible; the caret stays
          // visible via `caret-color`. The mirror behind it paints
          // the actual glyphs.
          slice ? '!text-transparent caret-foreground' : 'text-foreground',
          'flex-1 resize-none border-0 bg-transparent shadow-none',
          'focus-visible:ring-0 focus-visible:outline-none',
          SHARED_TYPO,
          textareaClassName,
        )}
      />
    </div>
  );
}
