import type { AgentDetailReplacementReferenceItem, AgentModeReferenceCandidate, AgentModeWorkspaceState, AgentWorkflowMaterialQueryType, } from "@/types/agent"; export function createEmptyAgentModeWorkspaceState(): AgentModeWorkspaceState { return { detailReplacement: { query: "", queryType: "spu", candidates: [], selectedCandidateId: null, references: [], }, skuReplacement: { query: "", queryType: "sku", candidates: [], selectedCandidateId: null, references: [], }, }; } function normalizeReferenceItems(value: unknown): AgentDetailReplacementReferenceItem[] { if (!Array.isArray(value)) return []; return value .map((item, index) => { const raw = item && typeof item === "object" && !Array.isArray(item) ? (item as Record) : {}; const id = String(raw.id || "").trim() || `reference-${index + 1}`; const label = String(raw.label || "").trim() || `参考图 ${index + 1}`; const imageUrl = String(raw.imageUrl || "").trim() || null; return { id, label, imageUrl, candidateId: String(raw.candidateId || "").trim() || null, sortOrder: Number.isFinite(Number(raw.sortOrder)) ? Math.max(0, Math.min(999, Math.round(Number(raw.sortOrder)))) : index, assignedProductIndex: Number.isFinite(Number(raw.assignedProductIndex)) ? Math.max(0, Math.min(9, Math.round(Number(raw.assignedProductIndex)))) : null, }; }) .filter((item) => Boolean(item.id)); } function normalizeQueryType(value: unknown, fallback: AgentWorkflowMaterialQueryType) { return value === "sku" || value === "spu" ? value : fallback; } function normalizeCandidate(value: unknown, index: number): AgentModeReferenceCandidate | null { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } const raw = value as Record; const normalizeUrlList = (input: unknown) => Array.isArray(input) ? Array.from( new Set( input .map((item) => String(item || "").trim()) .filter((item) => /^https?:\/\//i.test(item)) ) ).slice(0, 64) : []; const id = String(raw.id || "").trim() || `candidate-${index + 1}`; return { id, materialId: String(raw.materialId || "").trim() || null, taskNo: String(raw.taskNo || "").trim() || null, productModel: String(raw.productModel || "").trim() || null, categoryName: String(raw.categoryName || "").trim() || null, shopName: String(raw.shopName || "").trim() || null, sceneName: String(raw.sceneName || "").trim() || null, uploadedAt: String(raw.uploadedAt || "").trim() || null, isFullSet: raw.isFullSet !== false, mainImageUrls: normalizeUrlList(raw.mainImageUrls), skuImageUrls: normalizeUrlList(raw.skuImageUrls), detailImageUrls: normalizeUrlList(raw.detailImageUrls), allImageUrls: normalizeUrlList(raw.allImageUrls), }; } function normalizeCandidates(value: unknown): AgentModeReferenceCandidate[] { if (!Array.isArray(value)) return []; return value .map((item, index) => normalizeCandidate(item, index)) .filter((item): item is AgentModeReferenceCandidate => Boolean(item)); } export function normalizeAgentModeWorkspaceState(value: unknown): AgentModeWorkspaceState { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; const detailRaw = raw.detailReplacement && typeof raw.detailReplacement === "object" && !Array.isArray(raw.detailReplacement) ? (raw.detailReplacement as Record) : {}; const skuRaw = raw.skuReplacement && typeof raw.skuReplacement === "object" && !Array.isArray(raw.skuReplacement) ? (raw.skuReplacement as Record) : {}; return { detailReplacement: { query: String(detailRaw.query || detailRaw.spuQuery || "").trim(), queryType: normalizeQueryType(detailRaw.queryType, "spu"), candidates: normalizeCandidates(detailRaw.candidates), selectedCandidateId: String(detailRaw.selectedCandidateId || "").trim() || null, references: normalizeReferenceItems(detailRaw.references), }, skuReplacement: { query: String(skuRaw.query || skuRaw.spuQuery || "").trim(), queryType: normalizeQueryType(skuRaw.queryType, "sku"), candidates: normalizeCandidates(skuRaw.candidates), selectedCandidateId: String(skuRaw.selectedCandidateId || "").trim() || null, references: normalizeReferenceItems(skuRaw.references), }, }; }