'use client';

import React, { useState, useEffect, useCallback, useRef } from 'react';
import {
  EditorProvider,
  PortableTextEditable,
  useEditor,
  type PortableTextBlock,
  type RenderDecoratorFunction,
  type RenderStyleFunction,
  type RenderBlockFunction,
  type RenderListItemFunction,
  type RenderAnnotationFunction
} from '@portabletext/editor';
import { EventListenerPlugin } from '@portabletext/editor/plugins';
import { PortableText } from '@portabletext/react';
import Button from '../ui/Button';
import { sanitizeHtmlSync } from '../../lib/sanitization';
import { editorSchema } from '../../lib/portabletext/editorSchema';
import { debug } from '../../lib/debug';

interface PortableTextEditorProps {
  value: string; // HTML value for compatibility - will be converted
  onChange: (value: string) => void; // Returns HTML for compatibility
  placeholder?: string;
  error?: string;
  storyId?: string;
  enableImageProcessing?: boolean;
}

// Conversion utilities
function htmlToPortableTextBlocks(html: string): PortableTextBlock[] {
  if (!html || html.trim() === '') {
    return [{ _type: 'block', _key: generateKey(), style: 'normal', markDefs: [], children: [{ _type: 'span', _key: generateKey(), text: '', marks: [] }] }];
  }

  // Basic HTML to Portable Text conversion
  // This is a simplified implementation - you could enhance this
  const sanitizedHtml = sanitizeHtmlSync(html);
  const parser = new DOMParser();
  const doc = parser.parseFromString(sanitizedHtml, 'text/html');

  const blocks: PortableTextBlock[] = [];
  const paragraphs = doc.querySelectorAll('p, h1, h2, h3, h4, h5, h6, blockquote, div');

  if (paragraphs.length === 0) {
    // Fallback: treat body text as single block, preserving newlines
    const bodyText = doc.body.textContent || '';
    return [{
      _type: 'block',
      _key: generateKey(),
      style: 'normal',
      markDefs: [],
      children: [{
        _type: 'span',
        _key: generateKey(),
        text: bodyText, // Keep newlines in the text
        marks: []
      }]
    }];
  }

  // Check if we have only one paragraph that might contain newlines
  if (paragraphs.length === 1) {
    const singleParagraph = paragraphs[0];
    const textContent = singleParagraph.textContent || '';

    // If this single paragraph contains newlines, preserve them in a single block
    if (textContent.includes('\n')) {
      const style = getStyleFromElement(singleParagraph);
      return [{
        _type: 'block',
        _key: generateKey(),
        style,
        markDefs: [],
        children: [{
          _type: 'span',
          _key: generateKey(),
          text: textContent, // Keep newlines in the text
          marks: []
        }]
      }];
    }
  }

  // Process all elements in document order to maintain sequence
  const allElements = Array.from(doc.body.querySelectorAll('*'));
  const processedElements = new Set<Element>();

  for (const element of allElements) {
    // Skip if already processed
    if (processedElements.has(element)) continue;

    // Handle images
    if (element.tagName === 'IMG') {
      const img = element as HTMLImageElement;
      blocks.push({
        _type: 'image',
        _key: generateKey(),
        src: img.getAttribute('src') || '',
        alt: img.getAttribute('alt') || '',
        caption: img.getAttribute('title') || '',
        width: img.getAttribute('width') ? parseInt(img.getAttribute('width')!) : undefined,
        height: img.getAttribute('height') ? parseInt(img.getAttribute('height')!) : undefined,
      });
      processedElements.add(element);
      continue;
    }

    // Handle code blocks
    if ((element.tagName === 'CODE' && element.parentElement?.tagName === 'PRE') ||
        (element.tagName === 'PRE' && element.querySelector('code'))) {
      const codeEl = element.tagName === 'CODE' ? element : element.querySelector('code');
      if (codeEl) {
        // Use innerText to preserve newlines and whitespace formatting
        // innerText respects CSS white-space property, so <pre> formatting is preserved
        let code = (codeEl as HTMLElement).innerText || codeEl.textContent || '';

        const language = codeEl.getAttribute('class')?.replace('language-', '') || '';

        if (code.trim()) {
          blocks.push({
            _type: 'codeBlock',
            _key: generateKey(),
            code,
            language,
          });
          processedElements.add(element);
          if (element.tagName === 'PRE') processedElements.add(codeEl);
        }
      }
      continue;
    }

    // Handle text blocks (paragraphs, headings, etc.)
    if (['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE', 'DIV'].includes(element.tagName)) {
      // Skip if this contains already processed elements (but allow inline code)
      if (element.querySelector('img')) {
        processedElements.add(element);
        continue;
      }

      // Check if this is a standalone pre/code block that should be handled specially
      const hasStandaloneCodeBlock = element.querySelector('pre') && element.children.length === 1 && element.children[0].tagName === 'PRE';
      if (hasStandaloneCodeBlock) {
        processedElements.add(element);
        continue; // Let the pre/code block handler take care of this
      }

      const style = getStyleFromElement(element);

      // For text content that may contain inline code, preserve formatting better
      let text: string;
      if (element.querySelector('code') && !element.querySelector('pre')) {
        // Has inline code - use innerText to preserve some formatting
        text = (element as HTMLElement).innerText || element.textContent || '';
      } else {
        // Regular text - use textContent, but also check if this might have come from a pre block
        text = (element as HTMLElement).innerText || element.textContent || '';
      }

      if (text.trim()) {
        // Keep newlines within a single block - they'll be converted to <br> tags later
        blocks.push({
          _type: 'block',
          _key: generateKey(),
          style,
          markDefs: [],
          children: [{
            _type: 'span',
            _key: generateKey(),
            text, // Keep newlines in the text
            marks: []
          }]
        });
        processedElements.add(element);
      }
    }
  }

  return blocks.length > 0 ? blocks : [{
    _type: 'block',
    _key: generateKey(),
    style: 'normal',
    markDefs: [],
    children: [{
      _type: 'span',
      _key: generateKey(),
      text: '',
      marks: []
    }]
  }];
}

