/**
 * Lexical Image Node
 *
 * Custom DecoratorNode for inserting and displaying images in Lexical editors.
 * Supports alignment (float left, center, float right, block) for text wrapping
 * on rendered dealer pages.
 */

'use client';

import { useCallback, useEffect, useRef, useState, type ReactElement, type RefObject } from 'react';
import {
  $getNodeByKey,
  $getRoot,
  $getSelection,
  createCommand,
  DecoratorNode,
  DOMConversionMap,
  DOMConversionOutput,
  DOMExportOutput,
  LexicalCommand,
  LexicalEditor,
  LexicalNode,
  NodeKey,
  SerializedLexicalNode,
  Spread,
  COMMAND_PRIORITY_EDITOR,
} from 'lexical';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $insertNodeToNearestRoot } from '@lexical/utils';
import MediaLibraryModal from '@/app/components/MediaLibraryModal';
import type { ImageSelection } from '@/types/media';
import { usePopoverPosition } from '@/hooks/usePopoverPosition';

export type ImageAlignment = 'none' | 'left' | 'right' | 'center';

export interface ImagePayload {
  src: string;
  altText: string;
  width?: number;
  height?: number;
  alignment?: ImageAlignment;
  key?: NodeKey;
}

export type SerializedImageNode = Spread<
  {
    src: string;
    altText: string;
    width: number | undefined;
    height: number | undefined;
    alignment: ImageAlignment;
  },
  SerializedLexicalNode
>;

export const INSERT_IMAGE_COMMAND: LexicalCommand<ImagePayload> =
  createCommand('INSERT_IMAGE_COMMAND');

// --- ImageComponent (rendered inside the editor) ---

