/**
 * Utilities for rendering bands in preview mode
 * Handles band iteration, nesting, and layout calculation
 */

/**
 * Resolve nested path in object (e.g., 'contact.phone')
 */
function resolveNestedPath(obj, path) {
  if (!path || !obj) return undefined;

  const parts = path.split('.');
  let current = obj;

  for (const part of parts) {
    if (current && typeof current === 'object' && part in current) {
      current = current[part];
    } else {
      return undefined;
    }
  }

  return current;
}

/**
 * Get data array for a band
 * @param {Object} reportData - The full report data
 * @param {string} dataSource - Path to array in reportData (e.g., 'measurements', 'items')
 * @param {Object} parentBandData - Data from parent band (for nested bands)
 * @returns {Array} - Array of data items for band iteration
 */
export function getBandDataArray(reportData, dataSource, parentBandData = null) {
  if (!dataSource) return [];

  // If inside parent band, use parent's current data
  if (parentBandData) {
    const data = resolveNestedPath(parentBandData, dataSource);
    return Array.isArray(data) ? data : [];
  }

  // Otherwise, use reportData
  if (!reportData) return [];
  const data = reportData[dataSource];
  return Array.isArray(data) ? data : [];
}

/**
 * Calculate band instances for preview mode
 * Returns array of band instances with positions and data
 * @param {Array} bands - Array of band elements
 * @param {Object} reportData - The full report data
 * @param {Array} allElements - All elements in the report
 * @param {Object} parentBandData - Data from parent band (for nested bands)
 * @param {number} yOffset - Y offset for nested bands (parent instance's Y position)
 * @param {number} parentDesignY - Design Y position of parent band (for relative positioning)
 * @returns {Array} - Array of band instances
 */
export function calculateBandInstances(bands, reportData, allElements, parentBandData = null, yOffset = null, parentDesignY = null) {
  const instances = [];

  // Get bands at current nesting level
  const currentLevelBands = bands.filter(band =>
    parentBandData ? band.parentBandId === parentBandData.parentBandId : !band.parentBandId
  );

  // Sort bands by Y position
  const sortedBands = [...currentLevelBands].sort((a, b) => a.y - b.y);

  // Track where previous band ended in both design and preview mode
  let previousBandDesignEnd = null;
  let previousBandPreviewEnd = null;
  let currentY = 0;

  for (const band of sortedBands) {
    // Calculate starting Y for this band
    if (previousBandDesignEnd === null) {
      // First band at this nesting level
      if (yOffset !== null && parentDesignY !== null) {
        // Nested band: position relative to parent
        const relativeY = band.y - parentDesignY;
        currentY = yOffset + relativeY;
      } else {
        // Top-level band: use design Y
        currentY = band.y;
      }
    } else {
      // Subsequent bands: maintain the gap between bands from design mode
      const designGap = band.y - previousBandDesignEnd - 1;
      // Start this band after the previous band's preview end + the design gap
      currentY = previousBandPreviewEnd + 1 + designGap;
    }

    const bandStartY = currentY;
    const dataArray = getBandDataArray(reportData, band.dataSource, parentBandData);

    // Get child elements for this band
    const childElements = allElements.filter(el => band.children && band.children.includes(el.id));

    // Get nested bands (bands that have this band as parent)
    const nestedBands = bands.filter(b => b.parentBandId === band.id);

    if (dataArray.length === 0) {
      // No data - render once with null data (for static bands like headers/footers)
      const bandInstance = {
        bandId: band.id,
        instanceIndex: 0,
        y: currentY,
        height: band.height,
        data: null,
        childElements: childElements,
        originalBandY: band.y,
        bandType: band.bandType
      };

      instances.push(bandInstance);

      // Handle nested bands with no data
      if (nestedBands.length > 0) {
        const nestedInstances = calculateBandInstances(
          nestedBands,
          reportData,
          allElements,
          null, // No parent data
          currentY, // Parent instance Y
          band.y // Parent design Y
        );
        instances.push(...nestedInstances);
        currentY = Math.max(currentY + band.height, ...nestedInstances.map(ni => ni.y + ni.height));
      } else {
        currentY += band.height;
      }
    } else {
      // Render once per data item
      dataArray.forEach((item, index) => {
        const bandInstance = {
          bandId: band.id,
          instanceIndex: index,
          y: currentY,
          height: band.height,
          data: item,
          childElements: childElements,
          originalBandY: band.y,
          bandType: band.bandType
        };

        instances.push(bandInstance);

        // Handle nested bands
        if (nestedBands.length > 0) {
          const nestedInstances = calculateBandInstances(
            nestedBands,
            reportData,
            allElements,
            { ...item, parentBandId: band.id }, // Pass current item as parent data
            currentY, // Parent instance Y
            band.y // Parent design Y
          );
          instances.push(...nestedInstances);
          // Update currentY to account for nested band height
          const nestedHeight = nestedInstances.reduce((max, ni) => Math.max(max, ni.y + ni.height), currentY + band.height);
          currentY = nestedHeight;
        } else {
          currentY += band.height;
        }
      });
    }

    // Track where this band ended in both design and preview mode
    previousBandDesignEnd = band.y + band.height - 1;
    previousBandPreviewEnd = currentY - 1;
  }

  return instances;
}

