/**
 * Rich Text Field Component
 *
 * TipTap-based rich text editor with formatting toolbar and variable support.
 * Serializes to HTML with embedded variable spans.
 */

import { useEffect, useCallback, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useEditor, EditorContent } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import Underline from '@tiptap/extension-underline';
import Link from '@tiptap/extension-link';
import Placeholder from '@tiptap/extension-placeholder';
import {
  Bold,
  Italic,
  Underline as UnderlineIcon,
  Strikethrough,
  Heading2,
  Heading3,
  List,
  ListOrdered,
  Link as LinkIcon,
  Zap,
} from 'lucide-react';
import { Label } from '../ui/label';
import { VariableNode } from '../tiptap/VariableNode';
import { SuggestionDropdown } from '../tiptap/SuggestionDropdown';
import { AvailableContext, ContextVariable } from '../../types/workflow-context';
import { ActionField } from '../../types/action';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip';
import '../tiptap/richtext.css';

interface RichTextFieldProps {
  fieldName: string;
  field: ActionField;
  value: string;
  onChange: (value: string) => void;
  availableContext: AvailableContext;
  error?: string;
}

interface ToolbarButtonProps {
  onClick: () => void;
  isActive?: boolean;
  disabled?: boolean;
  title: string;
  dataVariableIcon?: boolean;
  children: React.ReactNode;
}