function portableTextToHtml(blocks: PortableTextBlock[]): string {
  if (!blocks || blocks.length === 0) return '';

  const htmlParts: string[] = [];

  blocks.forEach(block => {
    if (block._type === 'block' && Array.isArray(block.children)) {
      const tag = getHtmlTagFromStyle((block.style as string) || 'normal');
      const children = block.children as PortableTextChild[];
      const text = children
        .map(child => child._type === 'span' ? child.text || '' : '')
        .join('') || '';

      // Convert any remaining newlines in text to <br> tags for proper display
      const textWithBreaks = text.replace(/\n/g, '<br>');
      // Always include blocks, even empty ones (they represent line breaks/paragraph spacing)
      htmlParts.push(`<${tag}>${textWithBreaks}</${tag}>`);
    } else if (block._type === 'image' && isImageBlock(block)) {
      // Convert image blocks back to HTML
      const attrs: string[] = [];
      if (block.src) attrs.push(`src="${block.src}"`);
      if (block.alt) attrs.push(`alt="${block.alt}"`);
      if (block.caption) attrs.push(`title="${block.caption}"`);
      if (block.width) attrs.push(`width="${block.width}"`);
      if (block.height) attrs.push(`height="${block.height}"`);

      htmlParts.push(`<img ${attrs.join(' ')} />`);
    } else if (block._type === 'codeBlock' && isCodeBlock(block)) {
      // Convert code blocks back to HTML
      const langClass = block.language ? ` class="language-${block.language}"` : '';
      // Escape HTML entities in code content to prevent XSS and preserve formatting
      const escapedCode = (block.code || '')
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
      htmlParts.push(`<pre><code${langClass}>${escapedCode}</code></pre>`);
    }
  });

  const html = htmlParts.join('\n');
  return sanitizeHtmlSync(html);
}

function getStyleFromElement(element: Element): string {
  const tagName = element.tagName.toLowerCase();
  const styleMap: Record<string, string> = {
    'p': 'normal',
    'div': 'normal',
    'h1': 'h1',
    'h2': 'h2',
    'h3': 'h3',
    'h4': 'h4',
    'h5': 'h5',
    'h6': 'h6',
    'blockquote': 'blockquote',
  };
  return styleMap[tagName] || 'normal';
}

function getHtmlTagFromStyle(style: string): string {
  const tagMap: Record<string, string> = {
    'normal': 'p',
    'h1': 'h1',
    'h2': 'h2',
    'h3': 'h3',
    'h4': 'h4',
    'h5': 'h5',
    'h6': 'h6',
    'blockquote': 'blockquote',
  };
  return tagMap[style] || 'p';
}

interface PortableTextChild {
  _type: string;
  _key: string;
  text?: string;
  marks?: string[];
}

