import {
  createTextBlock,
  clearLegacyAutoRowHeights,
  DEFAULT_TEXT_BLOCK_HEIGHT,
  MAX_TEXT_BLOCKS,
  MIN_TEXT_BLOCKS,
  normalizeTextBlocks,
  reindexTextBlocks,
  removeTextBlock,
  textBlocksToLegacyFields,
  updateTextBlock,
} from "./textBlocks.js";
import {
  ensureBlockIconsForRows,
  inferBlockLogoCounts,
  reindexBlockIconsAfterRowRemove,
} from "./blockIcons.js";
import {
  MIN_ROW_BAND_HEIGHT_PX,
  MIN_ROW_HEIGHT_PX,
  PAGE_CONTENT_HEIGHT_PX,
  PAGE_VIEW_HEIGHT_PX,
  ROW_BORDER_PX,
  ROW_TEXT_CHROME_PX,
} from "./documentPageMetrics.js";

export {
  PAGE_CONTENT_HEIGHT_PX,
  PAGE_VIEW_HEIGHT_PX,
  MIN_ROW_HEIGHT_PX,
  ROW_BORDER_PX,
  ROW_TEXT_CHROME_PX,
};
export const DEFAULT_ROW_COUNT = 1;

export function rowCountFromPage(pageForm) {
  return normalizeTextBlocks(pageForm).length;
}

export function rowHeightPx(block) {
  const h = Number(block?.height_px);
  return Number.isFinite(h) && h >= MIN_ROW_HEIGHT_PX ? h : DEFAULT_TEXT_BLOCK_HEIGHT;
}

function floorRowBandHeights(heights) {
  return (heights || []).map((height) =>
    Math.max(MIN_ROW_BAND_HEIGHT_PX, Math.max(1, Math.round(Number(height) || 0)))
  );
}

/**
 * Row heights for editor + submission preview (shared coordinate space).
 * Each band is at least MIN_ROW_BAND_HEIGHT_PX so picture rows stay roomy.
 */
export function resolvePreviewRowHeightsPx(textBlocks, rowBorderPx = 2) {
  const blocks = normalizeTextBlocks({ text_blocks: textBlocks });
  const count = blocks.length;
  if (!count) return [];

  const borders = Math.max(0, count - 1) * rowBorderPx;
  const budget = PAGE_CONTENT_HEIGHT_PX - borders;
  const stored = blocks.map((block) => rowHeightPx(block));
  const hasStoredHeights = stored.some((height) => height > 0);

  let resolved;

  if (!hasStoredHeights) {
    const each = Math.floor(budget / count);
    resolved = blocks.map(() => each);
  } else {
    const fixedTotal = stored.reduce((sum, height) => sum + height, 0);
    const autoRowIndexes = stored
      .map((height, index) => (height > 0 ? -1 : index))
      .filter((index) => index >= 0);

    if (!autoRowIndexes.length) {
      if (fixedTotal <= budget) {
        const extra = budget - fixedTotal;
        resolved = stored.map((height, index) =>
          index === count - 1 ? height + extra : height
        );
      } else {
        const scale = budget / fixedTotal;
        resolved = stored.map((height) => Math.max(1, Math.round(height * scale)));
      }
    } else {
      const autoHeight = Math.max(
        1,
        Math.floor((budget - fixedTotal) / autoRowIndexes.length)
      );
      resolved = stored.map((height) => (height > 0 ? height : autoHeight));
    }
  }

  return floorRowBandHeights(resolved);
}

/** Page body height: grows when row bands need more than the default A4 budget. */
export function resolvePageContentHeightPx(textBlocks, rowBorderPx = 2) {
  const rowHeights = resolvePreviewRowHeightsPx(textBlocks, rowBorderPx);
  if (!rowHeights.length) return PAGE_CONTENT_HEIGHT_PX;
  const borders = Math.max(0, rowHeights.length - 1) * rowBorderPx;
  const total = rowHeights.reduce((sum, height) => sum + height, 0) + borders;
  return Math.max(PAGE_CONTENT_HEIGHT_PX, total);
}

export function totalContentHeightPx(textBlocks) {
  const blocks = Array.isArray(textBlocks) ? textBlocks : [];
  if (!blocks.length) return 0;
  const rowHeights = blocks.reduce((sum, block) => sum + rowHeightPx(block), 0);
  const borders = (blocks.length - 1) * ROW_BORDER_PX;
  return rowHeights + borders;
}

export function remainingHeightPx(textBlocks) {
  return PAGE_CONTENT_HEIGHT_PX - totalContentHeightPx(textBlocks);
}

export function maxHeightForRow(textBlocks, rowIndex) {
  const blocks = normalizeTextBlocks({ text_blocks: textBlocks });
  if (rowIndex < 0 || rowIndex >= blocks.length) return MIN_ROW_HEIGHT_PX;
  return Number.POSITIVE_INFINITY;
}

