/**
 * Layout Analyzer Utility
 *
 * Analyzes element positions and relationships to determine
 * the best CSS layout strategy (flexbox alignment, padding offsets, etc.)
 */

import type { CanvasElement } from "@/types";

// Base alignment thresholds (for large elements ~60px+)
const BASE_THRESHOLD = 5;
// Minimum threshold to prevent too-strict matching on small elements
const MIN_THRESHOLD = 2;
// Maximum threshold to prevent too-loose matching on large elements
const MAX_THRESHOLD = 12;
// Percentage of average height to use as threshold
const THRESHOLD_RATIO = 0.1; // 10% of average height

const HEIGHT_DIFFERENCE_THRESHOLD = 0.3; // If height differs by 30%+, prefer center over baseline
const HORIZONTAL_POSITION_THRESHOLD = 0.2; // 20% threshold for edge detection

/**
 * Calculate dynamic alignment threshold based on element sizes
 * Smaller elements need tighter thresholds, larger elements can be more lenient
 */
function calculateDynamicThreshold(heights: number[]): number {
  if (heights.length === 0) return BASE_THRESHOLD;

  const avgHeight = heights.reduce((sum, h) => sum + h, 0) / heights.length;
  const minHeight = Math.min(...heights);

  // Use smaller of: 10% of average height, or 15% of smallest element
  // This ensures small elements don't get incorrectly aligned
  const threshold = Math.min(avgHeight * THRESHOLD_RATIO, minHeight * 0.15);

  // Clamp to reasonable bounds
  return Math.max(MIN_THRESHOLD, Math.min(MAX_THRESHOLD, Math.round(threshold)));
}

/**
 * Vertical alignment types
 */
export type VerticalAlignment = "baseline" | "center" | "flex-start" | "flex-end" | "custom";

/**
 * Horizontal alignment types (for justify-content)
 */
export type HorizontalAlignment = "flex-start" | "center" | "flex-end" | "space-between";

/**
 * Alignment info for a group of elements
 */
export interface AlignmentInfo {
  // Vertical alignment for align-items
  vertical: VerticalAlignment;
  // Horizontal alignment for justify-content
  horizontal: HorizontalAlignment;
  // For custom alignment, these are the offsets needed
  offsets: Map<string, { top: number; bottom: number }>;
  // Reference line position (baseline Y, center Y, etc.)
  referenceLine: number;
}

/**
 * Container bounds calculated from elements
 */
export interface ContainerBounds {
  x: number;
  y: number;
  width: number;
  height: number;
  // Padding from edges to content
  contentPadding: {
    top: number;
    right: number;
    bottom: number;
    left: number;
  };
}

/**
 * Element position relative to container
 */
export interface ElementRelativePosition {
  elementId: string;
  // Position relative to container (for absolute positioning)
  relativeX: number;
  relativeY: number;
  // CSS positioning recommendation
  cssPosition: "static" | "relative" | "absolute";
  // If relative, any margin/padding adjustments needed
  adjustments: {
    marginTop?: number;
    marginBottom?: number;
    paddingTop?: number;
    paddingBottom?: number;
  };
}

/**
 * Get the bottom edge (baseline approximation) of an element
 */
function getBaseline(el: CanvasElement): number {
  return el.position.y + el.size.height;
}

/**
 * Get the vertical center of an element
 */
function getVerticalCenter(el: CanvasElement): number {
  return el.position.y + el.size.height / 2;
}

/**
 * Get the top edge of an element
 */
function getTop(el: CanvasElement): number {
  return el.position.y;
}

/**
 * Analyze vertical alignment of elements in a row
 */