function ImageComponent({
  src,
  altText,
  width,
  height,
  alignment,
  nodeKey,
}: {
  src: string;
  altText: string;
  width?: number;
  height?: number;
  alignment: ImageAlignment;
  nodeKey: NodeKey;
}) {
  const [editor] = useLexicalComposerContext();
  const [isSelected, setIsSelected] = useState(false);
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handleClickOutside = (e: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
        setIsSelected(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, []);

  const handleClick = useCallback(() => {
    setIsSelected(true);
  }, []);

  const handleAlignment = useCallback(
    (newAlignment: ImageAlignment) => {
      editor.update(() => {
        const node = $getNodeByKey(nodeKey);
        if (node && $isImageNode(node)) {
          node.setAlignment(newAlignment);
        }
      });
    },
    [editor, nodeKey]
  );

  const handleDelete = useCallback(() => {
    editor.update(() => {
      const node = $getNodeByKey(nodeKey);
      if (node) {
        node.remove();
      }
    });
  }, [editor, nodeKey]);

  const containerStyle: React.CSSProperties = {
    position: 'relative',
    cursor: 'pointer',
    padding: '4px 0',
    display: 'flex',
  };

  if (alignment === 'center') {
    containerStyle.justifyContent = 'center';
  } else if (alignment === 'right') {
    containerStyle.justifyContent = 'flex-end';
  } else {
    containerStyle.justifyContent = 'flex-start';
  }

  const overlayBtnStyle: React.CSSProperties = {
    padding: '3px 6px',
    border: '1px solid #d1d5db',
    borderRadius: '3px',
    background: 'white',
    cursor: 'pointer',
    fontSize: '11px',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    color: '#374151',
  };

  const activeOverlayBtnStyle: React.CSSProperties = {
    ...overlayBtnStyle,
    border: '2px solid #3b82f6',
    background: '#eff6ff',
  };

  return (
    <div ref={containerRef} onClick={handleClick} style={containerStyle}>
      <img
        src={src}
        alt={altText}
        width={width}
        height={height}
        style={{
          maxWidth: '100%',
          height: 'auto',
          border: isSelected ? '2px solid #3b82f6' : '2px solid transparent',
          borderRadius: '4px',
        }}
        draggable={false}
      />
      {isSelected && (
        <div
          style={{
            position: 'absolute',
            top: '-36px',
            left: '50%',
            transform: 'translateX(-50%)',
            display: 'flex',
            gap: '2px',
            background: 'white',
            border: '1px solid #d1d5db',
            borderRadius: '6px',
            padding: '3px',
            boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
            zIndex: 10,
          }}
        >
          {/* Float Left */}
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              handleAlignment('left');
            }}
            style={alignment === 'left' ? activeOverlayBtnStyle : overlayBtnStyle}
            title="Float left (text wraps right)"
          >
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
              <rect x="1" y="2" width="7" height="6" rx="1" fill="currentColor" />
              <line x1="10" y1="3" x2="17" y2="3" stroke="currentColor" strokeWidth="1.5" />
              <line x1="10" y1="5.5" x2="17" y2="5.5" stroke="currentColor" strokeWidth="1.5" />
              <line x1="10" y1="8" x2="14" y2="8" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="11" x2="17" y2="11" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="13.5" x2="17" y2="13.5" stroke="currentColor" strokeWidth="1.5" />
            </svg>
          </button>
          {/* Center */}
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              handleAlignment('center');
            }}
            style={alignment === 'center' ? activeOverlayBtnStyle : overlayBtnStyle}
            title="Center"
          >
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
              <rect x="4" y="2" width="10" height="6" rx="1" fill="currentColor" />
              <line x1="1" y1="11" x2="17" y2="11" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="13.5" x2="17" y2="13.5" stroke="currentColor" strokeWidth="1.5" />
            </svg>
          </button>
          {/* Float Right */}
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              handleAlignment('right');
            }}
            style={alignment === 'right' ? activeOverlayBtnStyle : overlayBtnStyle}
            title="Float right (text wraps left)"
          >
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
              <rect x="10" y="2" width="7" height="6" rx="1" fill="currentColor" />
              <line x1="1" y1="3" x2="8" y2="3" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="5.5" x2="8" y2="5.5" stroke="currentColor" strokeWidth="1.5" />
              <line x1="4" y1="8" x2="8" y2="8" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="11" x2="17" y2="11" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="13.5" x2="17" y2="13.5" stroke="currentColor" strokeWidth="1.5" />
            </svg>
          </button>
          {/* Block (no wrap) */}
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              handleAlignment('none');
            }}
            style={alignment === 'none' ? activeOverlayBtnStyle : overlayBtnStyle}
            title="Block (no text wrap)"
          >
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
              <rect x="1" y="2" width="16" height="7" rx="1" fill="currentColor" />
              <line x1="1" y1="12" x2="17" y2="12" stroke="currentColor" strokeWidth="1.5" />
              <line x1="1" y1="14.5" x2="17" y2="14.5" stroke="currentColor" strokeWidth="1.5" />
            </svg>
          </button>

          <span style={{ width: '1px', background: '#d1d5db', margin: '0 2px' }} />

          {/* Delete */}
          <button
            type="button"
            onClick={(e) => {
              e.stopPropagation();
              handleDelete();
            }}
            style={{
              ...overlayBtnStyle,
              color: '#ef4444',
            }}
            title="Remove image"
          >
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
              <line x1="3" y1="3" x2="11" y2="11" stroke="currentColor" strokeWidth="2" />
              <line x1="11" y1="3" x2="3" y2="11" stroke="currentColor" strokeWidth="2" />
            </svg>
          </button>
        </div>
      )}
    </div>
  );
}

// --- DOM conversion (HTML -> ImageNode) ---

function convertImageElement(domNode: Node): DOMConversionOutput | null {
  if (domNode instanceof HTMLImageElement) {
    const src = domNode.getAttribute('src');
    const altText = domNode.getAttribute('alt') || '';
    if (!src) return null;

    const widthAttr = domNode.getAttribute('width');
    const heightAttr = domNode.getAttribute('height');
    const width = widthAttr ? parseInt(widthAttr, 10) : undefined;
    const height = heightAttr ? parseInt(heightAttr, 10) : undefined;

    const style = domNode.getAttribute('style') || '';
    let alignment: ImageAlignment = 'none';
    if (style.includes('float: left') || style.includes('float:left')) {
      alignment = 'left';
    } else if (style.includes('float: right') || style.includes('float:right')) {
      alignment = 'right';
    } else if (
      (style.includes('margin-left: auto') && style.includes('margin-right: auto')) ||
      style.includes('margin: 0.5em auto')
    ) {
      alignment = 'center';
    }

    const node = $createImageNode({ src, altText, width, height, alignment });
    return { node };
  }
  return null;
}

