/**
 * Telegram-style adaptive album layout.
 *
 * Given the aspect ratios (width / height) of the images in an album, computes
 * an absolutely-positioned mosaic: each tile gets normalized {left, top, width,
 * height} as fractions (0..1) of the album box, plus the box's overall aspect
 * ratio. Absolute positioning (rather than CSS grid `fr`) keeps the layout
 * robust — it only depends on the container's resolved size.
 *
 * Rules (mirroring the product's request):
 *  - 2 images, both portrait  -> side by side (left/right)
 *  - 2 images, both landscape  -> stacked     (top/bottom)
 *  - 2 images, mixed           -> side by side, proportional
 *  - 3+ images                 -> "primary + line": the first image is the big
 *    one; a portrait primary sits on the LEFT with the rest stacked on the
 *    right, a landscape primary sits on TOP with the rest in a row below.
 */

export type AlbumTile = {
  /** Fractions (0..1) of the album box. */
  left: number;
  top: number;
  width: number;
  height: number;
};

export type AlbumLayout = {
  /** width / height of the whole album box. */
  aspectRatio: number;
  tiles: AlbumTile[];
};

/**
 * Keep ratios within a sane range so one extreme image can't make the whole
 * mosaic absurdly tall or flat. Beyond this the cell crops (object-cover).
 */
function safeRatio(ratio: number | undefined): number {
  if (!ratio || !Number.isFinite(ratio) || ratio <= 0) return 1;
  return ratio;
}

/** Bound a ratio so one extreme image can't make a multi-image mosaic ugly. */
function clampRatio(ratio: number): number {
  return Math.min(2, Math.max(0.55, ratio));
}

function layoutTwo(ratios: number[]): AlbumLayout {
  const [r0, r1] = ratios;
  const bothLandscape = r0 >= 1 && r1 >= 1;

  if (bothLandscape) {
    // Stacked: equal width, heights follow each ratio.
    const h0 = 1 / r0;
    const h1 = 1 / r1;
    const total = h0 + h1;
    return {
      aspectRatio: 1 / total,
      tiles: [
        { left: 0, top: 0, width: 1, height: h0 / total },
        { left: 0, top: h0 / total, width: 1, height: h1 / total },
      ],
    };
  }

  // Portrait or mixed: side by side, equal height, widths follow each ratio.
  const w0 = r0 / (r0 + r1);
  return {
    aspectRatio: r0 + r1,
    tiles: [
      { left: 0, top: 0, width: w0, height: 1 },
      { left: w0, top: 0, width: 1 - w0, height: 1 },
    ],
  };
}

function layoutPrimaryPlusLine(ratios: number[]): AlbumLayout {
  const r0 = ratios[0];
  const secondary = ratios.slice(1);
  const primaryOnLeft = r0 < 1; // portrait primary -> left; landscape -> top

  if (primaryOnLeft) {
    // Total height = 1. Left column holds the primary (full height); right
    // column is split into one row per secondary image (share width `wr`).
    const invSum = secondary.reduce((acc, r) => acc + 1 / r, 0);
    const wrPx = 1 / invSum; // right-column width in box-height units
    const total = r0 + wrPx; // box width when box height = 1 (== aspectRatio)
    const wl = r0 / total;
    const wr = wrPx / total;
    const tiles: AlbumTile[] = [{ left: 0, top: 0, width: wl, height: 1 }];
    let y = 0;
    secondary.forEach((r) => {
      const h = wrPx / r; // tile height as a fraction of box height (= 1)
      tiles.push({ left: wl, top: y, width: wr, height: h });
      y += h;
    });
    return { aspectRatio: total, tiles };
  }

  // Total width = 1. Primary on top (full width); secondary share a bottom row
  // (common height `hb`, widths follow ratio).
  const sumR = secondary.reduce((acc, r) => acc + r, 0);
  const ht = 1 / r0;
  const hb = 1 / sumR;
  const total = ht + hb; // box height when width = 1
  const tiles: AlbumTile[] = [
    { left: 0, top: 0, width: 1, height: ht / total },
  ];
  let x = 0;
  secondary.forEach((r) => {
    const w = r / sumR;
    tiles.push({ left: x, top: ht / total, width: w, height: hb / total });
    x += w;
  });
  return { aspectRatio: 1 / total, tiles };
}

/**
 * Compute the mosaic for the given image ratios (already sliced to the visible
 * count, max 4). Returns null for counts this layout doesn't handle (0 or 1).
 */
export function computeAlbumLayout(
  rawRatios: (number | undefined)[],
): AlbumLayout | null {
  const ratios = rawRatios.map(safeRatio);
  if (ratios.length < 2) return null;
  // Two images keep their true ratios so each cell matches its image exactly
  // (object-cover then fits with no cropping). 3+ are clamped to keep the
  // mosaic tidy.
  if (ratios.length === 2) return layoutTwo(ratios);
  return layoutPrimaryPlusLine(ratios.map(clampRatio));
}