export function analyzeRowAlignment(elements: CanvasElement[], containerBounds?: ContainerBounds): AlignmentInfo {
  if (elements.length <= 1) {
    // For single element, determine horizontal position relative to container
    const horizontal = analyzeHorizontalAlignment(elements, containerBounds);
    return {
      vertical: "baseline",
      horizontal,
      offsets: new Map(),
      referenceLine: elements[0]?.position.y ?? 0,
    };
  }

  // Get reference points for all elements
  const baselines = elements.map((el) => ({ id: el.id, value: getBaseline(el) }));
  const centers = elements.map((el) => ({
    id: el.id,
    value: getVerticalCenter(el),
  }));
  const tops = elements.map((el) => ({ id: el.id, value: getTop(el) }));
  const heights = elements.map((el) => el.size.height);

  // Determine vertical alignment
  let vertical: VerticalAlignment = "baseline";
  let referenceLine = 0;
  const offsets = new Map<string, { top: number; bottom: number }>();

  // Calculate alignment scores
  const avgBaseline = baselines.reduce((sum, b) => sum + b.value, 0) / baselines.length;
  const avgCenter = centers.reduce((sum, c) => sum + c.value, 0) / centers.length;
  const avgTop = tops.reduce((sum, t) => sum + t.value, 0) / tops.length;

  const maxBaselineDiff = Math.max(...baselines.map((b) => Math.abs(b.value - avgBaseline)));
  const maxCenterDiff = Math.max(...centers.map((c) => Math.abs(c.value - avgCenter)));
  const maxTopDiff = Math.max(...tops.map((t) => Math.abs(t.value - avgTop)));

  // Check if elements have significantly different heights
  const minHeight = Math.min(...heights);
  const maxHeight = Math.max(...heights);
  const heightDiffRatio = minHeight > 0 ? (maxHeight - minHeight) / maxHeight : 0;
  const hasSignificantHeightDiff = heightDiffRatio > HEIGHT_DIFFERENCE_THRESHOLD;

  // Calculate dynamic threshold based on element sizes
  // Smaller elements need tighter thresholds for accurate alignment detection
  const dynamicThreshold = calculateDynamicThreshold(heights);

  // Use SAME threshold for all alignment types - no special treatment for center
  const alignmentThreshold = dynamicThreshold;

  // Determine best alignment by finding which alignment has the SMALLEST deviation
  // This ensures we pick the alignment the user actually intended

  const baselineAligned = maxBaselineDiff < alignmentThreshold;
  const centerAligned = maxCenterDiff < alignmentThreshold;
  const topAligned = maxTopDiff < alignmentThreshold;

  // Find the alignment with minimum deviation (closest match wins)
  const alignmentScores = [
    { type: "baseline" as const, diff: maxBaselineDiff, aligned: baselineAligned, ref: avgBaseline },
    { type: "center" as const, diff: maxCenterDiff, aligned: centerAligned, ref: avgCenter },
    { type: "flex-start" as const, diff: maxTopDiff, aligned: topAligned, ref: avgTop },
  ];

  // Sort by deviation - smallest first
  alignmentScores.sort((a, b) => a.diff - b.diff);

  // Special case: elements with significantly different heights should prefer center
  // only if center is actually aligned (within threshold)
  if (hasSignificantHeightDiff && centerAligned) {
    vertical = "center";
    referenceLine = avgCenter;
  } else {
    // Pick the alignment with smallest deviation that's within threshold
    const bestMatch = alignmentScores.find((s) => s.aligned);

    if (bestMatch) {
      vertical = bestMatch.type;
      referenceLine = bestMatch.ref;
    } else {
      // Custom alignment - calculate offsets from a center-aligned layout
      vertical = "custom";
      referenceLine = avgCenter;

      elements.forEach((el) => {
        const elCenter = getVerticalCenter(el);
        const offset = elCenter - referenceLine;

        if (Math.abs(offset) > alignmentThreshold) {
          if (offset > 0) {
            offsets.set(el.id, { top: Math.round(offset), bottom: 0 });
          } else {
            offsets.set(el.id, { top: 0, bottom: Math.round(Math.abs(offset)) });
          }
        }
      });
    }
  }

  // Determine horizontal alignment based on element positions relative to container
  const horizontal = analyzeHorizontalAlignment(elements, containerBounds);

  return {
    vertical,
    horizontal,
    offsets,
    referenceLine,
  };
}

