'use client';

import { useCallback, useRef } from 'react';
import type { MouseEvent as ReactMouseEvent, RefObject } from 'react';

import { useChatContextOptional, type ComposerHandle } from '../context';
import type { Focusable } from './useAutoFocusOnStreamEnd';

export interface UseFocusOnEmptyClickOptions {
  /**
   * Custom focus target. Defaults to the composer registered in the chat
   * context.
   */
  targetRef?: RefObject<Focusable | HTMLElement | null>;
  /** Opt-out without unmounting the hook. @default true */
  enabled?: boolean;
  /**
   * Don't focus if currently streaming (user is reading the reply).
   * @default true
   */
  skipWhileStreaming?: boolean;
}

/**
 * "Click anywhere in the chat → focus the composer" — Slack / Linear /
 * ChatGPT behaviour.
 *
 * Returns a single `onMouseUp` handler to attach to the scrollable
 * messages container. Heuristics that mirror the popular chat apps:
 *
 *   1. Ignore clicks on interactive elements (`button`, `a`, `input`,
 *      `textarea`, `[role="button"]`, `[contenteditable]`, …) — they
 *      have their own behaviour.
 *   2. Ignore clicks that produced a text selection (drag-to-select).
 *      Stealing focus would break copy-paste flow.
 *   3. Ignore touch input — on mobile the system focuses the textarea
 *      via tap on the composer itself; touching messages should never
 *      open the keyboard.
 *   4. Optionally skip while the assistant is streaming so the user
 *      can keep reading without the keyboard popping up.
 *
 * @example
 * ```tsx
 * const onMouseUp = useFocusOnEmptyClick();
 * <div ref={scrollRef} onMouseUp={onMouseUp}>{messages}</div>
 * ```
 */
export function useFocusOnEmptyClick(
  options: UseFocusOnEmptyClickOptions = {},
): (event: ReactMouseEvent<HTMLElement>) => void {
  const { targetRef, enabled = true, skipWhileStreaming = true } = options;
  const ctx = useChatContextOptional();

  const composerHandleRef = useRef<ComposerHandle | null>(null);
  composerHandleRef.current = ctx?.composer ?? null;

  const isStreamingRef = useRef(false);
  isStreamingRef.current = ctx?.isStreaming ?? false;

  return useCallback(
    (event: ReactMouseEvent<HTMLElement>) => {
      if (!enabled) return;
      if (skipWhileStreaming && isStreamingRef.current) return;

      // Touch / pen → never steal focus (mobile keyboard pop is hostile).
      const pointerType = (event.nativeEvent as PointerEvent).pointerType;
      if (pointerType && pointerType !== 'mouse') return;

      // Drag-selected text → don't steal focus.
      const selection = typeof window !== 'undefined' ? window.getSelection?.() : null;
      if (selection && !selection.isCollapsed && selection.toString().length > 0) {
        return;
      }

      // Click landed on something interactive → it handles itself.
      const target = event.target as HTMLElement | null;
      if (!target) return;
      if (isInteractive(target)) return;

      // Refocus.
      const explicit = targetRef?.current as Focusable | HTMLElement | null | undefined;
      if (explicit) {
        explicit.focus?.();
        return;
      }
      composerHandleRef.current?.focus?.();
    },
    [enabled, skipWhileStreaming, targetRef],
  );
}

const INTERACTIVE_SELECTORS = [
  'a[href]',
  'button',
  'input',
  'textarea',
  'select',
  'label',
  'summary',
  '[role="button"]',
  '[role="link"]',
  '[role="menuitem"]',
  '[role="tab"]',
  '[contenteditable]:not([contenteditable="false"])',
  '[data-no-autofocus]',
].join(',');

function isInteractive(el: HTMLElement | null): boolean {
  if (!el) return false;
  return !!el.closest(INTERACTIVE_SELECTORS);
}
