import type { ProductPlanningPlatform } from "./types"; const HEADER_SANITIZE_PATTERN = /[\s_\-./\\()[\]{}::,,;;'"`~!@#$%^&*+|?<>]/g; export function normalizeHeader(value: unknown) { return String(value || "") .replace(/^\ufeff/, "") .trim() .toLowerCase() .replace(HEADER_SANITIZE_PATTERN, ""); } export function normalizeText(value: unknown) { return String(value || "").replace(/^\ufeff/, "").trim(); } export function parseRangeValue(value: unknown): number | null { if (value === null || value === undefined || value === "") { return null; } const text = String(value).trim(); if (!text) return null; const rangeMatch = text.match( /^(\d+(?:\.\d+)?)\s*[-~~至到]\s*(\d+(?:\.\d+)?)$/ ); if (rangeMatch) { return Math.min(Number.parseFloat(rangeMatch[1]), Number.parseFloat(rangeMatch[2])); } const cleaned = text.replace(/[,,%%\s]/g, ""); const parsed = Number.parseFloat(cleaned); return Number.isFinite(parsed) ? parsed : null; } export function parseInteger(value: unknown, fallback = 0) { const parsed = Number.parseInt(String(value || "").trim(), 10); return Number.isFinite(parsed) ? parsed : fallback; } export function ensureHttpsUrl(value: unknown) { const text = String(value || "").trim(); if (!text) return ""; if (text.startsWith("//")) return `https:${text}`; if (/^https?:\/\//i.test(text)) return text; return `https://${text}`; } export function splitUrlCell(value: unknown) { const text = String(value || "").trim(); if (!text) return [] as string[]; return text .split(/[\n,,;;\s]+/) .map((item) => ensureHttpsUrl(item)) .filter((item) => /^https?:\/\//i.test(item)); } export function extractTaobaoItemId(value: string) { const input = String(value || "").trim(); if (!input) return null; if (/^\d+$/.test(input)) return input; const patterns = [ /item\.taobao\.com\/item\.htm.*?[?&]id=(\d+)/i, /detail\.tmall\.com\/item\.htm.*?[?&]id=(\d+)/i, /a\.m\.taobao\.com\/i(\d+)/i, /[?&]id=(\d+)/i, /(\d{10,})/, ]; for (const pattern of patterns) { const matched = input.match(pattern); if (matched?.[1]) { return matched[1]; } } return null; } export function extractJdItemId(value: string) { const input = String(value || "").trim(); if (!input) return null; const explicitPrefix = input.match(/^jd[\s::]+(\d{6,})$/i); if (explicitPrefix?.[1]) { return explicitPrefix[1]; } const patterns = [ /item\.jd\.com\/(\d+)\.html/i, /sku=(\d+)/i, /\/product\/(\d+)\.html/i, /[?&]sku=(\d+)/i, ]; for (const pattern of patterns) { const matched = input.match(pattern); if (matched?.[1]) { return matched[1]; } } return null; } export function parseMarketplaceItemReference(value: string): { platform: ProductPlanningPlatform; itemId: string; } | null { const input = String(value || "").trim(); if (!input) return null; const jdItemId = extractJdItemId(input); if (jdItemId) { return { platform: "jd", itemId: jdItemId, }; } const taobaoItemId = extractTaobaoItemId(input); if (taobaoItemId) { return { platform: "taobao", itemId: taobaoItemId, }; } return null; } export function uniqueBy(items: T[], getKey: (item: T) => string) { const seen = new Set(); const result: T[] = []; for (const item of items) { const key = getKey(item); if (!key || seen.has(key)) continue; seen.add(key); result.push(item); } return result; } export function truncateText(value: unknown, maxLength: number) { const text = normalizeText(value); if (text.length <= maxLength) return text; return `${text.slice(0, maxLength)}...`; } export function parseOfficialFlag(value: unknown) { const text = normalizeText(value).toLowerCase(); return ["true", "1", "yes", "y", "是", "官方"].includes(text); }