/**
 * @license
 * SPDX-License-Identifier: Apache-2.0
 */

import React, { useRef, useEffect } from 'react';
import { FuriganaItem, WritingLine, SidebarNote } from '../types';
import { Trash2, Plus, ArrowUp, ArrowDown } from 'lucide-react';
import { FuriganaLayer, FuriganaLayerHandle } from './FuriganaLayer';

interface LineItemProps {
  line: WritingLine;
  lineNumber: number;
  showLineNumber: boolean;
  isActive: boolean;
  fontStyle: 'sans' | 'serif';
  fontSize: number;
  lineSpacing: number;
  readOnly?: boolean;
  canEdit: boolean;
  onUpdate: (lineId: string, newRawText: string, newFuriganaItems?: FuriganaItem[]) => void;
  onDelete: (lineId: string) => void;
  onActivate: (lineId: string) => void;
  onDeactivate: () => void;
  onDoubleClick?: (lineId: string) => void;
  onInsertBelow: (lineId: string) => void;
  onInsertAbove: (lineId: string) => void;
  onTextSelect: (selectedText: string, lineId: string) => void;
  selectedFuriganaItemId?: string | null;
  onFuriganaSelect: (lineId: string, itemId: string) => void;
  onNoteClick: (note: SidebarNote) => void;
  moveFocusUp: (lineId: string, focusField: 'main' | 'furigana') => void;
  moveFocusDown: (lineId: string, focusField: 'main' | 'furigana') => void;
}

