import type { Field } from '@/types/field' const NAME_FIELD_KEY_PATTERN = /^nameField(\d+)$/i export function isNameFieldKey(value: unknown): value is string { return typeof value === 'string' && NAME_FIELD_KEY_PATTERN.test(value) } function parseNameFieldKeyIndex(value: string): number | null { const match = NAME_FIELD_KEY_PATTERN.exec(value) return match ? parseInt(match[1], 10) : null } export function getChildFieldSettings(field: Field): Record { let settings: Record = {} if (typeof field.settings === 'string') { try { settings = JSON.parse(field.settings) as Record } catch { settings = {} } } else if (typeof field.settings === 'object' && field.settings !== null) { settings = { ...(field.settings as Record) } } if (field.nameFieldType) { settings.nameFieldType = field.nameFieldType } if (field.subFieldIndex !== undefined) { settings.subFieldIndex = field.subFieldIndex } return settings } function inferNameFieldKeyFromLabel(label: unknown): string | null { if (typeof label !== 'string' || label.trim() === '') { return null } const nameFieldMatch = /^nameField(\d+)$/i.exec(label.trim()) if (nameFieldMatch) { return `nameField${nameFieldMatch[1]}` } const labelMatch = /name\s*field\s*(\d+)/i.exec(label.trim()) if (labelMatch) { return `nameField${labelMatch[1]}` } return null } export function resolveNameFieldKey( subfield: { type?: string; label?: string }, settings?: Record, fallbackIndex?: number, ): string { if (isNameFieldKey(subfield.type)) { return subfield.type } const fromSettings = settings?.nameFieldType if (isNameFieldKey(fromSettings)) { return fromSettings } const fromLabel = inferNameFieldKeyFromLabel(subfield.label) if (fromLabel) { return fromLabel } if (fallbackIndex !== undefined) { return `nameField${fallbackIndex + 1}` } return 'nameField1' } function canonicalizeNameFieldKey(key: string): string { const index = parseNameFieldKeyIndex(key) return index !== null ? `nameField${index}` : key } function ensureUniqueNameFieldKey(key: string, usedKeys: readonly string[]): string { const normalizedKey = isNameFieldKey(key) ? canonicalizeNameFieldKey(key) : key const normalizedUsed = usedKeys.map((usedKey) => isNameFieldKey(usedKey) ? canonicalizeNameFieldKey(usedKey) : usedKey, ) if (!normalizedUsed.includes(normalizedKey)) { return normalizedKey } let candidate = 1 while (normalizedUsed.includes(`nameField${candidate}`)) { candidate++ } return `nameField${candidate}` } export function resolveUniqueNameFieldKey( subfield: { type?: string; label?: string }, settings: Record | undefined, fallbackIndex: number, usedKeys: readonly string[], ): string { const key = resolveNameFieldKey(subfield, settings, fallbackIndex) return ensureUniqueNameFieldKey(key, usedKeys) } function getSubFieldDisplayIndex(settings?: Record): number | null { const value = settings?.subFieldIndex if (typeof value === 'number' && Number.isFinite(value)) { return value } if (typeof value === 'string' && value.trim() !== '') { const parsed = Number(value) return Number.isFinite(parsed) ? parsed : null } return null } export function sortChildrenByDisplayOrder( children: T[], apiOrder: Map, ): T[] { return [...children].sort((a, b) => { const aSettings = getChildFieldSettings(a) const bSettings = getChildFieldSettings(b) const aIndex = getSubFieldDisplayIndex(aSettings) const bIndex = getSubFieldDisplayIndex(bSettings) const aHas = aIndex !== null const bHas = bIndex !== null if (aHas && bHas) return aIndex - bIndex if (aHas) return -1 if (bHas) return 1 return (apiOrder.get(a) || 0) - (apiOrder.get(b) || 0) }) } export function assignLegacyNameFieldKeysIfMissing( subfields: T[], settingsList: Array> = [], ): Array { const usedKeys: string[] = [] return subfields.map((subfield, idx) => { const type = resolveUniqueNameFieldKey(subfield, settingsList[idx], idx, usedKeys) usedKeys.push(type) return { ...subfield, type } }) }