/**
 * Analyze horizontal alignment of elements within a row
 * Determines justify-content value based on element positions relative to container
 *
 * For single elements: detect if positioned left, center, or right within container
 * For multiple elements: detect if spread apart (space-between) or grouped together
 */
function analyzeHorizontalAlignment(elements: CanvasElement[], containerBounds?: ContainerBounds): HorizontalAlignment {
  if (elements.length === 0) return "flex-start";

  // Sort elements by X position
  const sortedElements = [...elements].sort((a, b) => a.position.x - b.position.x);

  // Calculate row bounds
  const rowLeft = sortedElements[0].position.x;
  const rowRight = sortedElements[sortedElements.length - 1].position.x + sortedElements[sortedElements.length - 1].size.width;
  const rowWidth = rowRight - rowLeft;

  // If no container bounds, can't determine position - default to flex-start
  if (!containerBounds || containerBounds.width <= 0) {
    return "flex-start";
  }

  const containerLeft = containerBounds.x;
  const containerRight = containerBounds.x + containerBounds.width;
  const containerWidth = containerBounds.width;

  // Calculate margins from container edges
  const leftMargin = rowLeft - containerLeft;
  const rightMargin = containerRight - rowRight;

  // For multiple elements, check for space-between layout first
  if (elements.length >= 2) {
    const totalContentWidth = sortedElements.reduce((sum, el) => sum + el.size.width, 0);
    const totalGapSpace = rowWidth - totalContentWidth;

    // If gaps are significant relative to content, use space-between
    if (totalGapSpace > totalContentWidth * 0.5) {
      return "space-between";
    }
  }

  // Determine position relative to container using threshold
  const threshold = containerWidth * HORIZONTAL_POSITION_THRESHOLD;

  // Check if clearly on the right (small right margin, significant left margin)
  if (rightMargin < threshold && leftMargin > threshold) {
    return "flex-end";
  }

  // Check if clearly centered (similar margins on both sides)
  if (Math.abs(leftMargin - rightMargin) < threshold) {
    return "center";
  }

  // Check if clearly on the left (small left margin, significant right margin)
  if (leftMargin < threshold && rightMargin > threshold) {
    return "flex-start";
  }

  // Default to flex-start
  return "flex-start";
}

/**
 * Calculate container bounds from elements
 * This represents the oppb-price-container bounds
 */
export function calculateContainerBounds(elements: CanvasElement[], padding: number = 0): ContainerBounds {
  if (elements.length === 0) {
    return {
      x: 0,
      y: 0,
      width: 100,
      height: 40,
      contentPadding: { top: 0, right: 0, bottom: 0, left: 0 },
    };
  }

  const minX = Math.min(...elements.map((el) => el.position.x));
  const minY = Math.min(...elements.map((el) => el.position.y));
  const maxX = Math.max(...elements.map((el) => el.position.x + el.size.width));
  const maxY = Math.max(...elements.map((el) => el.position.y + el.size.height));

  return {
    x: minX - padding,
    y: minY - padding,
    width: maxX - minX + padding * 2,
    height: maxY - minY + padding * 2,
    contentPadding: {
      top: padding,
      right: padding,
      bottom: padding,
      left: padding,
    },
  };
}

/**
 * Calculate element position relative to container
 * Used for absolute positioning
 */
export function calculateRelativePosition(element: CanvasElement, containerBounds: ContainerBounds): ElementRelativePosition {
  const relativeX = element.position.x - containerBounds.x;
  const relativeY = element.position.y - containerBounds.y;

  if (element.positionType === "absolute") {
    return {
      elementId: element.id,
      relativeX,
      relativeY,
      cssPosition: "absolute",
      adjustments: {},
    };
  }

  // For relative elements, calculate if adjustments are needed
  return {
    elementId: element.id,
    relativeX,
    relativeY,
    cssPosition: "static",
    adjustments: {},
  };
}

/**
 * Detect gaps between elements in a row
 * Returns array of gap sizes in order
 */