export function clampRowHeight(textBlocks, rowIndex, requestedHeight) {
  const h = Number(requestedHeight);
  const requested = Number.isFinite(h) && h >= MIN_ROW_HEIGHT_PX ? h : DEFAULT_TEXT_BLOCK_HEIGHT;
  return requested;
}

function heightCostForNewRow(existingRowCount) {
  if (existingRowCount <= 0) return MIN_ROW_HEIGHT_PX;
  return MIN_ROW_HEIGHT_PX + ROW_BORDER_PX;
}

export function heightForNewRow() {
  return MIN_ROW_HEIGHT_PX;
}

export function canAddPageRow(pageForm) {
  const blocks = normalizeTextBlocks(pageForm);
  return blocks.length < MAX_TEXT_BLOCKS;
}

export function syncPageRowAssets(pageForm) {
  const text_blocks = clearLegacyAutoRowHeights(normalizeTextBlocks(pageForm));
  const rowCount = text_blocks.length;
  const block_icons = ensureBlockIconsForRows(pageForm.block_icons, rowCount);
  return {
    ...pageForm,
    text_blocks,
    block_icons,
    block_logo_counts: inferBlockLogoCounts(
      block_icons,
      rowCount,
      pageForm.block_logo_counts
    ),
    image_slots: pageForm.image_slots || {},
    picture_canvas: pageForm.picture_canvas || [],
    ...textBlocksToLegacyFields(text_blocks),
  };
}

export function addPageRow(pageForm) {
  const current = normalizeTextBlocks(pageForm);
  if (!canAddPageRow(pageForm)) return pageForm;

  const newHeight = heightForNewRow(pageForm);
  const text_blocks = reindexTextBlocks([
    ...current,
    createTextBlock(current.length + 1, "", newHeight),
  ]);

  return syncPageRowAssets({
    ...pageForm,
    text_blocks,
  });
}

export function removePageRow(pageForm, rowIndex) {
  const current = normalizeTextBlocks(pageForm);
  if (current.length <= MIN_TEXT_BLOCKS) return pageForm;

  const text_blocks = removeTextBlock(current, rowIndex);
  const block_icons = reindexBlockIconsAfterRowRemove(
    pageForm.block_icons || {},
    rowIndex + 1
  );
  const block_logo_counts = {};
  const keys = Object.keys(pageForm.block_logo_counts || {})
    .map(Number)
    .filter((k) => k >= 1)
    .sort((a, b) => a - b);
  let nextIndex = 1;
  for (const key of keys) {
    if (key === rowIndex + 1) continue;
    block_logo_counts[String(nextIndex)] =
      pageForm.block_logo_counts?.[String(key)] || 1;
    nextIndex += 1;
  }

  return syncPageRowAssets({
    ...pageForm,
    text_blocks,
    block_icons,
    block_logo_counts,
  });
}

export function updatePageRowText(pageForm, rowIndex, patch) {
  const text_blocks = normalizeTextBlocks(pageForm);
  const nextPatch = { ...patch };
  if (nextPatch.height_px != null) {
    nextPatch.height_px = clampRowHeight(text_blocks, rowIndex, nextPatch.height_px);
  }
  const updated = updateTextBlock(text_blocks, rowIndex, nextPatch);
  return {
    ...pageForm,
    text_blocks: updated,
    ...textBlocksToLegacyFields(updated),
  };
}

export function isPageContentWithinHeightBudget(textBlocks) {
  return totalContentHeightPx(normalizeTextBlocks({ text_blocks: textBlocks })) <= PAGE_CONTENT_HEIGHT_PX;
}

export function remainingHeightPercent(textBlocks) {
  const remaining = remainingHeightPx(textBlocks);
  return Math.max(0, Math.round((remaining / PAGE_CONTENT_HEIGHT_PX) * 100));
}

/** DOM id for a scroll-page section (matches DocumentFormPage pageSectionId). */
export function pageFormSectionId(pageForm) {
  return pageForm.page_id != null
    ? `doc-page-section-page-${pageForm.page_id}`
    : `doc-page-section-${pageForm._localId || pageForm.page_num}`;
}

/**
 * Read measured textarea heights from the live form before save (covers Save without blur).
 * @param {object[]} pageForms
 */
export function applyDomTextBlockHeightsToPageForms(pageForms) {
  if (typeof document === "undefined") return pageForms;

  return pageForms.map((pageForm) => {
    const section = document.getElementById(pageFormSectionId(pageForm));
    if (!section) return pageForm;

    const text_blocks = normalizeTextBlocks(pageForm);
    let changed = false;
    const updated = text_blocks.map((block, rowIndex) => {
      const textarea = section.querySelector(
        `textarea[name="block${block.block_index}"]`
      );
      if (!textarea) return block;

      const measured = Math.max(
        MIN_ROW_HEIGHT_PX,
        Math.round(textarea.getBoundingClientRect().height)
      );
      if (measured === block.height_px) return block;
      changed = true;
      return { ...block, height_px: measured };
    });

    if (!changed) return pageForm;

    return {
      ...pageForm,
      text_blocks: updated,
      ...textBlocksToLegacyFields(updated),
    };
  });
}
