const sizeOrder = ['2', '4', '6', '8', '10', '12', '14', '16', 'PP', 'P', 'M', 'G', 'GG', 'XG', 'G1', 'G2', 'G3', 'G4', 'G5']; const sizeSet = new Set(sizeOrder); const knownColors = [ 'BRANCO + PRETO', 'CINZA/PRETO', 'VERDE BANDEIRA', 'VERDE MILITAR', 'AZUL MARINHO', 'CINZA GRAFITE', 'AMARELO', 'BRANCO', 'GRAFITE', 'MARINHO', 'VERMELHO', 'BORDO', 'CAFE', 'CAQUI', 'CHUMBO', 'CINZA', 'MARROM', 'PEROLA', 'PRETO', 'ROYAL', 'ROSA', 'ROXO', 'VERDE', 'AZUL', 'BEGE' ].sort((a, b) => b.length - a.length); export const normalizeProductText = (value: string) => value.replace(/\s+/g, ' ').trim(); const normalizeForMatch = (value: string) => normalizeProductText(value) .normalize('NFD') .replace(/\p{Diacritic}/gu, '') .toUpperCase(); const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const toFlexiblePattern = (value: string) => escapeRegex(value).replace(/\s+/g, '\\s+'); const findLeadingColor = (value: string) => { const normalizedValue = normalizeForMatch(value); return knownColors.find(color => normalizedValue === color || normalizedValue.startsWith(`${color} `)) || ''; }; const findTrailingColor = (value: string) => { const normalizedValue = normalizeForMatch(value); return knownColors.find(color => normalizedValue === color || normalizedValue.endsWith(` ${color}`) || normalizedValue.endsWith(`- ${color}`)) || ''; }; export const encodeProductGroupKey = (value: string) => { const normalizedValue = normalizeProductText(value); const bytes = new TextEncoder().encode(normalizedValue); const binary = Array.from(bytes, byte => String.fromCharCode(byte)).join(''); return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); }; export const decodeProductGroupKey = (value: string) => { const base64 = value.replace(/-/g, '+').replace(/_/g, '/'); const paddedBase64 = base64.padEnd(Math.ceil(base64.length / 4) * 4, '='); const binary = atob(paddedBase64); const bytes = Uint8Array.from(binary, character => character.charCodeAt(0)); return normalizeProductText(new TextDecoder().decode(bytes)); }; export const sortProductSizes = (sizes: string[]) => [...sizes].sort((a, b) => { const indexA = sizeOrder.indexOf(a); const indexB = sizeOrder.indexOf(b); if (indexA !== -1 || indexB !== -1) { return (indexA === -1 ? Number.MAX_SAFE_INTEGER : indexA) - (indexB === -1 ? Number.MAX_SAFE_INTEGER : indexB); } return a.localeCompare(b, 'pt-BR'); }); export const parseProductName = (name: string) => { const cleanName = normalizeProductText(name); const explicitSizeMatch = cleanName.match(/\bTAMANHO\s*-?\s*([A-Z0-9]+)\b/i); const trailingTokenMatch = cleanName.match(/(?:\s+-\s+|\s)([A-Z0-9]+)$/i); const trailingToken = trailingTokenMatch?.[1]?.toUpperCase() || ''; let size = (explicitSizeMatch?.[1] || (sizeSet.has(trailingToken) ? trailingToken : '')).toUpperCase(); const colorMatch = cleanName.match(/\bCOR\s+(.+?)(?:\s+TAMANHO|\s+-\s+[A-Z0-9]+$|$)/i); let color = normalizeProductText(findLeadingColor(colorMatch?.[1] || '') || colorMatch?.[1] || ''); if (!color && size) { const nameBeforeSize = cleanName.replace(new RegExp(`(?:\\s+-\\s+|\\s)${escapeRegex(size)}$`, 'i'), ''); color = findTrailingColor(nameBeforeSize); } if (!color) { const trailingColor = findTrailingColor(cleanName); if (trailingColor) { const normalizedName = normalizeForMatch(cleanName); const normalizedPrefix = normalizeProductText(normalizedName.replace(new RegExp(`${escapeRegex(trailingColor)}$`), '')); const tokenBeforeColor = normalizedPrefix.match(/([A-Z0-9]+)$/)?.[1] || ''; if (sizeSet.has(tokenBeforeColor)) { color = trailingColor; size = tokenBeforeColor; } } } let baseName = cleanName .replace(/\bCOR\s+.+?(?:\s+TAMANHO\s*-?\s*[A-Z0-9]+|\s+-\s+[A-Z0-9]+$|$)/i, '') .replace(/\bTAMANHO\s*-?\s*[A-Z0-9]+\b/i, '') .replace(/\s+-\s*[A-Z0-9]+$/i, ''); if (color && size) { const colorPattern = toFlexiblePattern(color); const sizePattern = escapeRegex(size); baseName = baseName .replace(new RegExp(`\\s+-\\s*${colorPattern}\\s+${sizePattern}$`, 'i'), '') .replace(new RegExp(`\\s+${colorPattern}\\s+-\\s*${sizePattern}$`, 'i'), '') .replace(new RegExp(`\\s+${sizePattern}\\s+${colorPattern}$`, 'i'), '') .replace(new RegExp(`\\s+${colorPattern}\\s+${sizePattern}$`, 'i'), ''); } if (color) { const colorPattern = toFlexiblePattern(color); baseName = baseName .replace(new RegExp(`\\s+-\\s*${colorPattern}$`, 'i'), '') .replace(new RegExp(`\\s+${colorPattern}$`, 'i'), ''); } baseName = normalizeProductText(baseName.replace(/\s+-\s*$/g, '')); return { baseName: baseName || cleanName, color, size }; };