export const LineItem: React.FC<LineItemProps> = ({
  line,
  lineNumber,
  showLineNumber,
  isActive,
  fontStyle,
  fontSize,
  lineSpacing,
  readOnly = false,
  canEdit,
  onUpdate,
  onDelete,
  onActivate,
  onDeactivate,
  onDoubleClick,
  onInsertBelow,
  onInsertAbove,
  onTextSelect,
  selectedFuriganaItemId,
  onFuriganaSelect,
  onNoteClick,
  moveFocusUp,
  moveFocusDown,
}) => {
  const furiganaLayerRef = useRef<FuriganaLayerHandle>(null);
  const mainInputRef = useRef<HTMLInputElement>(null);
  const displayRef = useRef<HTMLDivElement>(null);
  const mainTextRef = useRef<HTMLDivElement>(null);
  const mobileSelectionRef = useRef<{
    pointerId: number;
    startX: number;
    startY: number;
    currentX: number;
    currentY: number;
    selecting: boolean;
    startPoint: TextHitPoint | null;
  } | null>(null);

  const furiganaItems = line.furiganaItems;

  type TextHitPoint = {
    node: Text;
    offset: number;
    globalOffset: number;
  };

  // Expose focus methods dynamically
  useEffect(() => {
    const handleCustomFocus = (e: CustomEvent<{ field: 'main' | 'furigana' }>) => {
      if (e.detail.field === 'furigana') {
        furiganaLayerRef.current?.focusFirstItem();
      } else {
        mainInputRef.current?.focus({ preventScroll: true });
      }
    };

    const el = document.getElementById(`line-${line.id}`);
    if (el) {
      el.addEventListener('focus-field' as any, handleCustomFocus as any);
    }
    return () => {
      if (el) {
        el.removeEventListener('focus-field' as any, handleCustomFocus as any);
      }
    };
  }, [furiganaItems, line.id]);

  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, isFurigana: boolean) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      if (isFurigana) {
        e.currentTarget.blur();
      } else {
        onInsertBelow(line.id);
      }
    } else if (e.key === 'Backspace') {
      // If both inputs are completely empty, delete this line
      if (!line.rawText && furiganaItems.length === 0) {
        e.preventDefault();
        onDelete(line.id);
      }
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      if (isFurigana) {
        // Go to previous line's main input
        moveFocusUp(line.id, 'main');
      } else {
        // Go up to the furigana input of the same line
        furiganaLayerRef.current?.focusFirstItem();
      }
    } else if (e.key === 'ArrowDown') {
      e.preventDefault();
      if (isFurigana) {
        // Go down to the main input of the same line
        mainInputRef.current?.focus({ preventScroll: true });
      } else {
        // Go to next line's furigana input
        moveFocusDown(line.id, 'furigana');
      }
    }
  };

  const updateFuriganaItems = (nextItems: FuriganaItem[]) => {
    onUpdate(line.id, line.rawText, nextItems);
  };

  const getCaretRangeFromPoint = (clientX: number, clientY: number) => {
    const doc = document as Document & {
      caretRangeFromPoint?: (x: number, y: number) => Range | null;
      caretPositionFromPoint?: (x: number, y: number) => { offsetNode: Node; offset: number } | null;
    };

    if (doc.caretRangeFromPoint) {
      return doc.caretRangeFromPoint(clientX, clientY);
    }

    const position = doc.caretPositionFromPoint?.(clientX, clientY);
    if (!position) return null;

    const range = document.createRange();
    range.setStart(position.offsetNode, position.offset);
    range.collapse(true);
    return range;
  };

  const getTextHitPoint = (clientX: number, clientY: number): TextHitPoint | null => {
    const root = mainTextRef.current;
    const range = getCaretRangeFromPoint(clientX, clientY);
    if (!root || !range || !root.contains(range.startContainer)) return null;

    if (range.startContainer.nodeType !== Node.TEXT_NODE) return null;

    const targetNode = range.startContainer as Text;
    const treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
    let globalOffset = 0;
    let current = treeWalker.nextNode() as Text | null;

    while (current) {
      if (current === targetNode) {
        return {
          node: targetNode,
          offset: Math.min(range.startOffset, targetNode.data.length),
          globalOffset: globalOffset + Math.min(range.startOffset, targetNode.data.length),
        };
      }

      globalOffset += current.data.length;
      current = treeWalker.nextNode() as Text | null;
    }

    return null;
  };

  const setVisualSelection = (startPoint: TextHitPoint, endPoint: TextHitPoint) => {
    const range = document.createRange();
    if (startPoint.globalOffset <= endPoint.globalOffset) {
      range.setStart(startPoint.node, startPoint.offset);
      range.setEnd(endPoint.node, endPoint.offset);
    } else {
      range.setStart(endPoint.node, endPoint.offset);
      range.setEnd(startPoint.node, startPoint.offset);
    }

    const selection = window.getSelection();
    selection?.removeAllRanges();
    selection?.addRange(range);
    return selection?.toString().trim() || '';
  };

  const handleMobileSelectionStart = (e: React.PointerEvent<HTMLDivElement>) => {
    if (canEdit || e.pointerType !== 'touch' || !line.rawText) return;
    const target = e.target as HTMLElement;
    if (target.closest('[data-note-highlight="true"]') || target.closest('[data-furigana-layer="true"]')) return;

    const startPoint = getTextHitPoint(e.clientX, e.clientY);
    mobileSelectionRef.current = {
      pointerId: e.pointerId,
      startX: e.clientX,
      startY: e.clientY,
      currentX: e.clientX,
      currentY: e.clientY,
      selecting: false,
      startPoint,
    };
  };

  const handleMobileSelectionMove = (e: React.PointerEvent<HTMLDivElement>) => {
    const gesture = mobileSelectionRef.current;
    if (!gesture || gesture.pointerId !== e.pointerId) return;

    gesture.currentX = e.clientX;
    gesture.currentY = e.clientY;
    const dx = Math.abs(e.clientX - gesture.startX);
    const dy = Math.abs(e.clientY - gesture.startY);

    if (!gesture.selecting && dx > 10 && dx > dy * 1.15) {
      gesture.selecting = true;
    }

    if (gesture.selecting) {
      e.preventDefault();
      e.stopPropagation();
      const endPoint = getTextHitPoint(e.clientX, e.clientY);
      if (gesture.startPoint && endPoint) {
        setVisualSelection(gesture.startPoint, endPoint);
      }
    }
  };

  const handleMobileSelectionEnd = (e: React.PointerEvent<HTMLDivElement>) => {
    const gesture = mobileSelectionRef.current;
    if (!gesture || gesture.pointerId !== e.pointerId) return;
    mobileSelectionRef.current = null;

    if (!gesture.selecting) return;

    e.preventDefault();
    e.stopPropagation();

    const endPoint = getTextHitPoint(gesture.currentX, gesture.currentY);
    const selectedText =
      gesture.startPoint && endPoint
        ? setVisualSelection(gesture.startPoint, endPoint)
        : window.getSelection()?.toString().trim() || '';

    if (selectedText) {
      onTextSelect(selectedText, line.id);
    }
  };

  // Capture selection in reading mode to add annotations
  const handleSelection = () => {
    const selection = window.getSelection();
    if (!selection || selection.isCollapsed) return;

    const isNodeInsideFurigana = (node: Node | null) => {
      const element = node instanceof Element ? node : node?.parentElement;
      return !!element?.closest('[data-furigana-layer="true"]');
    };

    const selectedText = selection.toString().trim();
    if (selectedText.length > 0) {
      const lowerSelected = selectedText.toLowerCase();
      const mainTextLower = (line.rawText || '').toLowerCase();
      const isInside = displayRef.current && (
        displayRef.current.contains(selection.anchorNode) || 
        displayRef.current.contains(selection.focusNode)
      );

      const isInsideFurigana = isNodeInsideFurigana(selection.anchorNode) || isNodeInsideFurigana(selection.focusNode);

      if (!isInsideFurigana && (isInside || mainTextLower.includes(lowerSelected))) {
        onTextSelect(selectedText, line.id);
      }
    }
  };

  // Renders text highlighted with custom note overlays
  const renderTextWithNotes = (text: string, notes: SidebarNote[]) => {
    if (!text || notes.length === 0) return text;

    const activeNotes = notes.filter((n) => text.includes(n.text));
    if (activeNotes.length === 0) return text;

    const splitText = (str: string, availableNotes: SidebarNote[]): React.ReactNode[] => {
      if (!str) return [];
      if (availableNotes.length === 0) return [str];

      let earliestNote: SidebarNote | null = null;
      let earliestIndex = Infinity;

      for (const note of availableNotes) {
        const idx = str.indexOf(note.text);
        if (idx !== -1 && idx < earliestIndex) {
          earliestIndex = idx;
          earliestNote = note;
        }
      }

      if (!earliestNote || earliestIndex === Infinity) {
        return [str];
      }

      const matchText = earliestNote.text;
      const before = str.slice(0, earliestIndex);
      const after = str.slice(earliestIndex + matchText.length);

      const parts: React.ReactNode[] = [];
      if (before) {
        parts.push(...splitText(before, availableNotes));
      }

      let colorClass = 'bg-[#FAF9F6] border-[#8B7E74] text-[#2D2926] hover:bg-[#E8E4DE]/50';
      if (earliestNote.color === 'rose') {
        colorClass = 'bg-rose-100/70 border-rose-300 text-rose-950 hover:bg-rose-200/80';
      } else if (earliestNote.color === 'sky') {
        colorClass = 'bg-sky-100/70 border-sky-300 text-sky-950 hover:bg-sky-200/80';
      } else if (earliestNote.color === 'emerald') {
        colorClass = 'bg-emerald-100/70 border-emerald-300 text-emerald-950 hover:bg-emerald-200/80';
      } else if (earliestNote.color === 'violet') {
        colorClass = 'bg-purple-100/70 border-purple-300 text-purple-950 hover:bg-purple-200/80';
      }

      parts.push(
        <span
          key={`highlight-${earliestNote.id}-${earliestIndex}`}
          data-note-highlight="true"
          onPointerUp={(e) => {
            if (e.pointerType !== 'touch') return;
            e.preventDefault();
            e.stopPropagation();
            onNoteClick(earliestNote!);
          }}
          onClick={(e) => {
            e.stopPropagation();
            onNoteClick(earliestNote!);
          }}
          className={`${colorClass} px-1 rounded border-b-2 font-medium cursor-pointer transition-all duration-200 inline-block active:scale-95`}
          title={`点击查看注释: ${earliestNote.comment}`}
        >
          {matchText}
        </span>
      );

      if (after) {
        parts.push(...splitText(after, availableNotes));
      }

      return parts;
    };

    return splitText(text, activeNotes);
  };

  const calculatedLineHeight = fontSize * lineSpacing;
  const isSerif = fontStyle === 'serif';

  return (
    <div
      id={`line-${line.id}`}
      className={`relative group flex w-full min-w-0 gap-1 pr-1 transition-colors ${isActive ? 'bg-[#FAF9F6]/40' : ''} hover:bg-[#FAF9F6]/40 md:min-w-[900px] md:gap-4 md:pr-3 xl:min-w-[1200px]`}
      style={{ minHeight: `${calculatedLineHeight}px` }}
    >
      {/* 1. Left gutter with number */}
      {showLineNumber && (
        <div className="relative flex w-8 shrink-0 flex-col items-center justify-start py-1 select-none text-[#A69F92]/60 border-r border-[#E8E4DE]/50 font-mono sm:w-10 md:w-12">
          <span className="text-[11px] font-bold select-none opacity-60">
            {lineNumber.toString().padStart(2, '0')}
          </span>
        </div>
      )}

      {/* 2. Interactive sheet writing lines (Direct live editing) */}
      <div className="min-w-0 flex-1 py-1 flex flex-col justify-center">
        {!readOnly ? (
          /* ================= EDIT MODE: FREE-POSITIONED FURIGANA + MAIN TEXT ================= */
          <div className="flex flex-col w-full min-w-0 overflow-hidden px-0.5 pr-0.5 font-mono md:-mx-1 md:px-1 md:pr-1 md:overflow-visible">
            {/* Top Row: Free-positioned Furigana / 假名 layer */}
            <FuriganaLayer
              ref={furiganaLayerRef}
              items={furiganaItems}
              editable
              selectedItemId={selectedFuriganaItemId}
              fontSize={fontSize}
              fontFamily={isSerif ? 'var(--font-serif), monospace' : 'var(--font-sans), monospace'}
              onChange={updateFuriganaItems}
              onItemSelect={(itemId) => onFuriganaSelect(line.id, itemId)}
              onFocusMain={() => mainInputRef.current?.focus({ preventScroll: true })}
              onFocusPreviousLine={() => moveFocusUp(line.id, 'main')}
            />

            {/* Bottom Row: Main Kanji / 正文 input line */}
            <input
              ref={mainInputRef}
              type="text"
              value={line.rawText || ''}
              onChange={(e) => onUpdate(line.id, e.target.value)}
              onKeyDown={(e) => handleKeyDown(e, false)}
              onSelect={(e) => {
                const target = e.currentTarget;
                const start = target.selectionStart;
                const end = target.selectionEnd;
                if (start !== null && end !== null && start !== end) {
                  const selectedText = target.value.substring(start, end).trim();
                  if (selectedText.length > 0) {
                    onTextSelect(selectedText, line.id);
                  }
                }
              }}
              placeholder=""
              className="w-full bg-transparent border-none outline-none focus:outline-none focus:ring-0 m-0 p-0 font-normal text-[#2D2926] placeholder-neutral-300 font-mono"
              style={{
                fontSize: `${fontSize}px`,
                fontFamily: isSerif ? 'var(--font-serif), monospace' : 'var(--font-sans), monospace',
                marginTop: '1px',
                height: `${fontSize * 1.2}px`,
                lineHeight: '1.2',
              }}
            />
          </div>
        ) : (
          /* ================= READ MODE: ALIGNED STATIC TEXT DISPLAY ================= */
          <div
            ref={displayRef}
            onMouseUp={handleSelection}
            onPointerDown={handleMobileSelectionStart}
            onPointerMove={handleMobileSelectionMove}
            onPointerUp={handleMobileSelectionEnd}
            onPointerCancel={() => {
              mobileSelectionRef.current = null;
            }}
            onClick={(e) => {
              if ((e.target as HTMLElement).closest('[data-furigana-layer="true"]')) return;
              if (!canEdit) return;
              const selection = window.getSelection();
              if (selection && !selection.isCollapsed) return;
              onActivate(line.id);
            }}
            onDoubleClick={(e) => {
              if (canEdit && onDoubleClick) {
                onDoubleClick(line.id);
              }
            }}
            className={`flex flex-col w-full min-w-0 select-text overflow-hidden pr-0.5 font-mono hover:bg-[#FAF9F6]/80 rounded px-0.5 md:-mx-1 md:px-1 md:pr-1 md:overflow-visible ${
              canEdit ? 'cursor-text' : 'cursor-default'
            }`}
          >
            {/* Furigana line */}
            <FuriganaLayer
              items={furiganaItems}
              editable={canEdit}
              selectedItemId={selectedFuriganaItemId}
              fontSize={fontSize}
              fontFamily={isSerif ? 'var(--font-serif), monospace' : 'var(--font-sans), monospace'}
              onChange={updateFuriganaItems}
              onItemSelect={canEdit ? (itemId) => onFuriganaSelect(line.id, itemId) : undefined}
            />

            {/* Main Kanji text line with word highlighter/comment select matches */}
            <div
              ref={mainTextRef}
              className="text-[#2D2926] m-0 p-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]"
              style={{
                fontSize: `${fontSize}px`,
                fontFamily: isSerif ? 'var(--font-serif), monospace' : 'var(--font-sans), monospace',
                marginTop: '1px',
                minHeight: `${fontSize * 1.2}px`,
                lineHeight: '1.2',
                touchAction: canEdit ? 'auto' : 'pan-y',
              }}
            >
              {line.rawText ? (
                renderTextWithNotes(line.rawText, line.notes)
              ) : (
                '\u00A0'
              )}
            </div>
          </div>
        )}
      </div>
    </div>
  );
};