function ToolbarButton({ onClick, isActive, disabled, title, dataVariableIcon, children }: ToolbarButtonProps) {
  return (
    <TooltipProvider delayDuration={300}>
      <Tooltip>
        <TooltipTrigger asChild>
          <button
            type="button"
            onClick={onClick}
            disabled={disabled}
            {...(dataVariableIcon ? { 'data-variable-icon': '' } : {})}
            className={`p-1.5 rounded transition-colors duration-150
              ${isActive ? 'bg-cyan-100 text-cyan-700' : 'text-slate-500 hover:text-slate-700 hover:bg-slate-100'}
              ${disabled ? 'opacity-40 cursor-not-allowed' : ''}`}
          >
            {children}
          </button>
        </TooltipTrigger>
        <TooltipContent side="bottom" className="text-xs">
          {title}
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

export function RichTextField({
  fieldName,
  field,
  value,
  onChange,
  availableContext,
  error,
}: RichTextFieldProps) {
  const isExternalUpdate = useRef(false);
  const containerRef = useRef<HTMLDivElement>(null);

  const [suggestionState, setSuggestionState] = useState<{
    isOpen: boolean;
    position: { top: number; left: number } | null;
    query: string;
    triggerPos: number | null;
  }>({
    isOpen: false,
    position: null,
    query: '',
    triggerPos: null,
  });

  const initialContent = useMemo(() => {
    // For richtext, the stored value is already HTML — set it directly
    return value || '';
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // Only compute on mount

  const editor = useEditor({
    extensions: [
      StarterKit.configure({
        heading: { levels: [2, 3] },
      }),
      Underline,
      Link.configure({
        openOnClick: false,
        HTMLAttributes: { class: 'richtext-link' },
      }),
      Placeholder.configure({
        placeholder: field.placeholder || field.description || 'Enter content...',
        emptyEditorClass: 'is-editor-empty',
      }),
      VariableNode,
    ],
    content: initialContent,
    editorProps: {
      attributes: {
        class: 'outline-none min-h-[120px] px-3 py-2',
      },
    },
    onUpdate: ({ editor }) => {
      if (isExternalUpdate.current) return;
      onChange(editor.getHTML());
    },
  });

  // Update editor content when external value changes
  useEffect(() => {
    if (!editor) return;

    const currentHTML = editor.getHTML();
    if (currentHTML !== value) {
      isExternalUpdate.current = true;
      editor.commands.setContent(value || '', { emitUpdate: false });
      isExternalUpdate.current = false;
    }
  }, [value, editor]);

  // Get cursor coordinates from editor
  const getCursorCoords = useCallback(() => {
    if (!editor) return null;

    const { from } = editor.state.selection;
    const coords = editor.view.coordsAtPos(from);

    return {
      top: coords.bottom + 4,
      left: coords.left,
    };
  }, [editor]);

  // Open suggestion via icon click
  const openSuggestionFromIcon = useCallback(() => {
    if (!editor) return;

    editor.chain().focus().run();

    const iconButton = containerRef.current?.querySelector('[data-variable-icon]');
    if (iconButton) {
      const rect = iconButton.getBoundingClientRect();
      setSuggestionState({
        isOpen: true,
        position: { top: rect.bottom + 4, left: rect.left - 280 },
        query: '',
        triggerPos: null,
      });
    }
  }, [editor]);

  const closeSuggestion = useCallback(() => {
    setSuggestionState({
      isOpen: false,
      position: null,
      query: '',
      triggerPos: null,
    });
  }, []);

  const handleVariableSelect = useCallback(
    (variable: ContextVariable) => {
      if (!editor) return;

      if (suggestionState.triggerPos !== null) {
        const { from } = editor.state.selection;
        editor
          .chain()
          .focus()
          .deleteRange({ from: suggestionState.triggerPos - 1, to: from })
          .insertVariable({
            path: variable.path,
            label: variable.label,
            type: variable.type,
          })
          .run();
      } else {
        editor
          .chain()
          .focus()
          .insertVariable({
            path: variable.path,
            label: variable.label,
            type: variable.type,
          })
          .run();
      }

      closeSuggestion();
    },
    [editor, suggestionState.triggerPos, closeSuggestion],
  );

  // Listen for @ keypress to open suggestion
  useEffect(() => {
    if (!editor) return;

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === '@' && !suggestionState.isOpen) {
        setTimeout(() => {
          const position = getCursorCoords();
          const { from } = editor.state.selection;

          setSuggestionState({
            isOpen: true,
            position,
            query: '',
            triggerPos: from,
          });
        }, 0);
      }
    };

    const editorElement = editor.view.dom;
    editorElement.addEventListener('keydown', handleKeyDown);

    return () => {
      editorElement.removeEventListener('keydown', handleKeyDown);
    };
  }, [editor, suggestionState.isOpen, getCursorCoords]);

  // Track query text after @ trigger
  useEffect(() => {
    if (!editor || !suggestionState.isOpen || suggestionState.triggerPos === null) return;

    const handleUpdate = () => {
      const { from } = editor.state.selection;
      const triggerPos = suggestionState.triggerPos!;

      if (from < triggerPos) {
        closeSuggestion();
        return;
      }

      const doc = editor.state.doc;
      const textAfterTrigger = doc.textBetween(triggerPos, from, ' ');
      const textIncludingTrigger = doc.textBetween(Math.max(0, triggerPos - 1), from, ' ');

      if (!textIncludingTrigger.startsWith('@')) {
        closeSuggestion();
        return;
      }

      if (textAfterTrigger.includes(' ')) {
        closeSuggestion();
        return;
      }

      setSuggestionState((prev) => ({
        ...prev,
        query: textAfterTrigger,
      }));
    };

    editor.on('update', handleUpdate);
    editor.on('selectionUpdate', handleUpdate);

    return () => {
      editor.off('update', handleUpdate);
      editor.off('selectionUpdate', handleUpdate);
    };
  }, [editor, suggestionState.isOpen, suggestionState.triggerPos, closeSuggestion]);

  const handleLinkToggle = useCallback(() => {
    if (!editor) return;

    if (editor.isActive('link')) {
      editor.chain().focus().unsetLink().run();
      return;
    }

    const url = window.prompt('Enter URL:');
    if (url) {
      editor.chain().focus().setLink({ href: url }).run();
    }
  }, [editor]);

  const hasVariables = availableContext.groups.length > 0;

  return (
    <div className="space-y-2" ref={containerRef}>
      <Label htmlFor={fieldName} className="text-[13px] font-medium text-slate-700 tracking-tight">
        {field.label}
        {field.required && <span className="text-rose-500 ml-0.5">*</span>}
      </Label>

      <div className="richtext-editor rounded-lg border border-slate-200 bg-white overflow-hidden focus-within:border-cyan-500 focus-within:ring-2 focus-within:ring-cyan-500/20 transition-colors duration-150">
        {/* Formatting toolbar */}
        <div className="flex items-center gap-0.5 px-2 py-1.5 border-b border-slate-100 bg-slate-50/50">
          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleBold().run()}
            isActive={editor?.isActive('bold')}
            disabled={!editor}
            title="Bold"
          >
            <Bold className="w-4 h-4" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleItalic().run()}
            isActive={editor?.isActive('italic')}
            disabled={!editor}
            title="Italic"
          >
            <Italic className="w-4 h-4" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleUnderline().run()}
            isActive={editor?.isActive('underline')}
            disabled={!editor}
            title="Underline"
          >
            <UnderlineIcon className="w-4 h-4" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleStrike().run()}
            isActive={editor?.isActive('strike')}
            disabled={!editor}
            title="Strikethrough"
          >
            <Strikethrough className="w-4 h-4" />
          </ToolbarButton>

          <div className="w-px h-5 bg-slate-200 mx-1" />

          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleHeading({ level: 2 }).run()}
            isActive={editor?.isActive('heading', { level: 2 })}
            disabled={!editor}
            title="Heading 2"
          >
            <Heading2 className="w-4 h-4" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleHeading({ level: 3 }).run()}
            isActive={editor?.isActive('heading', { level: 3 })}
            disabled={!editor}
            title="Heading 3"
          >
            <Heading3 className="w-4 h-4" />
          </ToolbarButton>

          <div className="w-px h-5 bg-slate-200 mx-1" />

          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleBulletList().run()}
            isActive={editor?.isActive('bulletList')}
            disabled={!editor}
            title="Bullet list"
          >
            <List className="w-4 h-4" />
          </ToolbarButton>
          <ToolbarButton
            onClick={() => editor?.chain().focus().toggleOrderedList().run()}
            isActive={editor?.isActive('orderedList')}
            disabled={!editor}
            title="Numbered list"
          >
            <ListOrdered className="w-4 h-4" />
          </ToolbarButton>

          <div className="w-px h-5 bg-slate-200 mx-1" />

          <ToolbarButton
            onClick={handleLinkToggle}
            isActive={editor?.isActive('link')}
            disabled={!editor}
            title="Link"
          >
            <LinkIcon className="w-4 h-4" />
          </ToolbarButton>

          <div className="flex-1" />

          <ToolbarButton
            onClick={openSuggestionFromIcon}
            disabled={!editor || !hasVariables}
            title={hasVariables ? 'Insert variable (or type @)' : 'No variables available'}
            dataVariableIcon
          >
            <Zap className="w-4 h-4" />
          </ToolbarButton>
        </div>

        {/* Editor content */}
        <EditorContent editor={editor} />
      </div>

      {error && (
        <div className="flex items-start gap-2 rounded-lg bg-rose-50 border border-rose-100 px-3 py-2.5">
          <span className="text-[13px] text-rose-700 leading-snug">{error}</span>
        </div>
      )}

      {/* Suggestion dropdown portal */}
      {suggestionState.isOpen &&
        suggestionState.position &&
        createPortal(
          <SuggestionDropdown
            availableContext={availableContext}
            onSelect={handleVariableSelect}
            onClose={closeSuggestion}
            position={suggestionState.position}
            initialQuery={suggestionState.query}
          />,
          document.body,
        )}
    </div>
  );
}