// Type guards for custom block types
function isImageBlock(value: any): value is {
  _type: 'image';
  src?: string;
  alt?: string;
  caption?: string;
  width?: number;
  height?: number;
} {
  return value && typeof value === 'object' && value._type === 'image';
}

function isCodeBlock(value: any): value is {
  _type: 'codeBlock';
  code?: string;
  language?: string;
} {
  return value && typeof value === 'object' && value._type === 'codeBlock';
}

function generateKey(): string {
  return Math.random().toString(36).substring(2, 11);
}


// Toolbar component
function EditorToolbar({
  isScrollable,
  onToggleScrollable
}: {
  isScrollable: boolean;
  onToggleScrollable: () => void;
}) {
  const editor = useEditor();

  const toggleDecorator = (decorator: string) => {
    editor.send({ type: 'decorator.toggle', decorator });
  };

  const setStyle = (style: string) => {
    editor.send({ type: 'style.toggle', style });
  };

  return (
    <div className="flex items-center justify-between p-2 theme-card border theme-border rounded-t-lg">
      <div className="flex items-center gap-2">
        <div className="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
          ✨ Portable Text Editor
        </div>

        {/* Style buttons */}
        <div className="flex items-center gap-1 border-r pr-2 mr-2">
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => setStyle('normal')}
            title="Normal paragraph"
          >
            P
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => setStyle('h1')}
            title="Heading 1"
            className="text-lg font-bold"
          >
            H1
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => setStyle('h2')}
            title="Heading 2"
            className="text-base font-bold"
          >
            H2
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => setStyle('h3')}
            title="Heading 3"
            className="text-sm font-bold"
          >
            H3
          </Button>
        </div>

        {/* Decorator buttons */}
        <div className="flex items-center gap-1">
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => toggleDecorator('strong')}
            title="Bold (Ctrl+B)"
            className="font-bold"
          >
            B
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => toggleDecorator('em')}
            title="Italic (Ctrl+I)"
            className="italic"
          >
            I
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => toggleDecorator('underline')}
            title="Underline"
            className="underline"
          >
            U
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={() => toggleDecorator('strike')}
            title="Strike-through"
            className="line-through"
          >
            S
          </Button>
        </div>
      </div>

      {/* Scrollable toggle */}
      <div className="flex items-center gap-2">
        <span className="text-xs theme-text">Scrollable:</span>
        <Button
          type="button"
          size="sm"
          variant="ghost"
          onClick={onToggleScrollable}
          className={isScrollable ? 'theme-accent-bg text-white' : ''}
          title={isScrollable ? 'Switch to auto-expand mode' : 'Switch to scrollable mode'}
        >
          {isScrollable ? '📜' : '📏'}
        </Button>
      </div>
    </div>
  );
}


