import { createEmptyComponentModelStructure, type ComponentModelCustomField, type ComponentModelStructure, type ComponentModelStructureNode, } from '../types/inventory' // Import for internal use in this file import { sanitizeCustomFields, sanitizePieces, sanitizeProducts, sanitizeSubcomponents } from './componentStructureSanitize' import { hydrateCustomFields, hydratePieces, hydrateProducts, hydrateSubcomponents, mapComponentCustomFields, mapComponentPieces, mapComponentProducts, mapSubcomponents } from './componentStructureHydrate' // Re-export sanitize functions so existing imports continue to work export { toStringArray, extractFieldValueObject, sanitizeCustomFields, sanitizePieces, sanitizeProducts, sanitizeSubcomponents, } from './componentStructureSanitize' // Re-export hydrate functions so existing imports continue to work export { hydrateCustomFields, hydratePieces, hydrateProducts, hydrateSubcomponents, mapComponentCustomFields, mapComponentPieces, mapComponentProducts, mapSubcomponents, } from './componentStructureHydrate' export const isPlainObject = (value: unknown): value is Record => { return value !== null && typeof value === 'object' && !Array.isArray(value) } export interface ModelStructurePreview { customFields: number pieces: number products: number subcomponents: number } export const defaultStructure = (): ComponentModelStructure => ({ ...createEmptyComponentModelStructure(), }) const ensureStructureShape = (input: any): ComponentModelStructure => { const base = createEmptyComponentModelStructure() if (!isPlainObject(input)) { return base } const clone: ComponentModelStructure = { ...base, customFields: Array.isArray((input as any).customFields) ? (input as any).customFields : [], pieces: Array.isArray((input as any).pieces) ? (input as any).pieces : [], products: Array.isArray((input as any).products) ? (input as any).products : [], subcomponents: Array.isArray((input as any).subcomponents) ? (input as any).subcomponents : Array.isArray((input as any).subComponents) ? (input as any).subComponents : [], typeComposantId: typeof (input as any).typeComposantId === 'string' ? (input as any).typeComposantId : undefined, typeComposantLabel: typeof (input as any).typeComposantLabel === 'string' ? (input as any).typeComposantLabel : undefined, modelId: typeof (input as any).modelId === 'string' ? (input as any).modelId : undefined, familyCode: typeof (input as any).familyCode === 'string' ? (input as any).familyCode : undefined, alias: typeof (input as any).alias === 'string' ? (input as any).alias : undefined, } return clone } export const cloneStructure = (input: any): ComponentModelStructure => { try { const cloned = JSON.parse(JSON.stringify(input ?? defaultStructure())) return ensureStructureShape(cloned) } catch (_error) { return defaultStructure() } } export const normalizeStructureForEditor = (input: any): ComponentModelStructure => { const source = cloneStructure(input) const sanitizedCustomFields = sanitizeCustomFields(source.customFields) const customFields = sanitizedCustomFields.map((field) => { const options = Array.isArray(field.options) ? [...field.options] : [] const optionsText = options.length ? options.join('\n') : '' const defaultValue = field.defaultValue !== undefined && field.defaultValue !== null && field.defaultValue !== '' ? String(field.defaultValue) : null const copy: ComponentModelCustomField = { name: field.name, type: field.type, required: field.required, options, defaultValue, optionsText, id: field.id, customFieldId: field.customFieldId, } return copy }) const result: ComponentModelStructure = { customFields: customFields as ComponentModelCustomField[], pieces: sanitizePieces(source.pieces), products: sanitizeProducts(source.products), subcomponents: hydrateSubcomponents(source.subcomponents), } if (typeof source.typeComposantId === 'string' && source.typeComposantId.length > 0) { result.typeComposantId = source.typeComposantId } if (typeof source.typeComposantLabel === 'string' && source.typeComposantLabel.length > 0) { result.typeComposantLabel = source.typeComposantLabel } if (typeof source.modelId === 'string' && source.modelId.length > 0) { result.modelId = source.modelId } if (typeof source.familyCode === 'string' && source.familyCode.length > 0) { result.familyCode = source.familyCode } if (typeof source.alias === 'string' && source.alias.length > 0) { result.alias = source.alias } return result } export const normalizeStructureForSave = (input: any): any => { const source = cloneStructure(input) const sanitizedCustomFields = sanitizeCustomFields(source.customFields) const backendCustomFields = sanitizedCustomFields.map((field) => { const value: Record = { type: field.type, required: !!field.required, } if (field.options && field.options.length) { value.options = field.options } if (field.defaultValue !== undefined && field.defaultValue !== null && field.defaultValue !== '') { value.defaultValue = field.defaultValue } const payload: Record = { key: field.name, value, } if (field.id) { payload.id = field.id } if (field.customFieldId) { payload.customFieldId = field.customFieldId } return payload }) as any const backendPieces = sanitizePieces(source.pieces).map((piece) => { const payload: Record = {} if ((piece as any).familyCode) { payload.familyCode = (piece as any).familyCode } if (piece.typePieceId) { payload.typePieceId = piece.typePieceId } if (piece.typePieceLabel) { payload.typePieceLabel = piece.typePieceLabel } if (piece.reference) { payload.reference = piece.reference } return payload }) as any const backendProducts = sanitizeProducts(source.products).map((product) => { const payload: Record = {} if ((product as any).familyCode) { payload.familyCode = (product as any).familyCode } if (product.typeProductId) { payload.typeProductId = product.typeProductId } if (product.role) { payload.role = product.role } return payload }) as any const mapSubcomponentForSave = (subcomponent: ComponentModelStructureNode): any => { const payload: Record = {} if (subcomponent.typeComposantId) { payload.typeComposantId = subcomponent.typeComposantId } if (subcomponent.modelId) { payload.modelId = subcomponent.modelId } if (subcomponent.familyCode) { payload.familyCode = subcomponent.familyCode } if (subcomponent.alias) { payload.alias = subcomponent.alias } if (Array.isArray(subcomponent.subcomponents) && subcomponent.subcomponents.length) { payload.subcomponents = subcomponent.subcomponents.map(mapSubcomponentForSave) } return payload } const backendSubcomponents = sanitizeSubcomponents(source.subcomponents).map(mapSubcomponentForSave) as any const result: ComponentModelStructure = { customFields: backendCustomFields, pieces: backendPieces, products: backendProducts, subcomponents: backendSubcomponents, } if (typeof source.typeComposantId === 'string' && source.typeComposantId.length > 0) { (result as any).typeComposantId = source.typeComposantId } if (typeof source.typeComposantLabel === 'string' && source.typeComposantLabel.length > 0) { (result as any).typeComposantLabel = source.typeComposantLabel } if (typeof source.modelId === 'string' && source.modelId.length > 0) { (result as any).modelId = source.modelId } if (typeof source.familyCode === 'string' && source.familyCode.length > 0) { (result as any).familyCode = source.familyCode } if (typeof source.alias === 'string' && source.alias.length > 0) { (result as any).alias = source.alias } return result } export const hydrateStructureForEditor = (input: any): ComponentModelStructure => { const source = cloneStructure(input) return { customFields: hydrateCustomFields(source.customFields), pieces: hydratePieces(source.pieces), products: hydrateProducts(source.products), subcomponents: hydrateSubcomponents( Array.isArray(source.subcomponents) ? source.subcomponents : (source as any).subComponents, ), typeComposantId: source.typeComposantId ?? '', typeComposantLabel: source.typeComposantLabel ?? '', modelId: source.modelId ?? '', familyCode: source.familyCode ?? '', alias: source.alias ?? '', } } export const extractStructureFromComponent = (component: any) => { if (!component) { return defaultStructure() } const raw = { customFields: mapComponentCustomFields(component.customFields), pieces: mapComponentPieces(component.pieces), products: mapComponentProducts(component.products), subcomponents: mapSubcomponents( Array.isArray(component?.subcomponents) ? component.subcomponents : component?.subComponents, ), typeComposantId: component?.typeComposantId ?? component?.typeComposant?.id ?? '', typeComposantLabel: component?.typeComposant?.name ?? component?.typeComposantLabel ?? '', modelId: component?.modelId ?? '', familyCode: component?.familyCode ?? component?.typeComposant?.code ?? '', alias: component?.alias ?? component?.name ?? '', } return normalizeStructureForEditor(raw) } export const computeStructureStats = (structure: any): ModelStructurePreview => { if (!structure || typeof structure !== 'object') { return { customFields: 0, pieces: 0, products: 0, subcomponents: 0 } } return { customFields: Array.isArray(structure.customFields) ? structure.customFields.length : 0, pieces: Array.isArray(structure.pieces) ? structure.pieces.length : 0, products: Array.isArray(structure.products) ? structure.products.length : 0, subcomponents: Array.isArray(structure.subcomponents) ? structure.subcomponents.length : Array.isArray(structure.subComponents) ? structure.subComponents.length : 0, } } export const formatStructurePreview = (structure: any) => { const stats = computeStructureStats(structure) if (!stats.customFields && !stats.pieces && !stats.products && !stats.subcomponents) { return 'Structure vide' } const segments: string[] = [] if (stats.customFields) segments.push(`${stats.customFields} champ(s)`) if (stats.pieces) segments.push(`${stats.pieces} pièce(s)`) if (stats.products) segments.push(`${stats.products} produit(s)`) if (stats.subcomponents) segments.push(`${stats.subcomponents} sous-composant(s)`) return segments.join(' • ') }