export function detectGaps(elements: CanvasElement[]): number[] {
  if (elements.length <= 1) return [];

  // Sort by x position
  const sorted = [...elements].sort((a, b) => a.position.x - b.position.x);
  const gaps: number[] = [];

  for (let i = 1; i < sorted.length; i++) {
    const prevRight = sorted[i - 1].position.x + sorted[i - 1].size.width;
    const currLeft = sorted[i].position.x;
    gaps.push(Math.max(0, currLeft - prevRight));
  }

  return gaps;
}

/**
 * Check if gaps are consistent (can use single gap value)
 */
export function areGapsConsistent(gaps: number[], threshold: number = 5): boolean {
  if (gaps.length === 0) return true;
  const avg = gaps.reduce((a, b) => a + b, 0) / gaps.length;
  return gaps.every((g) => Math.abs(g - avg) < threshold);
}

/**
 * Get recommended gap value for flexbox
 */
export function getRecommendedGap(gaps: number[]): number {
  if (gaps.length === 0) return 4;
  const avg = gaps.reduce((a, b) => a + b, 0) / gaps.length;
  // Round to nearest 2px for cleaner CSS
  return Math.round(avg / 2) * 2;
}

/**
 * Column group for column detection
 */
interface ColumnGroup {
  elements: CanvasElement[];
  minX: number;
  maxX: number;
  minY: number;
  maxY: number;
}

/**
 * Detect if elements form a column-based layout
 * Returns column groups if detected, null otherwise
 *
 * A column layout is detected when:
 * 1. There are distinct horizontal groups separated by a gap
 * 2. At least one group has multiple elements stacked vertically OR
 * 3. One element spans vertically against multiple elements on the other side
 */
function detectColumnLayout(elements: CanvasElement[]): ColumnGroup[] | null {
  if (elements.length < 2) return null;

  // Sort elements by X position (left edge)
  const sortedByX = [...elements].sort((a, b) => a.position.x - b.position.x);

  // Find horizontal gaps between consecutive elements (by X position)
  const horizontalGaps: Array<{ index: number; gap: number; leftEl: CanvasElement; rightEl: CanvasElement }> = [];

  for (let i = 1; i < sortedByX.length; i++) {
    const leftEl = sortedByX[i - 1];
    const rightEl = sortedByX[i];
    const leftRight = leftEl.position.x + leftEl.size.width;
    const rightLeft = rightEl.position.x;
    const gap = rightLeft - leftRight;

    // Only consider positive gaps
    if (gap > 0) {
      horizontalGaps.push({ index: i, gap, leftEl, rightEl });
    }
  }

  if (horizontalGaps.length === 0) return null;

  // Find the largest gap
  const maxGap = Math.max(...horizontalGaps.map((g) => g.gap));

  // Calculate minimum element width for threshold (instead of average)
  const minWidth = Math.min(...elements.map((el) => el.size.width));

  // Column layout requires a gap of at least 10px or 20% of minimum element width
  const minGapThreshold = Math.max(10, minWidth * 0.2);

  if (maxGap < minGapThreshold) return null;

  // Find the gap that best splits elements into columns
  const splitGap = horizontalGaps.find((g) => g.gap === maxGap);
  if (!splitGap) return null;

  // Split elements into left and right columns
  const leftElements = sortedByX.slice(0, splitGap.index);
  const rightElements = sortedByX.slice(splitGap.index);

  // Validate this is actually a column layout
  const leftYPositions = leftElements.map((el) => el.position.y);
  const rightYPositions = rightElements.map((el) => el.position.y);
  const leftHeights = leftElements.map((el) => el.size.height);
  const rightHeights = rightElements.map((el) => el.size.height);

  // Check if elements are stacked vertically (Y positions differ significantly)
  const rightIsStacked = rightElements.length > 1 && Math.max(...rightYPositions) - Math.min(...rightYPositions) > 10;
  const leftIsStacked = leftElements.length > 1 && Math.max(...leftYPositions) - Math.min(...leftYPositions) > 10;

  // Check if one side has a tall element that spans against multiple elements on the other
  const leftIsTall = leftElements.length === 1 && rightElements.length > 1 && leftHeights[0] > Math.max(...rightHeights) * 0.8;
  const rightIsTall = rightElements.length === 1 && leftElements.length > 1 && rightHeights[0] > Math.max(...leftHeights) * 0.8;

  // Determine if this qualifies as a column layout:
  // 1. At least one side has stacked elements
  // 2. OR one side has a single element and the other has multiple
  // 3. OR one side has a tall element spanning against the other
  const isColumnLayout =
    rightIsStacked ||
    leftIsStacked ||
    leftIsTall ||
    rightIsTall ||
    (leftElements.length === 1 && rightElements.length > 1) ||
    (rightElements.length === 1 && leftElements.length > 1);

  if (!isColumnLayout) return null;

  // Build column groups
  const columns: ColumnGroup[] = [];

  if (leftElements.length > 0) {
    columns.push({
      elements: leftElements,
      minX: Math.min(...leftElements.map((el) => el.position.x)),
      maxX: Math.max(...leftElements.map((el) => el.position.x + el.size.width)),
      minY: Math.min(...leftElements.map((el) => el.position.y)),
      maxY: Math.max(...leftElements.map((el) => el.position.y + el.size.height)),
    });
  }

  if (rightElements.length > 0) {
    columns.push({
      elements: rightElements,
      minX: Math.min(...rightElements.map((el) => el.position.x)),
      maxX: Math.max(...rightElements.map((el) => el.position.x + el.size.width)),
      minY: Math.min(...rightElements.map((el) => el.position.y)),
      maxY: Math.max(...rightElements.map((el) => el.position.y + el.size.height)),
    });
  }

  return columns.length >= 2 ? columns : null;
}

