import React, { useRef, useEffect, useMemo } from 'react';
import TextField from './TextField';
import DBTextField from './DBTextField';
import ExpressionField from './ExpressionField';
import FrameElement from './FrameElement';
import HorizontalLine from './HorizontalLine';
import VerticalLine from './VerticalLine';
import SymbolElement from './SymbolElement';
import BandElement from './BandElement';
import { useReportData } from './DataContext';
import { BandProvider } from './BandContext';
import { detectCrossroads } from './utils/crossroadDetector';
import { elementIntersectsRect } from './utils/elementUtils';
import { calculateBandInstances, calculateTotalContentHeight } from './utils/bandRenderer';
import './EditorCanvas.css';

const CHAR_WIDTH = 18;
const CHAR_HEIGHT = 32;
const GRID_COLS = 80;
const GRID_ROWS = 66;

function EditorCanvas({
  elements,
  selectedElementIds,
  onElementSelect,
  onSelectMultiple,
  onDeselectAll,
  onElementUpdate,
  onElementDelete,
  toolMode,
  borderStyle,
  onAddElement,
  previewMode = false,
  selectedChar = '─'
}) {
  const canvasRef = useRef(null);
  const [frameStart, setFrameStart] = React.useState(null);
  const [framePreview, setFramePreview] = React.useState(null);
  const [lineStart, setLineStart] = React.useState(null);
  const [linePreview, setLinePreview] = React.useState(null);
  const [selectionRect, setSelectionRect] = React.useState(null);
  const [isSelecting, setIsSelecting] = React.useState(false);
  const [isPainting, setIsPainting] = React.useState(false);
  const [paintedPositions, setPaintedPositions] = React.useState(new Set());
  const [didDragSelect, setDidDragSelect] = React.useState(false);

  // Get report data for preview mode
  const { reportData } = useReportData();

  // Calculate total content height in preview mode
  const contentHeight = useMemo(() => {
    if (previewMode) {
      const bands = elements.filter(el => el.type === 'band');
      const totalHeight = calculateTotalContentHeight(bands, reportData, elements);
      return totalHeight * CHAR_HEIGHT;
    }
    return GRID_ROWS * CHAR_HEIGHT; // Default height in design mode
  }, [previewMode, elements, reportData]);

  const snapToGrid = (pixelX, pixelY) => {
    const col = Math.max(0, Math.min(GRID_COLS - 1, Math.round(pixelX / CHAR_WIDTH)));
    const row = Math.max(0, Math.min(GRID_ROWS - 1, Math.round(pixelY / CHAR_HEIGHT)));
    return { col, row };
  };

  const snapToGridAlt = (pixelX, pixelY) => {
    const col = Math.max(0, Math.min(GRID_COLS - 1, Math.floor(pixelX / CHAR_WIDTH)));
    const row = Math.max(0, Math.min(GRID_ROWS - 1, Math.floor(pixelY / CHAR_HEIGHT)));
    return { col, row };
  };


  const gridToPixels = (col, row) => ({
    x: col * CHAR_WIDTH,
    y: row * CHAR_HEIGHT
  });

  const handleCanvasMouseDown = (e) => {
    const rect = canvasRef.current.getBoundingClientRect();
    const pixelX = e.clientX - rect.left;
    const pixelY = e.clientY - rect.top;
    const { col, row } = snapToGridAlt(pixelX, pixelY);

    if (toolMode === 'select') {
      // Start selection rectangle
      setIsSelecting(true);
      setSelectionRect({ startX: col, startY: row, endX: col, endY: row });
      setDidDragSelect(false);
      return;
    } else if (toolMode === 'drawTable') {
      // Start painting symbols
      setIsPainting(true);
      paintSymbol(col, row);
      return;
    }
  };

  const handleCanvasClick = (e) => {
    const rect = canvasRef.current.getBoundingClientRect();
    const pixelX = e.clientX - rect.left;
    const pixelY = e.clientY - rect.top;
    const { col, row } = snapToGridAlt(pixelX, pixelY);

    if (toolMode === 'addText') {
      const newElement = {
        id: `text-${Date.now()}`,
        type: 'text',
        x: col,
        y: row,
        width: 10,
        height: 1,
        content: 'Text'
      };
      onAddElement(newElement);
    } else if (toolMode === 'addDBText') {
      const newElement = {
        id: `dbtext-${Date.now()}`,
        type: 'dbtext',
        x: col,
        y: row,
        width: 10,
        height: 1,
        objectKey: '',
        fieldPath: ''
      };
      onAddElement(newElement);
    } else if (toolMode === 'addExpr') {
      const newElement = {
        id: `expr-${Date.now()}`,
        type: 'expr',
        x: col,
        y: row,
        width: 10,
        height: 1,
        expression: '',
        dataType: 'general',
        alignment: 'left'
      };
      onAddElement(newElement);
    } else if (toolMode === 'addFrame') {
      if (!frameStart) {
        // First click - set start position
        setFrameStart({ col, row });
      } else {
        // Second click - create frame
        const x = Math.min(frameStart.col, col);
        const y = Math.min(frameStart.row, row);
        const width = Math.abs(col - frameStart.col) + 1;
        const height = Math.abs(row - frameStart.row) + 1;

        if (width >= 3 && height >= 3) {
          const newElement = {
            id: `frame-${Date.now()}`,
            type: 'frame',
            x,
            y,
            width,
            height,
            borderStyle
          };
          onAddElement(newElement);
        }
        setFrameStart(null);
        setFramePreview(null);
      }
    } else if (toolMode === 'addHLine') {
      if (!lineStart) {
        // First click - set start position
        setLineStart({ col, row, type: 'hline' });
      } else {
        // Second click - create horizontal line
        const x = Math.min(lineStart.col, col);
        const y = lineStart.row;
        const length = Math.abs(col - lineStart.col) + 1;

        if (length >= 2) {
          const newElement = {
            id: `hline-${Date.now()}`,
            type: 'hline',
            x,
            y,
            length,
            lineStyle: borderStyle
          };
          onAddElement(newElement);
        }
        setLineStart(null);
        setLinePreview(null);
      }
    } else if (toolMode === 'addVLine') {
      if (!lineStart) {
        // First click - set start position
        setLineStart({ col, row, type: 'vline' });
      } else {
        // Second click - create vertical line
        const x = lineStart.col;
        const y = Math.min(lineStart.row, row);
        const length = Math.abs(row - lineStart.row) + 1;

        if (length >= 2) {
          const newElement = {
            id: `vline-${Date.now()}`,
            type: 'vline',
            x,
            y,
            length,
            lineStyle: borderStyle
          };
          onAddElement(newElement);
        }
        setLineStart(null);
        setLinePreview(null);
      }
    } else if (toolMode === 'addBand') {
      // Add band (always full width, x=0)
      // Check for overlap with existing bands
      const newBand = {
        id: `band-${Date.now()}`,
        type: 'band',
        x: 0,
        y: row,
        width: 80,
        height: 3,
        bandType: 'detail',
        dataSource: '',
        caption: '',
        children: [],
        parentBandId: null
      };

      // Check if this overlaps with any existing band
      const overlaps = elements.some(el => {
        if (el.type !== 'band') return false;
        const elMaxY = el.y + el.height - 1;
        const newMaxY = newBand.y + newBand.height - 1;
        return !(elMaxY < newBand.y || el.y > newMaxY);
      });

      if (overlaps) {
        alert('Cannot place band here - it overlaps with another band');
      } else {
        onAddElement(newBand);
      }
    } else if (toolMode === 'select') {
      // Click on canvas background - deselect all (but not if we just did a drag selection)
      if (!didDragSelect) {
        onDeselectAll();
      }
      setDidDragSelect(false);
    }
  };

  const paintSymbol = (col, row) => {
    const key = `${col},${row}`;
    if (paintedPositions.has(key)) return; // Already painted this cell

    // Check if symbol already exists at this position
    const existingIndex = elements.findIndex(
      el => el.type === 'symbol' && el.x === col && el.y === row
    );

    if (existingIndex >= 0) {
      // Replace existing symbol
      onElementUpdate(elements[existingIndex].id, { char: selectedChar });
    } else {
      // Create new symbol
      const newElement = {
        id: `symbol-${Date.now()}-${Math.random()}`,
        type: 'symbol',
        x: col,
        y: row,
        char: selectedChar
      };
      onAddElement(newElement);
    }

    setPaintedPositions(prev => new Set([...prev, key]));
  };

  const findElementsInRect = (rect) => {
    const minX = Math.min(rect.startX, rect.endX);
    const maxX = Math.max(rect.startX, rect.endX);
    const minY = Math.min(rect.startY, rect.endY);
    const maxY = Math.max(rect.startY, rect.endY);

    // Use element class method for intersection checking
    return elements
      .filter(el => elementIntersectsRect(el, { minX, maxX, minY, maxY }))
      .map(el => el.id);
  };

  const handleMouseMove = (e) => {
    const rect = canvasRef.current.getBoundingClientRect();
    const pixelX = e.clientX - rect.left;
    const pixelY = e.clientY - rect.top;
    const { col, row } = snapToGrid(pixelX, pixelY);
    const { col: altCol, row: altRow } = snapToGridAlt(pixelX, pixelY);

    if (isSelecting && toolMode === 'select') {
      setSelectionRect(prev => {
        // Mark that we dragged if the rectangle has actually moved
        if (prev && (prev.startX !== altCol || prev.startY !== altRow)) {
          setDidDragSelect(true);
        }
        return { ...prev, endX: altCol, endY: altRow };
      });
    } else if (isPainting && toolMode === 'drawTable') {
      paintSymbol(altCol, altRow);
    } else if (toolMode === 'addFrame' && frameStart) {
      const x = Math.min(frameStart.col, col);
      const y = Math.min(frameStart.row, row);
      const width = Math.abs(col - frameStart.col) + 1;
      const height = Math.abs(row - frameStart.row) + 1;

      setFramePreview({ x, y, width, height });
    } else if (toolMode === 'addHLine' && lineStart) {
      const x = Math.min(lineStart.col, col);
      const y = lineStart.row;
      const length = Math.abs(col - lineStart.col) + 1;

      setLinePreview({ x, y, length, type: 'hline' });
    } else if (toolMode === 'addVLine' && lineStart) {
      const x = lineStart.col;
      const y = Math.min(lineStart.row, row);
      const length = Math.abs(row - lineStart.row) + 1;

      setLinePreview({ x, y, length, type: 'vline' });
    }
  };

  const handleElementDragStart = (elementId, startX, startY) => {
    const element = elements.find(el => el.id === elementId);
    return {
      elementId,
      startX,
      startY,
      initialElementX: element.x,
      initialElementY: element.y
    };
  };

  const handleElementDrag = (dragData, deltaX, deltaY) => {
    const element = elements.find(el => el.id === dragData.elementId);
    if (!element) return;

    // Calculate new position based on initial position + delta
    const newPixelX = (dragData.initialElementX * CHAR_WIDTH) + deltaX;
    const newPixelY = (dragData.initialElementY * CHAR_HEIGHT) + deltaY;
    const { col, row } = snapToGrid(newPixelX, newPixelY);

    // Only update if position actually changed
    if (col !== element.x || row !== element.y) {
      // If this element is selected with others, move all selected elements
      if (selectedElementIds.includes(dragData.elementId) && selectedElementIds.length > 1) {
        const deltaCol = col - element.x;
        const deltaRow = row - element.y;
        selectedElementIds.forEach(id => {
          const el = elements.find(e => e.id === id);
          if (el) {
            onElementUpdate(id, { x: el.x + deltaCol, y: el.y + deltaRow });
          }
        });
      } else {
        onElementUpdate(dragData.elementId, { x: col, y: row });
      }
    }
  };

  // Add global mouseup listener to handle drag completion outside canvas
  useEffect(() => {
    const handleGlobalMouseUp = () => {
      if (isSelecting && toolMode === 'select' && selectionRect) {
        const selectedIds = findElementsInRect(selectionRect);
        onSelectMultiple(selectedIds);
        setIsSelecting(false);
        setSelectionRect(null);
      } else if (isPainting && toolMode === 'drawTable') {
        setIsPainting(false);
        setPaintedPositions(new Set());
      }
    };

    document.addEventListener('mouseup', handleGlobalMouseUp);
    return () => document.removeEventListener('mouseup', handleGlobalMouseUp);
  }, [isSelecting, isPainting, selectionRect, toolMode, elements, onSelectMultiple]);

  // Render grid lines
  const renderGrid = () => {
    const gridLines = [];
    // Vertical lines
    for (let i = 0; i <= GRID_COLS; i++) {
      gridLines.push(
        <div
          key={`v-${i}`}
          className="grid-line vertical"
          style={{ left: `${i * (CHAR_WIDTH)}px` }}
        />
      );
    }
    // Horizontal lines
    for (let i = 0; i <= GRID_ROWS; i++) {
      gridLines.push(
        <div
          key={`h-${i}`}
          className="grid-line horizontal"
          style={{ top: `${i * CHAR_HEIGHT}px` }}
        />
      );
    }
    return gridLines;
  };

  // Calculate crossroad map for intersection rendering
  const crossroadMap = useMemo(() => {
    return detectCrossroads(elements);
  }, [elements]);

  const showCrosshair = toolMode === 'addFrame' || toolMode === 'addHLine' || toolMode === 'addVLine';

  // Render page break indicators (every 66 rows in preview mode)
  const renderPageBreaks = () => {
    if (!previewMode) return null;

    const breaks = [];
    const pageHeight = GRID_ROWS * CHAR_HEIGHT; // 66 rows * 32px = 2112px
    const numPages = Math.ceil(contentHeight / pageHeight);

    for (let i = 1; i < numPages; i++) {
      breaks.push(
        <div
          key={`page-break-${i}`}
          className="page-break-indicator"
          style={{ top: `${i * pageHeight}px` }}
        />
      );
    }

    return breaks;
  };

  // Helper function to render an element (used for standalone and child elements)
  const renderElement = (element, parentBandId = null) => {
    const commonProps = {
      key: element.id,
      element,
      isSelected: selectedElementIds.includes(element.id),
      isSingleSelection: selectedElementIds.length === 1 && selectedElementIds[0] === element.id,
      onSelect: (e) => onElementSelect(element.id, e?.ctrlKey || e?.metaKey),
      onUpdate: (updates) => onElementUpdate(element.id, updates),
      onDelete: () => onElementDelete(element.id),
      onDragStart: handleElementDragStart,
      onDrag: handleElementDrag,
      charWidth: CHAR_WIDTH,
      charHeight: CHAR_HEIGHT,
      toolMode
    };

    if (element.type === 'text') {
      return <TextField {...commonProps} />;
    } else if (element.type === 'dbtext') {
      return (
        <DBTextField
          {...commonProps}
          previewMode={previewMode}
          parentBandId={parentBandId}
        />
      );
    } else if (element.type === 'expr') {
      return (
        <ExpressionField
          {...commonProps}
          previewMode={previewMode}
          parentBandId={parentBandId}
        />
      );
    } else if (element.type === 'frame') {
      return <FrameElement {...commonProps} />;
    } else if (element.type === 'hline') {
      return <HorizontalLine {...commonProps} crossroadMap={crossroadMap} />;
    } else if (element.type === 'vline') {
      return <VerticalLine {...commonProps} crossroadMap={crossroadMap} />;
    } else if (element.type === 'symbol') {
      return <SymbolElement {...commonProps} />;
    }
    return null;
  };

  return (
    <div className="canvas-container">
      <div
        ref={canvasRef}
        className={`editor-canvas ${showCrosshair ? 'crosshair' : ''}`}
        onClick={handleCanvasClick}
        onMouseDown={handleCanvasMouseDown}
        onMouseMove={handleMouseMove}
      >
        <div
          className="editor-canvas-content"
          style={{
            minHeight: `${GRID_ROWS * CHAR_HEIGHT}px`,
            height: previewMode ? `${contentHeight}px` : `${GRID_ROWS * CHAR_HEIGHT}px`
          }}
        >
          {renderGrid()}

          {/* Page break indicators */}
          {renderPageBreaks()}

        {/* Render selection rectangle */}
        {selectionRect && isSelecting && (
          <div
            className="selection-rectangle"
            style={{
              left: `${Math.min(selectionRect.startX, selectionRect.endX) * CHAR_WIDTH}px`,
              top: `${Math.min(selectionRect.startY, selectionRect.endY) * CHAR_HEIGHT}px`,
              width: `${(Math.abs(selectionRect.endX - selectionRect.startX) + 1) * CHAR_WIDTH}px`,
              height: `${(Math.abs(selectionRect.endY - selectionRect.startY) + 1) * CHAR_HEIGHT}px`
            }}
          />
        )}

        {/* Render frame preview */}
        {framePreview && (
          <div
            className="frame-preview"
            style={{
              left: `${framePreview.x * CHAR_WIDTH}px`,
              top: `${framePreview.y * CHAR_HEIGHT}px`,
              width: `${framePreview.width * CHAR_WIDTH}px`,
              height: `${framePreview.height * CHAR_HEIGHT}px`
            }}
          />
        )}

        {/* Render line preview */}
        {linePreview && (
          <div
            className="line-preview"
            style={{
              left: `${linePreview.x * CHAR_WIDTH}px`,
              top: `${linePreview.y * CHAR_HEIGHT}px`,
              width: linePreview.type === 'hline' ? `${linePreview.length * CHAR_WIDTH}px` : `${CHAR_WIDTH}px`,
              height: linePreview.type === 'vline' ? `${linePreview.length * CHAR_HEIGHT}px` : `${CHAR_HEIGHT}px`
            }}
          />
        )}

        {/* Render elements */}
        {elements.map(element => {
          // Check if element is a child of a band
          const isChildOfBand = elements.some(el => el.type === 'band' && el.children && el.children.includes(element.id));

          // In design mode, skip child elements (they're rendered inside band containers)
          if (!previewMode) {
            if (isChildOfBand && element.type !== 'band') {
              return null; // Skip - will be rendered as child of band
            }
          } else {
            // In preview mode, skip ALL elements - they're rendered in dedicated sections below
            // (bands get repeated with data, children render inside bands, standalone elements get shifted)
            return null;
          }

          // Render bands specially in design mode
          if (element.type === 'band') {
            if (!previewMode) {
              // Design mode: render band as container with children
              const childElements = elements.filter(el => element.children && element.children.includes(el.id));
              return (
                <BandElement
                  key={element.id}
                  element={element}
                  isSelected={selectedElementIds.includes(element.id)}
                  isSingleSelection={selectedElementIds.length === 1 && selectedElementIds[0] === element.id}
                  onSelect={(e) => onElementSelect(element.id, e?.ctrlKey || e?.metaKey)}
                  onUpdate={(updates) => onElementUpdate(element.id, updates)}
                  onDelete={() => onElementDelete(element.id)}
                  onDragStart={handleElementDragStart}
                  onDrag={handleElementDrag}
                  charWidth={CHAR_WIDTH}
                  charHeight={CHAR_HEIGHT}
                  toolMode={toolMode}
                >
                  {/* Render child elements inside band */}
                  {childElements.map(childEl => renderElement(childEl, element.id))}
                </BandElement>
              );
            }
            // Preview mode: skip (bands rendered separately below)
            return null;
          }

          // Render other element types
          return renderElement(element);
        })}

        {/* Preview mode: Render band instances with repeated data */}
        {previewMode && (() => {
          const bands = elements.filter(el => el.type === 'band');
          const bandInstances = calculateBandInstances(bands, reportData, elements);

          return bandInstances.map(instance => {
            // Build band data map for this instance
            const bandDataMap = instance.data ? { [instance.bandId]: instance.data } : {};

            return (
              <BandProvider key={`${instance.bandId}-instance-${instance.instanceIndex}`} bandDataMap={bandDataMap}>
                {/* Render child elements at adjusted positions */}
                {instance.childElements.map(childEl => {
                  // Child element Y is relative to band, so add instance Y to get absolute position
                  const adjustedElement = {
                    ...childEl,
                    y: instance.y + childEl.y
                  };

                  return renderElement(adjustedElement, instance.bandId);
                })}
              </BandProvider>
            );
          });
        })()}

        {/* Preview mode: Render standalone elements that aren't in bands */}
        {previewMode && (() => {
          const bands = elements.filter(el => el.type === 'band');
          const bandInstances = calculateBandInstances(bands, reportData, elements);

          // Build a map of band Y ranges in design mode vs preview mode
          // IMPORTANT: Only consider top-level bands (no parent) because nested band expansion
          // is already included in their parent's expansion
          const bandShifts = new Map();
          bands.filter(band => !band.parentBandId).forEach(band => {
            const bandDesignMaxY = band.y + band.height - 1;

            // Find all instances of this band
            const instances = bandInstances.filter(inst => inst.bandId === band.id);
            if (instances.length > 0) {
              const lastInstance = instances[instances.length - 1];
              const bandPreviewMaxY = lastInstance.y + lastInstance.height - 1;

              // Calculate how much this band expanded
              const expansion = bandPreviewMaxY - bandDesignMaxY;
              bandShifts.set(band.id, { designMaxY: bandDesignMaxY, expansion });
            }
          });

          return elements.filter(el => {
            if (el.type === 'band') return false; // Skip bands themselves
            // Check if this element is a child of any band
            const isChildOfBand = elements.some(band => band.type === 'band' && band.children && band.children.includes(el.id));
            return !isChildOfBand; // Only render if NOT a child of a band
          }).map(element => {
            // Calculate cumulative shift for elements below bands
            let totalShift = 0;
            bandShifts.forEach(({ designMaxY, expansion }) => {
              if (element.y > designMaxY) {
                totalShift += expansion;
              }
            });

            // Render with adjusted Y position
            const adjustedElement = {
              ...element,
              y: element.y + totalShift
            };

            return renderElement(adjustedElement);
          });
        })()}
        </div>
      </div>
    </div>
  );
}

export default EditorCanvas;
