import { useRef, useEffect, useCallback, useState } from 'react';
import DOMPurify from 'dompurify';
import { useInputBarStore } from '../store/inputBar';
import type { UploadedFile } from '../types';
import styles from './PromptInput.module.css';

const placeholders: Record<string, string> = {
  universal: '上传参考图或视频，输入文字，通过 @ 引用素材',
  keyframe: '输入描述，定义首帧到尾帧的运动过程',
};

export function PromptInput() {
  const prompt = useInputBarStore((s) => s.prompt);
  const setPrompt = useInputBarStore((s) => s.setPrompt);
  const editorHtml = useInputBarStore((s) => s.editorHtml);
  const setEditorHtml = useInputBarStore((s) => s.setEditorHtml);
  const mode = useInputBarStore((s) => s.mode);
  const references = useInputBarStore((s) => s.references);
  const insertAtTrigger = useInputBarStore((s) => s.insertAtTrigger);

  const editorRef = useRef<HTMLDivElement>(null);
  const [showMentionPopup, setShowMentionPopup] = useState(false);
  const [mentionPos, setMentionPos] = useState({ top: 0, left: 0 });
  const typedAtRef = useRef(false); // tracks if popup was triggered by typing @
  const [highlightedIdx, setHighlightedIdx] = useState(0);
  const [hoverRef, setHoverRef] = useState<UploadedFile | null>(null);
  const [hoverPos, setHoverPos] = useState({ top: 0, left: 0 });

  // Auto-focus
  useEffect(() => {
    editorRef.current?.focus();
  }, []);

  // Sync editor when editorHtml changes (e.g. after submit or reEdit)
  useEffect(() => {
    const el = editorRef.current;
    if (!el) return;
    if (el.innerHTML !== editorHtml) {
      el.innerHTML = DOMPurify.sanitize(editorHtml, { ALLOWED_TAGS: ['span', 'br'], ALLOWED_ATTR: ['class', 'contenteditable', 'data-ref-id', 'data-ref-type'] });
      // If the HTML is plain text but we have references, rebuild mention spans
      // This handles the case where editorHtml comes from backend (plain text only)
      if (editorHtml && !editorHtml.includes('data-ref-id') && references.length > 0) {
        rebuildMentionSpans(el);
      }
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [editorHtml]);

  // Handle @ button from toolbar
  useEffect(() => {
    if (insertAtTrigger === 0) return;
    if (references.length === 0) return;
    typedAtRef.current = false;
    openMentionPopup();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [insertAtTrigger]);

  // Rebuild mention spans from plain text @label patterns
  const rebuildMentionSpans = useCallback((el: HTMLElement) => {
    const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
    const replacements: { node: Text; matches: { start: number; end: number; ref: UploadedFile }[] }[] = [];

    let textNode: Text | null;
    while ((textNode = walker.nextNode() as Text | null)) {
      const text = textNode.textContent || '';
      const matches: { start: number; end: number; ref: UploadedFile }[] = [];
      for (const ref of references) {
        const pattern = `@${ref.label}`;
        let idx = text.indexOf(pattern);
        while (idx !== -1) {
          matches.push({ start: idx, end: idx + pattern.length, ref });
          idx = text.indexOf(pattern, idx + pattern.length);
        }
      }
      if (matches.length > 0) {
        matches.sort((a, b) => a.start - b.start);
        replacements.push({ node: textNode, matches });
      }
    }

    for (const { node, matches } of replacements) {
      const text = node.textContent || '';
      const frag = document.createDocumentFragment();
      let lastIdx = 0;
      for (const m of matches) {
        if (m.start > lastIdx) {
          frag.appendChild(document.createTextNode(text.slice(lastIdx, m.start)));
        }
        const span = document.createElement('span');
        span.className = styles.mention;
        span.contentEditable = 'false';
        span.dataset.refId = m.ref.id;
        span.dataset.refType = m.ref.type;
        span.textContent = `@${m.ref.label}`;
        frag.appendChild(span);
        lastIdx = m.end;
      }
      if (lastIdx < text.length) {
        frag.appendChild(document.createTextNode(text.slice(lastIdx)));
      }
      node.parentNode?.replaceChild(frag, node);
    }

    if (replacements.length > 0) {
      setEditorHtml(el.innerHTML);
    }
  }, [references, setEditorHtml]);

  const openMentionPopup = useCallback(() => {
    const el = editorRef.current;
    if (!el) return;
    el.focus();

    const sel = window.getSelection();
    const editorRect = el.getBoundingClientRect();

    // Position popup above cursor line
    let top = 0; // fallback: top of editor
    let left = 0;

    if (sel && sel.rangeCount > 0) {
      const range = sel.getRangeAt(0);
      const rect = range.getBoundingClientRect();

      if (rect.height > 0) {
        top = rect.top - editorRect.top - 8;
        left = Math.max(0, rect.left - editorRect.left);
      } else {
        // Collapsed range returns zero rect — use temp marker
        const marker = document.createElement('span');
        marker.textContent = '\u200B';
        range.insertNode(marker);
        const mRect = marker.getBoundingClientRect();
        top = mRect.top - editorRect.top - 8;
        left = Math.max(0, mRect.left - editorRect.left);
        marker.remove();
        el.normalize();
      }
    }

    setMentionPos({ top, left });
    setHighlightedIdx(0);
    setShowMentionPopup(true);
  }, []);

  const extractText = useCallback(() => {
    const el = editorRef.current;
    if (!el) return;
    setPrompt(el.textContent || '');
    setEditorHtml(el.innerHTML);
  }, [setPrompt, setEditorHtml]);

  // Remove orphaned mention spans when a reference is deleted
  useEffect(() => {
    const el = editorRef.current;
    if (!el) return;
    const refIds = new Set(references.map((r) => r.id));
    const spans = el.querySelectorAll<HTMLElement>('[data-ref-id]');
    let changed = false;
    spans.forEach((span) => {
      if (!refIds.has(span.dataset.refId!)) {
        span.replaceWith('');
        changed = true;
      }
    });
    if (changed) {
      el.normalize();
      extractText();
    }
  }, [references, extractText]);

  const handleInput = useCallback(() => {
    extractText();

    // Detect if user just typed @
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0) return;
    const range = sel.getRangeAt(0);
    const node = range.startContainer;
    if (node.nodeType !== Node.TEXT_NODE) return;

    const text = node.textContent || '';
    const offset = range.startOffset;
    if (offset > 0 && text[offset - 1] === '@' && references.length > 0) {
      // Keep the @ visible, open popup above it
      typedAtRef.current = true;
      openMentionPopup();
    }
  }, [extractText, references.length, openMentionPopup]);

  const insertMention = useCallback((ref: UploadedFile) => {
    setShowMentionPopup(false);
    const el = editorRef.current;
    if (!el) return;

    el.focus();
    const sel = window.getSelection();
    if (!sel || sel.rangeCount === 0) return;

    const range = sel.getRangeAt(0);

    // If triggered by typing @, remove the @ character first
    if (typedAtRef.current) {
      typedAtRef.current = false;
      const node = range.startContainer;
      if (node.nodeType === Node.TEXT_NODE) {
        const text = node.textContent || '';
        const offset = range.startOffset;
        const atIdx = text.lastIndexOf('@', offset - 1);
        if (atIdx >= 0) {
          node.textContent = text.substring(0, atIdx) + text.substring(offset);
          range.setStart(node, atIdx);
          range.collapse(true);
        }
      }
    }

    range.deleteContents();

    // Create mention span
    const mention = document.createElement('span');
    mention.className = styles.mention;
    mention.contentEditable = 'false';
    mention.dataset.refId = ref.id;
    mention.dataset.refType = ref.type;
    mention.textContent = `@${ref.label}`;

    // Insert mention + trailing space
    range.insertNode(mention);

    const space = document.createTextNode('\u00A0');
    mention.after(space);

    // Move cursor after space
    const newRange = document.createRange();
    newRange.setStartAfter(space);
    newRange.collapse(true);
    sel.removeAllRanges();
    sel.addRange(newRange);

    extractText();
  }, [extractText]);

  const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
    if (showMentionPopup) {
      if (e.key === 'Escape') {
        e.preventDefault();
        setShowMentionPopup(false);
      } else if (e.key === 'ArrowDown') {
        e.preventDefault();
        setHighlightedIdx((prev) => (prev + 1) % references.length);
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        setHighlightedIdx((prev) => (prev - 1 + references.length) % references.length);
      } else if (e.key === 'Enter') {
        e.preventDefault();
        insertMention(references[highlightedIdx]);
      }
    }
  }, [showMentionPopup, references, highlightedIdx, insertMention]);

  const handlePaste = useCallback((e: React.ClipboardEvent) => {
    e.preventDefault();

    // Handle pasted image files (Ctrl+V screenshot / copied image)
    const items = e.clipboardData.items;
    const imageFiles: File[] = [];
    for (let i = 0; i < items.length; i++) {
      if (items[i].type.startsWith('image/')) {
        const file = items[i].getAsFile();
        if (file) imageFiles.push(file);
      }
    }
    if (imageFiles.length > 0) {
      useInputBarStore.getState().addReferences(imageFiles);
      return;
    }

    // Plain text paste — strip @label patterns to prevent duplicate mention tags
    let text = e.clipboardData.getData('text/plain');
    for (const ref of references) {
      const pattern = `@${ref.label}`;
      while (text.includes(pattern)) {
        text = text.replace(pattern, ref.label);
      }
    }
    document.execCommand('insertText', false, text);
    extractText();
  }, [extractText, references]);

  // Mention hover — delegated event
  const handleMouseOver = useCallback((e: React.MouseEvent) => {
    const target = (e.target as HTMLElement).closest('[data-ref-id]') as HTMLElement | null;
    if (!target) return;
    const refId = target.dataset.refId;
    const ref = references.find((r) => r.id === refId);
    if (!ref) return;

    const rect = target.getBoundingClientRect();
    const wrapperRect = editorRef.current!.parentElement!.getBoundingClientRect();
    setHoverRef(ref);
    setHoverPos({
      top: rect.top - wrapperRect.top - 8,
      left: rect.left - wrapperRect.left + rect.width / 2,
    });
  }, [references]);

  const handleMouseOut = useCallback((e: React.MouseEvent) => {
    const target = (e.target as HTMLElement).closest('[data-ref-id]');
    if (target) setHoverRef(null);
  }, []);

  // Close mention popup on click outside
  useEffect(() => {
    if (!showMentionPopup) return;
    const handler = (e: MouseEvent) => {
      const popup = document.querySelector(`.${styles.mentionPopup}`);
      if (popup && !popup.contains(e.target as Node)) {
        setShowMentionPopup(false);
      }
    };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [showMentionPopup]);

  const isEmpty = !prompt.trim() && !editorHtml;

  return (
    <div className={styles.wrapper}>
      {isEmpty && (
        <div className={styles.placeholder}>{placeholders[mode]}</div>
      )}
      <div
        ref={editorRef}
        className={styles.editor}
        contentEditable
        suppressContentEditableWarning
        onInput={handleInput}
        onKeyDown={handleKeyDown}
        onPaste={handlePaste}
        onMouseOver={handleMouseOver}
        onMouseOut={handleMouseOut}
      />

      {/* Mention popup */}
      {showMentionPopup && references.length > 0 && (
        <div
          className={styles.mentionPopup}
          style={{ top: mentionPos.top, left: mentionPos.left }}
        >
          <div className={styles.mentionHeader}>可能@的内容</div>
          {references.map((ref, idx) => (
            <button
              key={`${ref.id}-${idx}`}
              className={`${styles.mentionItem} ${idx === highlightedIdx ? styles.mentionItemActive : ''}`}
              onMouseDown={(e) => {
                e.preventDefault();
                insertMention(ref);
              }}
            >
              <div className={styles.mentionThumb}>
                {ref.type === 'video' ? (
                  <video src={ref.previewUrl} muted className={styles.thumbMedia} />
                ) : (
                  <img src={ref.previewUrl} alt="" className={styles.thumbMedia} />
                )}
              </div>
              <span className={styles.mentionLabel}>{ref.label}</span>
              <span className={styles.mentionType}>
                {ref.type === 'video' ? '视频' : '图片'}
              </span>
            </button>
          ))}
        </div>
      )}

      {/* Hover preview tooltip */}
      {hoverRef && (
        <div
          className={styles.previewTooltip}
          style={{ top: hoverPos.top, left: hoverPos.left }}
        >
          {hoverRef.type === 'video' ? (
            <video
              src={hoverRef.previewUrl}
              autoPlay
              loop
              muted
              playsInline
              className={styles.previewMedia}
            />
          ) : (
            <img
              src={hoverRef.previewUrl}
              alt={hoverRef.label}
              className={styles.previewMedia}
            />
          )}
          <div className={styles.previewLabel}>{hoverRef.label}</div>
        </div>
      )}
    </div>
  );
}