/**
 * Calculate how columns should be vertically aligned within the container
 * Based on where each column's content sits relative to the container bounds
 */
function calculateColumnsVerticalAlignment(
  columnGroups: ColumnGroup[],
  containerBounds: ContainerBounds
): "flex-start" | "center" | "flex-end" {
  if (columnGroups.length === 0) return "flex-start";

  const containerTop = containerBounds.y;
  const containerBottom = containerBounds.y + containerBounds.height;
  const containerHeight = containerBounds.height;

  // Calculate average top and bottom margins for all columns
  let totalTopMargin = 0;
  let totalBottomMargin = 0;

  columnGroups.forEach((col) => {
    totalTopMargin += col.minY - containerTop;
    totalBottomMargin += containerBottom - col.maxY;
  });

  const avgTopMargin = totalTopMargin / columnGroups.length;
  const avgBottomMargin = totalBottomMargin / columnGroups.length;

  // Threshold for alignment detection (10% of container height)
  const threshold = containerHeight * 0.1;

  // Check alignment based on margins
  // flex-end: content is at the bottom (large top margin, small bottom margin)
  if (avgBottomMargin < threshold && avgTopMargin > threshold) {
    return "flex-end";
  }

  // flex-start: content is at the top (small top margin, large bottom margin)
  if (avgTopMargin < threshold && avgBottomMargin > threshold) {
    return "flex-start";
  }

  // center: similar margins on top and bottom
  if (Math.abs(avgTopMargin - avgBottomMargin) < threshold) {
    return "center";
  }

  // Default to flex-start if unclear
  return "flex-start";
}

/**
 * Analyze elements within a column and group them into rows
 */
