import { autoColor } from "./color-utils.js";
/**
* Detect product groups by shared title prefix.
*
* Products are considered siblings when their titles share a common base before
* a separator, e.g.:
* "T-Shirt - Red", "T-Shirt - Blue" -> base "T-Shirt", values Red / Blue
* "Mug / Small", "Mug / Large" -> base "Mug", values Small / Large
*
* The suffix becomes the swatch label, and we auto-assign a colour when the
* suffix names a known colour. Single-product bases are ignored.
*
* @param {Array<{id, title, handle, image}>} products
* @returns {Array<{base, members: Array}>}
*/
const SEPARATORS = [" - ", " – ", " — ", " / ", " | ", ": ", ", "];
function split(title) {
for (const sep of SEPARATORS) {
const idx = title.indexOf(sep);
if (idx > 0) {
return {
base: title.slice(0, idx).trim(),
suffix: title.slice(idx + sep.length).trim(),
};
}
}
return null;
}
export function detectGroups(products) {
const byBase = new Map();
for (const p of products) {
const parts = split(p.title || "");
if (!parts || !parts.base || !parts.suffix) continue;
const key = parts.base.toLowerCase();
if (!byBase.has(key)) byBase.set(key, { base: parts.base, members: [] });
byBase.get(key).members.push({
productId: p.id,
productTitle: p.title,
handle: p.handle,
image: p.image || "",
swatchValue: parts.suffix,
swatchColor: autoColor(parts.suffix) || "",
});
}
// Only bases with 2+ products are real groups.
return [...byBase.values()]
.filter((g) => g.members.length >= 2)
.map((g) => ({ ...g, members: g.members.sort((a, b) =>
a.swatchValue.localeCompare(b.swatchValue)
) }));
}
/**
* Pick a group style. Use colour swatches only when EVERY value maps to a real
* colour (otherwise some swatches would be blank grey dots). When the values are
* words/sizes/model names (e.g. "Oxygen", "Small"), buttons read far better.
*/
export function suggestStyle(group) {
const allColored =
group.members.length > 0 && group.members.every((m) => m.swatchColor);
return allColored ? "color" : "button";
}