import type { AiCadWorkspace } from "@/lib/db/schema"; type CadGenerationProvider = "kittycad-cad" | "tripo-3d"; const FINAL_TASK_STATUS = new Set([ "success", "failed", "banned", "expired", "cancelled", "unknown", ]); const DEFAULT_VIEWER_SETTINGS = { cameraPreset: "iso", envPreset: "studio", lightIntensity: 1.1, autoRotate: true, showGrid: true, wireframe: false, } as const; type WorkspaceVersionLike = { status?: unknown; prompt?: unknown; }; export type UpdateAiCadWorkspaceRequest = { name?: string; prompt?: string; generationProvider?: string; modelVersion?: string; viewerSettings?: unknown; messages?: unknown; versions?: unknown; activeVersionId?: string | null; }; function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null; } function getWorkspaceVersions(value: unknown): WorkspaceVersionLike[] { if (!Array.isArray(value)) { return []; } return value.filter((item): item is WorkspaceVersionLike => isRecord(item)); } export function normalizeViewerSettings( value: unknown ): Record { if (!isRecord(value)) { return { ...DEFAULT_VIEWER_SETTINGS }; } return { cameraPreset: value.cameraPreset === "top" || value.cameraPreset === "front" || value.cameraPreset === "right" || value.cameraPreset === "iso" ? value.cameraPreset : DEFAULT_VIEWER_SETTINGS.cameraPreset, envPreset: typeof value.envPreset === "string" && value.envPreset.trim() ? value.envPreset : DEFAULT_VIEWER_SETTINGS.envPreset, lightIntensity: typeof value.lightIntensity === "number" && Number.isFinite(value.lightIntensity) ? value.lightIntensity : DEFAULT_VIEWER_SETTINGS.lightIntensity, autoRotate: typeof value.autoRotate === "boolean" ? value.autoRotate : DEFAULT_VIEWER_SETTINGS.autoRotate, showGrid: typeof value.showGrid === "boolean" ? value.showGrid : DEFAULT_VIEWER_SETTINGS.showGrid, wireframe: typeof value.wireframe === "boolean" ? value.wireframe : DEFAULT_VIEWER_SETTINGS.wireframe, }; } export function normalizeJsonRecordArray( value: unknown ): Record[] { if (!Array.isArray(value)) { return []; } return value.filter((item): item is Record => isRecord(item) ); } export function normalizeWorkspaceName( value: unknown, fallbackName = "AI-CAD 工作台" ) { return typeof value === "string" && value.trim() ? value.trim() : fallbackName; } export function normalizeWorkspacePayload(body: UpdateAiCadWorkspaceRequest): { name?: string; prompt: string; generationProvider: CadGenerationProvider; modelVersion: string; viewerSettings: Record; messages: Record[]; versions: Record[]; activeVersionId: string | null; } { return { ...(typeof body.name === "string" && body.name.trim() ? { name: body.name.trim() } : {}), prompt: String(body.prompt || ""), generationProvider: body.generationProvider === "tripo-3d" ? "tripo-3d" : "kittycad-cad", modelVersion: typeof body.modelVersion === "string" && body.modelVersion.trim() ? body.modelVersion : "Turbo-v1.0-20250506", viewerSettings: normalizeViewerSettings(body.viewerSettings), messages: normalizeJsonRecordArray(body.messages), versions: normalizeJsonRecordArray(body.versions), activeVersionId: typeof body.activeVersionId === "string" && body.activeVersionId.trim() ? body.activeVersionId : null, }; } export function buildDefaultWorkspaceName(index: number) { return index > 1 ? `AI-CAD 工作台 ${index}` : "AI-CAD 工作台"; } export function createWorkspaceSummary(workspace: AiCadWorkspace) { const versions = getWorkspaceVersions(workspace.versions); const versionCount = versions.length; const completedCount = versions.filter( (version) => version.status === "success" ).length; const hasPendingTasks = versions.some( (version) => typeof version.status !== "string" || !FINAL_TASK_STATUS.has(version.status) ); const latestPrompt = typeof workspace.prompt === "string" && workspace.prompt.trim() ? workspace.prompt.trim() : versions.find( (version) => typeof version.prompt === "string" && version.prompt.trim() )?.prompt || null; return { id: workspace.id, name: workspace.name, generationProvider: workspace.generationProvider, versionCount, completedCount, hasPendingTasks, latestPrompt: typeof latestPrompt === "string" && latestPrompt.trim() ? latestPrompt.trim() : null, createdAt: workspace.createdAt?.toISOString() || null, updatedAt: workspace.updatedAt?.toISOString() || null, }; } export function createWorkspaceDocument(workspace: AiCadWorkspace) { return { id: workspace.id, name: workspace.name, prompt: workspace.prompt, generationProvider: workspace.generationProvider, modelVersion: workspace.modelVersion, viewerSettings: workspace.viewerSettings, messages: workspace.messages, versions: workspace.versions, activeVersionId: workspace.activeVersionId, createdAt: workspace.createdAt?.toISOString() || null, updatedAt: workspace.updatedAt?.toISOString() || null, }; }