function analyzeColumnContent(
  elements: CanvasElement[],
  containerBounds: ContainerBounds
): {
  rows: LayoutAnalysis["rows"];
  verticalAlign: "flex-start" | "center" | "flex-end";
} {
  if (elements.length === 0) {
    return { rows: [], verticalAlign: "flex-start" };
  }

  if (elements.length === 1) {
    // Single element - vertical alignment will be determined at container level
    return {
      rows: [
        {
          elements,
          alignment: analyzeRowAlignment(elements, containerBounds),
          gaps: [],
          recommendedGap: 0,
        },
      ],
      verticalAlign: "flex-start", // Will be aligned at container level
    };
  }

  // Multiple elements - group into rows by Y overlap
  const sortedByY = [...elements].sort((a, b) => a.position.y - b.position.y);
  const rows: LayoutAnalysis["rows"] = [];
  let currentRow: CanvasElement[] = [sortedByY[0]];

  for (let i = 1; i < sortedByY.length; i++) {
    const el = sortedByY[i];
    const lastEl = currentRow[currentRow.length - 1];

    const elTop = el.position.y;
    const elBottom = el.position.y + el.size.height;
    const lastTop = lastEl.position.y;
    const lastBottom = lastEl.position.y + lastEl.size.height;

    const overlaps = !(elBottom < lastTop || elTop > lastBottom);

    if (overlaps) {
      currentRow.push(el);
    } else {
      currentRow.sort((a, b) => a.position.x - b.position.x);
      const gaps = detectGaps(currentRow);
      rows.push({
        elements: currentRow,
        alignment: analyzeRowAlignment(currentRow, containerBounds),
        gaps,
        recommendedGap: getRecommendedGap(gaps),
      });
      currentRow = [el];
    }
  }

  // Add final row
  if (currentRow.length > 0) {
    currentRow.sort((a, b) => a.position.x - b.position.x);
    const gaps = detectGaps(currentRow);
    rows.push({
      elements: currentRow,
      alignment: analyzeRowAlignment(currentRow, containerBounds),
      gaps,
      recommendedGap: getRecommendedGap(gaps),
    });
  }

  return { rows, verticalAlign: "flex-start" };
}

/**
 * Full layout analysis result
 */
export interface LayoutAnalysis {
  containerBounds: ContainerBounds;
  // Layout type: 'rows' = simple row-based, 'columns' = column-based grid
  layoutType: "rows" | "columns";
  // For row-based layouts
  rows: Array<{
    elements: CanvasElement[];
    alignment: AlignmentInfo;
    gaps: number[];
    recommendedGap: number;
  }>;
  // For column-based layouts
  columns: Array<{
    elements: CanvasElement[];
    // Vertical alignment within the column
    verticalAlign: "flex-start" | "center" | "flex-end";
    // Rows within this column (if multiple elements)
    rows: Array<{
      elements: CanvasElement[];
      alignment: AlignmentInfo;
      gaps: number[];
      recommendedGap: number;
    }>;
  }>;
  // Gap between columns (horizontal)
  columnGap: number;
  // Vertical alignment of columns within container (align-items for column layout)
  containerAlignItems: "flex-start" | "center" | "flex-end";
  // Recommended gap between rows (vertical gap for container)
  rowGap: number;
  absoluteElements: Array<{
    element: CanvasElement;
    relativePosition: ElementRelativePosition;
  }>;
}

/**
 * Perform full layout analysis
 */
