import { z } from 'zod'; import { createBackendEntitySchema, idSchema, nonNegativeNumberSchema, positiveNumberSchema, } from '@/schemas/common'; /** * Backend-aligned Resource Flow schemas * Matches the Go backend domain.ResourceFlow struct and related types * * Uses Zod v4's composition and enum features for DRY code */ // ============================================================================ // Enum Schemas (using Zod v4's improved enum support) // ============================================================================ export const resourceDirectionSchema = z .enum(['input', 'output']) .describe('Resource flow direction'); export type ResourceDirection = z.infer; export const resourceTypeSchema = z .enum([ 'heat', 'water', 'steam', 'CO2', 'biowaste', 'cooling', 'logistics', 'materials', 'service', ]) .describe('Type of resource'); export type ResourceType = z.infer; export const precisionLevelSchema = z .enum(['rough', 'estimated', 'measured']) .describe('Precision level of measurement'); export type PrecisionLevel = z.infer; export const sourceTypeSchema = z .enum(['declared', 'device', 'calculated']) .describe('Source of the data'); export type SourceType = z.infer; export const physicalStateSchema = z .enum(['solid', 'liquid', 'gas']) .describe('Physical state of the resource'); export type PhysicalState = z.infer; // ============================================================================ // Composite Schemas // ============================================================================ export const qualitySchema = z .object({ temperature_celsius: z.number().optional().describe('Temperature in Celsius'), pressure_bar: positiveNumberSchema.optional().describe('Pressure in bar'), purity_pct: z.number().min(0).max(100).optional().describe('Purity percentage'), grade: z.string().optional().describe('Quality grade'), hazardousness: z.boolean().optional().describe('Whether resource is hazardous'), composition: z.string().optional().describe('Chemical composition'), physical_state: physicalStateSchema.optional(), }) .describe('Quality specifications'); export type Quality = z.infer; export const quantitySchema = z .object({ amount: positiveNumberSchema.describe('Amount of resource'), unit: z.string().min(1).describe('Unit of measurement'), temporal_unit: z.string().optional().describe('Temporal unit (e.g., per month)'), variability: nonNegativeNumberSchema.optional().describe('Variability in amount'), }) .describe('Quantity specifications'); export type Quantity = z.infer; export const economicDataSchema = z .object({ cost_in: nonNegativeNumberSchema.optional().describe('Input cost'), cost_out: nonNegativeNumberSchema.optional().describe('Output cost'), waste_disposal_cost: nonNegativeNumberSchema.optional().describe('Waste disposal cost'), primary_input_cost: nonNegativeNumberSchema.optional().describe('Primary input cost'), transportation_cost: nonNegativeNumberSchema.optional().describe('Transportation cost'), cost_sharing_fraction: z .number() .min(0) .max(1) .optional() .describe('Cost sharing fraction (0-1)'), }) .describe('Economic data'); export type EconomicData = z.infer; export const constraintsSchema = z .object({ max_distance_km: positiveNumberSchema.optional().describe('Maximum distance in kilometers'), requires_permit: z.boolean().optional().describe('Whether permit is required'), min_quality_threshold: z.string().optional().describe('Minimum quality threshold'), regulatory_compliance: z.boolean().optional().describe('Regulatory compliance status'), }) .describe('Constraints and requirements'); export type Constraints = z.infer; export const timeRangeSchema = z .object({ start: z .string() .regex(/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/, { message: 'Time must be in HH:MM format', }) .describe('Start time (HH:MM)'), end: z .string() .regex(/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/, { message: 'Time must be in HH:MM format', }) .describe('End time (HH:MM)'), }) .describe('Time range'); export type TimeRange = z.infer; export const timeProfileSchema = z .object({ availability: z .record(z.string(), timeRangeSchema) .optional() .describe('Availability by day of week'), seasonality: z.array(z.string()).optional().describe('Months when available'), supply_pattern: z .enum(['continuous', 'batch', 'seasonal', 'on_demand', 'irregular']) .optional() .describe('Supply pattern'), }) .describe('Time profile'); export type TimeProfile = z.infer; export const serviceDetailsSchema = z .object({ domain: z .string() .optional() .describe('Service domain (maintenance, consulting, transport, inspection)'), mobility: z.enum(['on_site', 'remote']).optional().describe('Service mobility type'), capacity_h_per_month: positiveNumberSchema.optional().describe('Capacity in hours per month'), service_area_km: positiveNumberSchema.optional().describe('Service area radius in kilometers'), }) .describe('Service details'); export type ServiceDetails = z.infer; // ============================================================================ // Main Resource Flow Schema // ============================================================================ /** * Backend Resource Flow schema * Uses Zod v4's composition features for DRY code */ export const backendResourceFlowSchema = createBackendEntitySchema({ OrganizationID: idSchema.describe('Organization ID'), SiteID: idSchema.describe('Site ID'), Direction: resourceDirectionSchema, Type: resourceTypeSchema, Quality: qualitySchema, Quantity: quantitySchema, TimeProfile: timeProfileSchema.optional(), EconomicData: economicDataSchema.optional(), Constraints: constraintsSchema.optional(), ServiceDetails: serviceDetailsSchema.optional(), PrecisionLevel: precisionLevelSchema.optional(), SourceType: sourceTypeSchema.optional(), DeviceSignature: z.string().optional().describe('Device signature for IoT data'), }); export type BackendResourceFlow = z.infer; /** * Request schema for creating a resource flow * Uses Zod v4's composition for consistency */ export const createResourceFlowRequestSchema = z.object({ organization_id: idSchema.describe('Organization ID'), site_id: idSchema.describe('Site ID'), direction: resourceDirectionSchema, type: resourceTypeSchema, quality: qualitySchema.optional(), quantity: quantitySchema, time_profile: timeProfileSchema.optional(), economic_data: economicDataSchema.optional(), constraints: constraintsSchema.optional(), service_details: serviceDetailsSchema.optional(), precision_level: precisionLevelSchema.optional(), source_type: sourceTypeSchema.optional(), }); export type CreateResourceFlowRequest = z.infer;