// --- ImageNode class ---

export class ImageNode extends DecoratorNode<ReactElement> {
  __src: string;
  __altText: string;
  __width: number | undefined;
  __height: number | undefined;
  __alignment: ImageAlignment;

  static getType(): string {
    return 'image';
  }

  static clone(node: ImageNode): ImageNode {
    return new ImageNode(
      node.__src,
      node.__altText,
      node.__width,
      node.__height,
      node.__alignment,
      node.__key
    );
  }

  constructor(
    src: string,
    altText: string,
    width?: number,
    height?: number,
    alignment: ImageAlignment = 'none',
    key?: NodeKey
  ) {
    super(key);
    this.__src = src;
    this.__altText = altText;
    this.__width = width;
    this.__height = height;
    this.__alignment = alignment;
  }

  static importJSON(serializedNode: SerializedImageNode): ImageNode {
    const { src, altText, width, height, alignment } = serializedNode;
    return $createImageNode({ src, altText, width, height, alignment });
  }

  exportJSON(): SerializedImageNode {
    return {
      type: 'image',
      version: 1,
      src: this.__src,
      altText: this.__altText,
      width: this.__width,
      height: this.__height,
      alignment: this.__alignment,
    };
  }

  exportDOM(): DOMExportOutput {
    const img = document.createElement('img');
    img.setAttribute('src', this.__src);
    img.setAttribute('alt', this.__altText);
    if (this.__width) img.setAttribute('width', String(this.__width));
    if (this.__height) img.setAttribute('height', String(this.__height));

    const styles: string[] = ['max-width: 100%', 'height: auto'];
    if (this.__alignment === 'left') {
      styles.push('float: left', 'margin: 0 1em 0.5em 0');
    } else if (this.__alignment === 'right') {
      styles.push('float: right', 'margin: 0 0 0.5em 1em');
    } else if (this.__alignment === 'center') {
      styles.push('display: block', 'margin: 0.5em auto');
    } else {
      styles.push('display: block', 'margin: 0.5em 0');
    }
    img.setAttribute('style', styles.join('; '));

    return { element: img };
  }

  static importDOM(): DOMConversionMap | null {
    return {
      img: () => ({
        conversion: convertImageElement,
        priority: 0,
      }),
    };
  }

  createDOM(): HTMLElement {
    const div = document.createElement('div');
    return div;
  }

  updateDOM(): boolean {
    return false;
  }

  isInline(): boolean {
    return false;
  }

  setWidth(width: number | undefined): void {
    const writable = this.getWritable();
    writable.__width = width;
  }

  setHeight(height: number | undefined): void {
    const writable = this.getWritable();
    writable.__height = height;
  }

  setAlignment(alignment: ImageAlignment): void {
    const writable = this.getWritable();
    writable.__alignment = alignment;
  }

  decorate(): ReactElement {
    return (
      <ImageComponent
        src={this.__src}
        altText={this.__altText}
        width={this.__width}
        height={this.__height}
        alignment={this.__alignment}
        nodeKey={this.__key}
      />
    );
  }
}

// --- Helper functions ---

export function $createImageNode(payload: ImagePayload): ImageNode {
  return new ImageNode(
    payload.src,
    payload.altText,
    payload.width,
    payload.height,
    payload.alignment || 'none',
    payload.key
  );
}

export function $isImageNode(node: LexicalNode | null | undefined): node is ImageNode {
  return node instanceof ImageNode;
}

// --- Plugin (registers INSERT_IMAGE_COMMAND handler) ---