// Simple component that uses Portable Text editor directly
function EditorContent({
  value,
  onChange,
  placeholder,
  error
}: {
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
  error?: string;
}) {
  const [portableTextValue, setPortableTextValue] = useState<PortableTextBlock[]>(() =>
    htmlToPortableTextBlocks(value)
  );
  const [isScrollable, setIsScrollable] = useState(true); // Default to scrollable

  // Sync HTML value with prop changes (but not for internal changes)
  useEffect(() => {
    // Skip re-initialization if this change came from the editor itself
    if (isInternalChange.current) {
      debug.log('🔄 Skipping re-initialization for internal change');
      return;
    }

    debug.log('🔄 Editor value changed externally:', { valueLength: value?.length, valuePreview: value?.substring(0, 100) });
    setPortableTextValue(htmlToPortableTextBlocks(value));
  }, [value]);

  // Debug: log when portableTextValue changes
  useEffect(() => {
    debug.log('📝 Portable text blocks updated:', { blockCount: portableTextValue.length, blocks: portableTextValue });
  }, [portableTextValue]);

  // Track if changes are coming from internal editor changes
  const isInternalChange = useRef(false);

  // Handle content changes using the EventListenerPlugin
  const handleEditorChange = useCallback((event: any) => {
    if (event.type === 'mutation') {
      debug.log('📝 Editor content changed via EventListener:', { valueLength: event.value?.length });
      const html = portableTextToHtml(event.value);
      debug.log('📝 Converted to HTML:', { htmlLength: html.length, htmlPreview: html.substring(0, 200) });

      // Mark this as an internal change to prevent re-initialization
      isInternalChange.current = true;
      onChange(html);
      // Reset flag after a short delay to allow external changes
      setTimeout(() => {
        isInternalChange.current = false;
      }, 100);
    }
  }, [onChange]);

  // Add a ref to the editor container for direct paste handling
  const editorContainerRef = useRef<HTMLDivElement>(null);

  // Global paste event listener to catch ALL paste events
  useEffect(() => {
    const handleGlobalPaste = (event: ClipboardEvent) => {
      debug.log('🌍 Global paste event captured');

      // Check if the paste is happening within our editor
      const target = event.target as Element;
      const isInEditor = editorContainerRef.current?.contains(target);

      debug.log('📋 Paste details:', {
        isInEditor,
        targetTag: target?.tagName,
        targetClasses: target?.className,
        hasClipboardData: !!event.clipboardData
      });

      if (isInEditor && event.clipboardData) {
        const htmlData = event.clipboardData.getData('text/html');
        const textData = event.clipboardData.getData('text/plain');

        debug.log('📋 Clipboard contents:', {
          htmlLength: htmlData.length,
          textLength: textData.length,
          hasImages: htmlData.includes('<img'),
          htmlPreview: htmlData.substring(0, 300)
        });

        if (htmlData && htmlData.includes('<img')) {
          debug.log('📋 Images detected in paste! Attempting to process...');

          // Prevent default paste to handle it completely ourselves
          event.preventDefault();
          event.stopPropagation();

          // Convert the pasted HTML to our blocks maintaining order
          const pastedBlocks = htmlToPortableTextBlocks(htmlData);

          debug.log('📋 Converted blocks:', pastedBlocks.map(block => ({
            type: block._type,
            key: block._key,
            ...(block._type === 'image' ? { src: (block as any).src, alt: (block as any).alt } : {}),
            ...(block._type === 'block' ? { style: (block as any).style, text: (block as any).children?.[0]?.text?.substring(0, 50) } : {})
          })));

          if (pastedBlocks.length > 0) {
            // Insert the blocks at the end of current content (maintaining order within the paste)
            setTimeout(() => {
              setPortableTextValue(prev => {
                const updatedBlocks = [...prev, ...pastedBlocks];
                const html = portableTextToHtml(updatedBlocks);
                onChange(html);
                debug.log('📋 Added structured blocks maintaining order:', { pastedCount: pastedBlocks.length, totalBlocks: updatedBlocks.length });
                return updatedBlocks;
              });
            }, 10);
          }
        }
      }
    };

    // Add global event listener with capture phase to catch events early
    document.addEventListener('paste', handleGlobalPaste, true);

    return () => {
      document.removeEventListener('paste', handleGlobalPaste, true);
    };
  }, [onChange]);

  // Handle paste events directly on the editor container (backup approach)
  const handleContainerPaste = useCallback((_event: React.ClipboardEvent) => {
    debug.log('📦 Container paste handler triggered');
    // This might not be reached if global handler prevents default
  }, []);

  // Render functions for the editor
  const renderStyle: RenderStyleFunction = useCallback((props) => {
    const { schemaType, children } = props;

    switch (schemaType.value) {
      case 'h1':
        return <h1 className="text-3xl font-bold mb-4">{children}</h1>;
      case 'h2':
        return <h2 className="text-2xl font-bold mb-3">{children}</h2>;
      case 'h3':
        return <h3 className="text-xl font-bold mb-3">{children}</h3>;
      case 'h4':
        return <h4 className="text-lg font-bold mb-2">{children}</h4>;
      case 'h5':
        return <h5 className="text-base font-bold mb-2">{children}</h5>;
      case 'h6':
        return <h6 className="text-sm font-bold mb-2">{children}</h6>;
      case 'blockquote':
        return <blockquote className="border-l-4 border-gray-300 pl-4 italic my-4">{children}</blockquote>;
      default:
        return <p className="mb-2">{children}</p>;
    }
  }, []);

  const renderDecorator: RenderDecoratorFunction = useCallback((props) => {
    const { schemaType, children } = props;

    switch (schemaType.value) {
      case 'strong':
        return <strong>{children}</strong>;
      case 'em':
        return <em>{children}</em>;
      case 'underline':
        return <u>{children}</u>;
      case 'strike':
        return <s>{children}</s>;
      case 'code':
        return <code className="bg-gray-100 px-1 py-0.5 rounded text-sm font-mono">{children}</code>;
      default:
        return <>{children}</>;
    }
  }, []);

  const renderBlock: RenderBlockFunction = useCallback((props) => {
    const { schemaType, value, children } = props;

    debug.log('🎨 Rendering block:', { schemaType: schemaType.name, valueType: value?._type, value });

    // Handle image blocks
    if (schemaType.name === 'image' && isImageBlock(value)) {
      debug.log('🖼️ Rendering image block:', value);
      return (
        <div className="my-4 p-3 border border-dashed border-gray-300 rounded-lg bg-gray-50">
          <div className="flex items-center gap-2 mb-2">
            <span className="text-lg">🖼️</span>
            <span className="font-medium text-gray-700">Image Block</span>
          </div>
          <div className="text-sm text-gray-600 space-y-1">
            <p><strong>Source:</strong> {value.src || 'No source'}</p>
            {value.alt && <p><strong>Alt text:</strong> {value.alt}</p>}
            {value.caption && <p><strong>Caption:</strong> {value.caption}</p>}
            {(value.width || value.height) && (
              <p><strong>Dimensions:</strong> {value.width || '?'} × {value.height || '?'}</p>
            )}
          </div>
        </div>
      );
    }

    // Handle code blocks
    if (schemaType.name === 'codeBlock' && isCodeBlock(value)) {
      return (
        <div className="my-4 p-3 border border-dashed border-blue-300 rounded-lg bg-blue-50">
          <div className="flex items-center gap-2 mb-2">
            <span className="text-lg">💻</span>
            <span className="font-medium text-blue-700">Code Block</span>
            {value.language && (
              <span className="text-xs bg-blue-200 text-blue-800 px-2 py-1 rounded">
                {value.language}
              </span>
            )}
          </div>
          <pre className="text-sm text-gray-800 bg-white p-2 rounded border overflow-x-auto">
            <code>{value.code || '// No code'}</code>
          </pre>
        </div>
      );
    }

    // Default block rendering
    return <div>{children}</div>;
  }, []);

  const renderListItem: RenderListItemFunction = useCallback((props) => {
    return <li>{props.children}</li>;
  }, []);

  const renderAnnotation: RenderAnnotationFunction = useCallback((props) => {
    const { schemaType, children, value } = props;

    if (schemaType.name === 'link' && value && typeof value === 'object') {
      const linkValue = value as { href?: string; target?: string; title?: string };
      return (
        <a
          href={linkValue.href}
          target={linkValue.target || '_self'}
          title={linkValue.title}
          className="text-blue-600 hover:text-blue-800 underline"
        >
          {children}
        </a>
      );
    }

    return <>{children}</>;
  }, []);

  return (
    <div className="space-y-2">
      <EditorProvider
        key={`editor-${portableTextValue.length}-${Date.now()}`}
        initialConfig={{
          schemaDefinition: editorSchema,
          initialValue: portableTextValue,
        }}
      >
        <EventListenerPlugin on={handleEditorChange} />
        <EditorToolbar
          isScrollable={isScrollable}
          onToggleScrollable={() => setIsScrollable(!isScrollable)}
        />
        <div
          ref={editorContainerRef}
          className="border theme-border rounded-b-lg overflow-hidden"
          onPaste={handleContainerPaste}
        >
          <PortableTextEditable
            className={`p-3 focus:outline-none focus:ring-0 resize-none ${
              isScrollable
                ? 'h-[400px] overflow-y-auto'
                : 'min-h-[300px]'
            }`}
            placeholder={placeholder}
            renderStyle={renderStyle}
            renderDecorator={renderDecorator}
            renderBlock={renderBlock}
            renderListItem={renderListItem}
            renderAnnotation={renderAnnotation}
          />
        </div>
      </EditorProvider>

      {error && (
        <p className="text-sm text-red-600 dark:text-red-400">{error}</p>
      )}

      <div className="text-xs theme-text">
        <p>
          <strong>Portable Text Editor:</strong> Rich text editor with structured content.
          {isScrollable ? ' Fixed height with scrolling.' : ' Auto-expanding height.'}
          📋 Paste detection active.
        </p>
      </div>
    </div>
  );
}

export default function PortableTextEditorNew({
  value,
  onChange,
  placeholder = 'Write your story here...',
  error,
  storyId,
  enableImageProcessing = false
}: PortableTextEditorProps) {
  debug.log('🎯 Portable Text Editor loaded!', {
    valueLength: value?.length,
    enableImageProcessing,
    hasStoryId: !!storyId
  });

  return (
    <EditorContent
      value={value}
      onChange={onChange}
      placeholder={placeholder}
      error={error}
    />
  );
}