export function analyzeLayout(elements: CanvasElement[]): LayoutAnalysis {
  const visibleElements = elements.filter((el) => el.visible);

  // Separate absolute and relative elements
  const absoluteElements = visibleElements.filter((el) => el.positionType === "absolute");
  const relativeElements = visibleElements.filter((el) => el.positionType !== "absolute");

  // Calculate container bounds from all elements
  const containerBounds = calculateContainerBounds(visibleElements, 0);

  // Process absolute elements
  const absoluteAnalysis = absoluteElements.map((el) => ({
    element: el,
    relativePosition: calculateRelativePosition(el, containerBounds),
  }));

  // Check for column-based layout first
  const columnGroups = detectColumnLayout(relativeElements);

  if (columnGroups && columnGroups.length >= 2) {
    // Column-based layout detected
    const columns: LayoutAnalysis["columns"] = columnGroups.map((group) => {
      const columnContent = analyzeColumnContent(group.elements, containerBounds);
      return {
        elements: group.elements,
        verticalAlign: columnContent.verticalAlign,
        rows: columnContent.rows,
      };
    });

    // Calculate column gap (horizontal gap between columns)
    let columnGap = 8;
    if (columnGroups.length >= 2) {
      const colGaps: number[] = [];
      for (let i = 1; i < columnGroups.length; i++) {
        const prevCol = columnGroups[i - 1];
        const currCol = columnGroups[i];
        colGaps.push(Math.max(0, currCol.minX - prevCol.maxX));
      }
      columnGap = getRecommendedGap(colGaps);
    }

    // Calculate row gap within columns
    let rowGap = 4;
    const allRowGaps: number[] = [];
    columns.forEach((col) => {
      if (col.rows.length > 1) {
        for (let i = 1; i < col.rows.length; i++) {
          const prevRow = col.rows[i - 1];
          const currRow = col.rows[i];
          const prevBottom = Math.max(...prevRow.elements.map((el) => el.position.y + el.size.height));
          const currTop = Math.min(...currRow.elements.map((el) => el.position.y));
          allRowGaps.push(Math.max(0, currTop - prevBottom));
        }
      }
    });
    if (allRowGaps.length > 0) {
      rowGap = getRecommendedGap(allRowGaps);
    }

    // Calculate container align-items based on column positions
    // Compare top/center/bottom alignment of columns relative to container
    const containerAlignItems = calculateColumnsVerticalAlignment(columnGroups, containerBounds);

    return {
      containerBounds,
      layoutType: "columns",
      rows: [], // Empty for column layouts
      columns,
      columnGap,
      containerAlignItems,
      rowGap,
      absoluteElements: absoluteAnalysis,
    };
  }

  // Row-based layout (traditional behavior)
  const rows: LayoutAnalysis["rows"] = [];

  if (relativeElements.length > 0) {
    // Sort by Y position first
    const sortedByY = [...relativeElements].sort((a, b) => a.position.y - b.position.y);

    let currentRow: CanvasElement[] = [sortedByY[0]];

    for (let i = 1; i < sortedByY.length; i++) {
      const el = sortedByY[i];
      const lastEl = currentRow[currentRow.length - 1];

      // Check if this element is in the same row (overlapping Y ranges)
      const elTop = el.position.y;
      const elBottom = el.position.y + el.size.height;
      const lastTop = lastEl.position.y;
      const lastBottom = lastEl.position.y + lastEl.size.height;

      const overlaps = !(elBottom < lastTop || elTop > lastBottom);

      if (overlaps) {
        currentRow.push(el);
      } else {
        // Sort current row by X and add
        currentRow.sort((a, b) => a.position.x - b.position.x);
        const gaps = detectGaps(currentRow);
        rows.push({
          elements: currentRow,
          alignment: analyzeRowAlignment(currentRow, containerBounds),
          gaps,
          recommendedGap: getRecommendedGap(gaps),
        });
        currentRow = [el];
      }
    }

    // Add final row
    if (currentRow.length > 0) {
      currentRow.sort((a, b) => a.position.x - b.position.x);
      const gaps = detectGaps(currentRow);
      rows.push({
        elements: currentRow,
        alignment: analyzeRowAlignment(currentRow, containerBounds),
        gaps,
        recommendedGap: getRecommendedGap(gaps),
      });
    }
  }

  // Calculate vertical gap between rows
  let rowGap = 4; // default
  if (rows.length > 1) {
    const rowGaps: number[] = [];
    for (let i = 1; i < rows.length; i++) {
      const prevRow = rows[i - 1];
      const currRow = rows[i];
      // Find bottom of previous row (max bottom of all elements)
      const prevBottom = Math.max(...prevRow.elements.map((el) => el.position.y + el.size.height));
      // Find top of current row (min top of all elements)
      const currTop = Math.min(...currRow.elements.map((el) => el.position.y));
      rowGaps.push(Math.max(0, currTop - prevBottom));
    }
    rowGap = getRecommendedGap(rowGaps);
  }

  return {
    containerBounds,
    layoutType: "rows",
    rows,
    columns: [],
    columnGap: 0,
    containerAlignItems: "flex-start",
    rowGap,
    absoluteElements: absoluteAnalysis,
  };
}