/**
 * Calculate total content height for preview mode
 * @param {Array} bands - Array of band elements
 * @param {Object} reportData - The full report data
 * @param {Array} allElements - All elements in the report
 * @param {number} minHeight - Minimum height (default canvas height)
 * @returns {number} - Total height in grid rows
 */
export function calculateTotalContentHeight(bands, reportData, allElements, minHeight = 66) {
  if (bands.length === 0) {
    return minHeight;
  }

  const instances = calculateBandInstances(bands, reportData, allElements);

  if (instances.length === 0) {
    return minHeight;
  }

  // Find the maximum Y + height from all instances
  const maxY = instances.reduce((max, instance) => {
    return Math.max(max, instance.y + instance.height);
  }, 0);

  // Also consider non-band elements
  const nonBandElements = allElements.filter(el => el.type !== 'band' && !bands.some(b => b.children.includes(el.id)));
  const maxElementY = nonBandElements.reduce((max, el) => {
    const bounds = el.getBounds ? el.getBounds() : { maxY: el.y };
    return Math.max(max, bounds.maxY + 1);
  }, 0);

  return Math.max(minHeight, maxY, maxElementY);
}

/**
 * Get available arrays from reportData (for ObjectInspector dropdown)
 * @param {Object} reportData - The full report data
 * @param {Object} parentBand - Parent band (for nested bands)
 * @returns {Array} - Array of { key, path } objects
 */
export function getAvailableArrays(reportData, parentBand = null) {
  const arrays = [];

  // If there's a parent band, get arrays from the parent's data structure
  if (parentBand && parentBand.dataSource) {
    const parentDataArray = reportData?.[parentBand.dataSource];
    if (Array.isArray(parentDataArray) && parentDataArray.length > 0) {
      // Look at first item in parent's array to find nested arrays
      const sampleItem = parentDataArray[0];
      findArraysInObject(sampleItem, arrays);
      return arrays;
    }
    return []; // Parent has no data
  }

  // No parent band - get top-level arrays only
  function findTopLevelArrays(obj) {
    if (!obj || typeof obj !== 'object') return;

    for (const key of Object.keys(obj)) {
      const value = obj[key];
      if (Array.isArray(value)) {
        arrays.push({ key, path: key });
      }
    }
  }

  findTopLevelArrays(reportData);
  return arrays;
}

/**
 * Helper to find arrays within an object (for nested band data sources)
 * @param {Object} obj - Object to search
 * @param {Array} arrays - Array to populate with results
 * @param {string} prefix - Path prefix for nested objects
 */
function findArraysInObject(obj, arrays, prefix = '') {
  if (!obj || typeof obj !== 'object') return;

  for (const key of Object.keys(obj)) {
    const value = obj[key];
    const path = prefix ? `${prefix}.${key}` : key;

    if (Array.isArray(value)) {
      arrays.push({ key, path });
      // Don't recurse into arrays - we only want immediate arrays
    } else if (typeof value === 'object' && value !== null) {
      // Recurse into nested objects
      findArraysInObject(value, arrays, path);
    }
  }
}