export function ImagePlugin(): null {
  const [editor] = useLexicalComposerContext();

  useEffect(() => {
    return editor.registerCommand<ImagePayload>(
      INSERT_IMAGE_COMMAND,
      (payload) => {
        const imageNode = $createImageNode(payload);
        const selection = $getSelection();
        if (selection) {
          $insertNodeToNearestRoot(imageNode);
        } else {
          $getRoot().append(imageNode);
        }
        return true;
      },
      COMMAND_PRIORITY_EDITOR
    );
  }, [editor]);

  return null;
}

// --- ImageInsertPopover (shared popover for image source selection) ---

interface ImageInsertPopoverProps {
  editor: LexicalEditor;
  onClose: () => void;
  onUploadClick: () => void;
  /** Popover styling variant: 'light' for Puck editor, 'dark' for Basic editor */
  variant?: 'light' | 'dark';
  /** Ref to the trigger wrapper element for viewport-aware positioning */
  triggerRef: RefObject<HTMLElement | null>;
}

export function ImageInsertPopover({
  editor,
  onClose,
  onUploadClick,
  variant = 'light',
  triggerRef,
}: ImageInsertPopoverProps) {
  const popoverRef = useRef<HTMLDivElement>(null);
  const [showLibrary, setShowLibrary] = useState(false);
  const popoverPosition = usePopoverPosition(triggerRef, popoverRef, !showLibrary);

  // Close when clicking outside (disabled while the library modal is open)
  useEffect(() => {
    if (showLibrary) return;
    const handleClickOutside = (e: MouseEvent) => {
      if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
        onClose();
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [onClose, showLibrary]);

  const handleLibrarySelect = useCallback(
    (image: ImageSelection) => {
      editor.focus();
      editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
        src: image.url,
        altText: image.alt || image.filename.replace(/\.[^.]+$/, ''),
      });
      setShowLibrary(false);
      onClose();
    },
    [editor, onClose]
  );

  const isDark = variant === 'dark';

  const optionStyle: React.CSSProperties = {
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    width: '100%',
    padding: '8px 12px',
    border: 'none',
    background: 'transparent',
    cursor: 'pointer',
    fontSize: '0.8125rem',
    color: isDark ? 'rgba(255, 255, 255, 0.9)' : '#374151',
    borderRadius: '4px',
    textAlign: 'left' as const,
  };

  const optionHoverBg = isDark ? 'rgba(255, 255, 255, 0.1)' : '#f3f4f6';

  return (
    <>
      <div
        ref={popoverRef}
        style={{
          position: 'absolute',
          top: popoverPosition.top,
          bottom: popoverPosition.bottom,
          left: popoverPosition.left,
          right: popoverPosition.right,
          zIndex: 1000,
          background: isDark ? 'rgba(30, 41, 59, 0.95)' : 'white',
          border: isDark ? '1px solid rgba(148, 163, 184, 0.25)' : '1px solid #d1d5db',
          borderRadius: '12px',
          padding: '4px',
          boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
          minWidth: '200px',
        }}
      >
        <button
          type="button"
          style={optionStyle}
          onClick={() => {
            onUploadClick();
            onClose();
          }}
          onMouseEnter={(e) => {
            e.currentTarget.style.background = optionHoverBg;
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.background = 'transparent';
          }}
        >
          <svg
            width="16"
            height="16"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
          >
            <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
            <polyline points="17 8 12 3 7 8" />
            <line x1="12" y1="3" x2="12" y2="15" />
          </svg>
          Upload from computer
        </button>
        <button
          type="button"
          style={optionStyle}
          onClick={() => setShowLibrary(true)}
          onMouseEnter={(e) => {
            e.currentTarget.style.background = optionHoverBg;
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.background = 'transparent';
          }}
        >
          <svg
            width="16"
            height="16"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
          >
            <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
            <circle cx="8.5" cy="8.5" r="1.5" />
            <polyline points="21 15 16 10 5 21" />
          </svg>
          Choose from Media Library
        </button>
      </div>
      <MediaLibraryModal
        isOpen={showLibrary}
        onClose={() => {
          setShowLibrary(false);
          onClose();
        }}
        onSelect={handleLibrarySelect}
        context="general"
      />
    </>
  );
}
