import { ConflictException, Injectable, NotFoundException, UnprocessableEntityException, } from "@nestjs/common"; import { type AdjustInventoryDto, type BrandDto, type ComplexCategoryDto, type ComplexProductDto, ComplexProductStatus, type CreateComplexProductDto, type CursorProductQueryDto, type InventoryItemDto, type UpdateComplexProductDto, type WarehouseDto, } from "./dto/catalog.dto"; import { type ComplexOrderDto, ComplexOrderStatus, type CreateComplexOrderDto, type CreateReviewDto, type CustomerDto, type CustomerQueryDto, type OrderCursorQueryDto, type PaymentDto, type PromotionDto, type ReviewDto, } from "./dto/commerce.dto"; import { type ChatMessageDto, type ConversationDto, type SendMessageDto, } from "./dto/chat.dto"; import { ComplexRole, type ComplexUserDto, type MemberDto, type OrganizationDto, OrganizationPlan, } from "./dto/identity.dto"; import { type AuditEventDto, type AuditQueryDto, type FileMetadataDto, type JobDto, JobStatus, type NotificationDto, NotificationKind, } from "./dto/operations.dto"; interface ComplexUserRecord extends ComplexUserDto { password: string; } interface TenantNotification extends NotificationDto { organizationId: string; } interface TenantFile extends FileMetadataDto { organizationId: string; } interface TenantAuditEvent extends AuditEventDto { organizationId: string; } interface TenantJob extends JobDto { organizationId: string; startedAtMs: number; } interface TenantReview extends ReviewDto { organizationId: string; } @Injectable() export class ComplexStore { private users: ComplexUserRecord[] = []; private organizations: OrganizationDto[] = []; private members: MemberDto[] = []; private products: ComplexProductDto[] = []; private categories: ComplexCategoryDto[] = []; private brands: BrandDto[] = []; private warehouses: WarehouseDto[] = []; private inventory: InventoryItemDto[] = []; private customers: CustomerDto[] = []; private orders: ComplexOrderDto[] = []; private payments: PaymentDto[] = []; private promotions: PromotionDto[] = []; private reviews: TenantReview[] = []; private notifications: TenantNotification[] = []; private files: TenantFile[] = []; private fileContents = new Map(); private auditEvents: TenantAuditEvent[] = []; private jobs: TenantJob[] = []; private conversations: ConversationDto[] = []; private messages: ChatMessageDto[] = []; private orderIdempotency = new Map(); private sequences = { product: 20, order: 20, review: 20, file: 20, audit: 20, job: 20, message: 20, }; constructor() { this.reset("small"); } reset(preset: "small" | "large" = "small"): void { this.users = [ { id: "complex-user-admin", email: "admin@complex.demo", password: "demo1234", name: "Complex Admin", role: ComplexRole.Admin, avatarUrl: "https://i.pravatar.cc/160?img=20", organizationIds: ["org-acme", "org-globex"], }, { id: "complex-user-manager", email: "manager@complex.demo", password: "demo1234", name: "Complex Manager", role: ComplexRole.Manager, avatarUrl: "https://i.pravatar.cc/160?img=32", organizationIds: ["org-acme"], }, { id: "complex-user-support", email: "support@complex.demo", password: "demo1234", name: "Complex Support", role: ComplexRole.Support, avatarUrl: null, organizationIds: ["org-acme"], }, { id: "complex-user-viewer", email: "viewer@complex.demo", password: "demo1234", name: "Complex Viewer", role: ComplexRole.Viewer, avatarUrl: null, organizationIds: ["org-acme"], }, ]; this.organizations = [ { id: "org-acme", name: "Acme Commerce", plan: OrganizationPlan.Enterprise, timezone: "Europe/Berlin", currency: "USD", createdAt: "2025-03-01T10:00:00.000Z", }, { id: "org-globex", name: "Globex Retail", plan: OrganizationPlan.Business, timezone: "America/New_York", currency: "EUR", createdAt: "2025-08-15T14:00:00.000Z", }, ]; this.members = this.users.flatMap((user, userIndex) => user.organizationIds.map((organizationId, organizationIndex) => ({ id: `member-${userIndex + 1}-${organizationIndex + 1}`, organizationId, userId: user.id, userName: user.name, email: user.email, role: user.role, status: "active" as const, })), ); this.categories = [ { id: "complex-category-electronics", organizationId: "org-acme", name: "Electronics", parentId: null, childIds: ["complex-category-keyboards"], }, { id: "complex-category-keyboards", organizationId: "org-acme", name: "Keyboards", parentId: "complex-category-electronics", childIds: [], }, { id: "complex-category-office", organizationId: "org-acme", name: "Office", parentId: null, childIds: [], }, { id: "globex-category-home", organizationId: "org-globex", name: "Home", parentId: null, childIds: [], }, ]; this.brands = [ { id: "brand-northstar", organizationId: "org-acme", name: "Northstar", logoUrl: "https://picsum.photos/seed/northstar/160/80", }, { id: "brand-contoso", organizationId: "org-acme", name: "Contoso", logoUrl: null, }, { id: "brand-globex", organizationId: "org-globex", name: "Globex", logoUrl: null, }, ]; const baseProducts = [ this.product( "complex-product-keyboard", "org-acme", "Pro Mechanical Keyboard", "129.90", "complex-category-keyboards", "brand-northstar", ComplexProductStatus.Active, ), this.product( "complex-product-mouse", "org-acme", "Precision Mouse", "79.90", "complex-category-electronics", "brand-northstar", ComplexProductStatus.Active, ), this.product( "complex-product-desk", "org-acme", "Modular Standing Desk", "599.00", "complex-category-office", "brand-contoso", ComplexProductStatus.Draft, ), this.product( "globex-product-lamp", "org-globex", "Ambient Home Lamp", "89.00", "globex-category-home", "brand-globex", ComplexProductStatus.Active, ), ]; this.products = preset === "large" ? Array.from({ length: 250 }, (_, index) => { const source = baseProducts[index % 3]; const number = index + 1; return { ...source, id: `complex-product-${String(number).padStart(3, "0")}`, name: `${source.name} ${number}`, slug: `${source.slug}-${number}`, variants: source.variants.map((variant) => ({ ...variant, id: `${variant.id}-${number}`, sku: `${variant.sku}-${number}`, })), }; }) : baseProducts; this.warehouses = [ { id: "warehouse-berlin", organizationId: "org-acme", name: "Berlin Warehouse", countryCode: "DE", status: "active", }, { id: "warehouse-paris", organizationId: "org-acme", name: "Paris Overflow", countryCode: "FR", status: "maintenance", }, { id: "warehouse-new-york", organizationId: "org-globex", name: "New York Warehouse", countryCode: "US", status: "active", }, ]; this.inventory = [ { id: "inventory-keyboard-berlin", productId: "complex-product-keyboard", variantId: "variant-complex-product-keyboard-default", warehouseId: "warehouse-berlin", available: 42, reserved: 5, reorderPoint: 10, version: 2, }, { id: "inventory-mouse-berlin", productId: "complex-product-mouse", variantId: "variant-complex-product-mouse-default", warehouseId: "warehouse-berlin", available: 8, reserved: 2, reorderPoint: 12, version: 1, }, { id: "inventory-lamp-new-york", productId: "globex-product-lamp", variantId: "variant-globex-product-lamp-default", warehouseId: "warehouse-new-york", available: 30, reserved: 0, reorderPoint: 5, version: 1, }, ]; const baseCustomers: CustomerDto[] = [ { id: "customer-ada", organizationId: "org-acme", name: "Ada Lovelace", email: "ada@example.test", defaultAddress: { line1: "Friedrichstrasse 100", line2: null, city: "Berlin", postalCode: "10117", countryCode: "DE", }, tags: ["vip", "newsletter"], createdAt: "2026-01-10T09:00:00.000Z", }, { id: "customer-grace", organizationId: "org-acme", name: "Grace Hopper", email: "grace@example.test", defaultAddress: { line1: "1 Compiler Lane", line2: "Suite 5", city: "Paris", postalCode: "75001", countryCode: "FR", }, tags: ["b2b"], createdAt: "2026-02-12T11:30:00.000Z", }, { id: "customer-katherine", organizationId: "org-acme", name: "Katherine Johnson", email: "katherine@example.test", defaultAddress: { line1: "42 Orbit Road", line2: null, city: "London", postalCode: "SW1A 1AA", countryCode: "GB", }, tags: [], createdAt: "2026-03-15T08:00:00.000Z", }, ]; this.customers = preset === "large" ? Array.from({ length: 250 }, (_, index) => { const source = baseCustomers[index % baseCustomers.length]; return { ...source, id: `customer-${String(index + 1).padStart(3, "0")}`, name: `${source.name} ${index + 1}`, email: `customer${index + 1}@example.test`, }; }) : baseCustomers; this.orders = [ { id: "complex-order-001", organizationId: "org-acme", customerId: "customer-ada", status: ComplexOrderStatus.Paid, items: [ { productId: "complex-product-keyboard", variantId: "variant-complex-product-keyboard-default", name: "Pro Mechanical Keyboard", quantity: 1, unitPrice: { amount: "129.90", currency: "USD" }, }, ], subtotal: { amount: "129.90", currency: "USD" }, discount: { amount: "0.00", currency: "USD" }, total: { amount: "129.90", currency: "USD" }, shippingAddress: baseCustomers[0].defaultAddress, createdAt: "2026-07-20T10:00:00.000Z", version: 1, }, { id: "complex-order-002", organizationId: "org-acme", customerId: "customer-grace", status: ComplexOrderStatus.Shipped, items: [ { productId: "complex-product-mouse", variantId: "variant-complex-product-mouse-default", name: "Precision Mouse", quantity: 2, unitPrice: { amount: "79.90", currency: "USD" }, }, ], subtotal: { amount: "159.80", currency: "USD" }, discount: { amount: "15.98", currency: "USD" }, total: { amount: "143.82", currency: "USD" }, shippingAddress: baseCustomers[1].defaultAddress, createdAt: "2026-07-22T12:30:00.000Z", version: 2, }, ]; this.payments = [ { id: "payment-001", orderId: "complex-order-001", status: "succeeded", method: "card", amount: { amount: "129.90", currency: "USD" }, createdAt: "2026-07-20T10:03:00.000Z", }, { id: "payment-002", orderId: "complex-order-002", status: "succeeded", method: "bank-transfer", amount: { amount: "143.82", currency: "USD" }, createdAt: "2026-07-22T12:45:00.000Z", }, ]; this.promotions = [ { id: "promotion-welcome", code: "WELCOME10", type: "percentage", value: "10.00", validUntil: "2027-01-01T00:00:00.000Z", active: true, }, { id: "promotion-old", code: "OLD20", type: "percentage", value: "20.00", validUntil: "2025-01-01T00:00:00.000Z", active: false, }, ]; this.reviews = [ { id: "review-001", organizationId: "org-acme", productId: "complex-product-keyboard", customerId: "customer-ada", rating: 5, comment: "Excellent keyboard for daily development.", status: "published", createdAt: "2026-07-21T10:00:00.000Z", }, ]; this.notifications = [ { id: "notification-001", organizationId: "org-acme", kind: NotificationKind.Order, title: "Order shipped", payload: { type: "order", orderId: "complex-order-002", status: "shipped", }, read: false, createdAt: "2026-07-23T09:00:00.000Z", }, { id: "notification-002", organizationId: "org-acme", kind: NotificationKind.Inventory, title: "Low stock", payload: { type: "inventory", productId: "complex-product-mouse", remaining: 8, }, read: false, createdAt: "2026-07-23T08:00:00.000Z", }, { id: "notification-003", organizationId: "org-acme", kind: NotificationKind.System, title: "Maintenance", payload: { type: "system", text: "Scheduled maintenance begins at 02:00 UTC.", }, read: true, createdAt: "2026-07-22T16:00:00.000Z", }, ]; this.files = [ { id: "file-001", organizationId: "org-acme", name: "products.csv", mimeType: "text/csv", size: 45, downloadUrl: "/api/v1/files/file-001/download", createdAt: "2026-07-24T10:00:00.000Z", }, ]; this.fileContents = new Map([ ["file-001", Buffer.from("id,name\ncomplex-product-keyboard,Keyboard\n")], ]); this.auditEvents = [ { id: "audit-001", organizationId: "org-acme", action: "order.created", actorId: "complex-user-admin", resourceType: "order", resourceId: "complex-order-001", metadata: { source: "seed" }, createdAt: "2026-07-20T10:00:00.000Z", }, { id: "audit-002", organizationId: "org-acme", action: "product.updated", actorId: "complex-user-manager", resourceType: "product", resourceId: "complex-product-keyboard", metadata: { version: 3 }, createdAt: "2026-07-19T10:00:00.000Z", }, ]; this.jobs = []; this.conversations = [ { id: "conversation-support", organizationId: "org-acme", title: "Order support", participantIds: ["complex-user-admin", "complex-user-support"], lastMessagePreview: "Can you check order complex-order-001?", updatedAt: "2026-07-25T10:05:00.000Z", }, ]; this.messages = [ { id: "message-001", conversationId: "conversation-support", senderId: "complex-user-admin", text: "Can you check order complex-order-001?", clientMessageId: "seed-message-001", createdAt: "2026-07-25T10:05:00.000Z", }, ]; this.orderIdempotency.clear(); this.sequences = { product: this.products.length + 20, order: 20, review: 20, file: 20, audit: 20, job: 20, message: 20, }; } findUserByEmail(email: string): ComplexUserRecord | undefined { return this.users.find( (user) => user.email.toLowerCase() === email.toLowerCase(), ); } findUserById(id: string): ComplexUserRecord | undefined { return this.users.find((user) => user.id === id); } publicUser(user: ComplexUserRecord): ComplexUserDto { const { password: _password, ...publicUser } = user; return publicUser; } listUsers(organizationId: string): ComplexUserDto[] { return this.users .filter((user) => user.organizationIds.includes(organizationId)) .map((user) => this.publicUser(user)); } changeUserRole(userId: string, role: ComplexRole): ComplexUserDto { const user = this.findUserById(userId); if (!user) throw new NotFoundException({ code: "USER_NOT_FOUND", message: "User not found.", }); user.role = role; for (const member of this.members.filter((item) => item.userId === userId)) member.role = role; return this.publicUser(user); } isMember(userId: string, organizationId: string): boolean { return this.members.some( (member) => member.userId === userId && member.organizationId === organizationId && member.status === "active", ); } listOrganizations(userId: string): OrganizationDto[] { const user = this.findUserById(userId); return this.organizations.filter((organization) => user?.organizationIds.includes(organization.id), ); } getOrganization(id: string, userId: string): OrganizationDto { const organization = this.organizations.find( (item) => item.id === id && this.isMember(userId, id), ); if (!organization) throw new NotFoundException({ code: "ORGANIZATION_NOT_FOUND", message: "Organization not found or unavailable.", }); return organization; } listMembers(organizationId: string): MemberDto[] { return this.members.filter( (member) => member.organizationId === organizationId, ); } listProducts(organizationId: string, query: CursorProductQueryDto) { let products = this.products.filter( (product) => product.organizationId === organizationId, ); if (query.search) products = products.filter((product) => `${product.name} ${product.description}` .toLowerCase() .includes(query.search!.toLowerCase()), ); if (query.status) products = products.filter((product) => product.status === query.status); return this.cursorPage(products, query.cursor, query.limit); } getProduct(organizationId: string, id: string): ComplexProductDto { const product = this.products.find( (item) => item.id === id && item.organizationId === organizationId, ); if (!product) throw new NotFoundException({ code: "PRODUCT_NOT_FOUND", message: "Product not found.", }); return product; } createProduct( organizationId: string, actorId: string, dto: CreateComplexProductDto, ): ComplexProductDto { if ( !this.categories.some( (category) => category.id === dto.categoryId && category.organizationId === organizationId, ) ) { throw new UnprocessableEntityException({ code: "CATEGORY_NOT_FOUND", message: "Selected category does not exist in this organization.", details: [{ field: "categoryId", message: "Unknown tenant category." }], }); } const id = `complex-product-${this.sequences.product++}`; const organization = this.organizations.find( (item) => item.id === organizationId, )!; const product: ComplexProductDto = { id, organizationId, name: dto.name, slug: `${dto.name.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${id}`, description: dto.description, status: dto.status, categoryId: dto.categoryId, brandId: dto.brandId, price: { amount: dto.priceAmount, currency: organization.currency }, variants: dto.variants.map((variant, index) => ({ id: `${id}-variant-${index + 1}`, sku: variant.sku, attributes: variant.attributes, price: { amount: variant.priceAmount, currency: organization.currency }, })), tags: dto.tags, publishedAt: dto.status === ComplexProductStatus.Active ? new Date().toISOString() : null, createdAt: new Date().toISOString(), version: 1, }; this.products.unshift(product); this.addAudit(organizationId, actorId, "product.created", "product", id, { version: 1, }); return product; } updateProduct( organizationId: string, actorId: string, id: string, dto: UpdateComplexProductDto, ): ComplexProductDto { const product = this.getProduct(organizationId, id); if (product.version !== dto.version) { throw new ConflictException({ code: "PRODUCT_VERSION_CONFLICT", message: "Product was changed by another user.", details: [ { field: "version", message: `Current version is ${product.version}.`, }, ], }); } const { version: _version, priceAmount, variants, ...changes } = dto; Object.assign(product, changes); if (priceAmount) product.price.amount = priceAmount; if (variants) product.variants = variants.map((variant, index) => ({ id: `${id}-variant-${index + 1}`, sku: variant.sku, attributes: variant.attributes, price: { amount: variant.priceAmount, currency: product.price.currency, }, })); if (dto.status === ComplexProductStatus.Active && !product.publishedAt) product.publishedAt = new Date().toISOString(); product.version += 1; this.addAudit(organizationId, actorId, "product.updated", "product", id, { version: product.version, }); return product; } listCategories(organizationId: string): ComplexCategoryDto[] { return this.categories.filter( (category) => category.organizationId === organizationId, ); } listBrands(organizationId: string): BrandDto[] { return this.brands.filter( (brand) => brand.organizationId === organizationId, ); } listWarehouses(organizationId: string): WarehouseDto[] { return this.warehouses.filter( (warehouse) => warehouse.organizationId === organizationId, ); } listInventory( organizationId: string, productId?: string, ): InventoryItemDto[] { const warehouseIds = new Set( this.listWarehouses(organizationId).map((warehouse) => warehouse.id), ); return this.inventory.filter( (item) => warehouseIds.has(item.warehouseId) && (!productId || item.productId === productId), ); } adjustInventory( organizationId: string, actorId: string, id: string, dto: AdjustInventoryDto, ): InventoryItemDto { const item = this.listInventory(organizationId).find( (inventory) => inventory.id === id, ); if (!item) throw new NotFoundException({ code: "INVENTORY_NOT_FOUND", message: "Inventory item not found.", }); if (item.version !== dto.version) throw new ConflictException({ code: "INVENTORY_VERSION_CONFLICT", message: "Inventory was changed by another user.", }); if (item.available + dto.delta < 0) throw new ConflictException({ code: "NEGATIVE_INVENTORY", message: "Adjustment would make available stock negative.", }); item.available += dto.delta; item.version += 1; this.addAudit( organizationId, actorId, "inventory.adjusted", "inventory", id, { delta: dto.delta, reason: dto.reason, version: item.version }, ); return item; } listCustomers(organizationId: string, query: CustomerQueryDto) { let customers = this.customers.filter( (customer) => customer.organizationId === organizationId, ); if (query.search) customers = customers.filter((customer) => `${customer.name} ${customer.email}` .toLowerCase() .includes(query.search!.toLowerCase()), ); const total = customers.length; const start = (query.page - 1) * query.limit; return { data: customers.slice(start, start + query.limit), meta: { page: query.page, limit: query.limit, total, totalPages: Math.ceil(total / query.limit), }, }; } getCustomer(organizationId: string, id: string): CustomerDto { const customer = this.customers.find( (item) => item.id === id && item.organizationId === organizationId, ); if (!customer) throw new NotFoundException({ code: "CUSTOMER_NOT_FOUND", message: "Customer not found.", }); return customer; } listOrders(organizationId: string, query: OrderCursorQueryDto) { let orders = this.orders.filter( (order) => order.organizationId === organizationId, ); if (query.status) orders = orders.filter((order) => order.status === query.status); return this.cursorPage(orders, query.cursor, query.limit); } getOrder(organizationId: string, id: string): ComplexOrderDto { const order = this.orders.find( (item) => item.id === id && item.organizationId === organizationId, ); if (!order) throw new NotFoundException({ code: "ORDER_NOT_FOUND", message: "Order not found.", }); return order; } createOrder( organizationId: string, actorId: string, idempotencyKey: string, dto: CreateComplexOrderDto, ): ComplexOrderDto { const previousOrderId = this.orderIdempotency.get( `${organizationId}:${idempotencyKey}`, ); if (previousOrderId) return this.getOrder(organizationId, previousOrderId); const customer = this.getCustomer(organizationId, dto.customerId); if (dto.items.length === 0) throw new UnprocessableEntityException({ code: "EMPTY_ORDER", message: "Order must contain at least one item.", details: [{ field: "items", message: "Add at least one item." }], }); const organization = this.organizations.find( (item) => item.id === organizationId, )!; const items = dto.items.map((input) => { const product = this.getProduct(organizationId, input.productId); const variant = product.variants.find( (item) => item.id === input.variantId, ); if (!variant) throw new UnprocessableEntityException({ code: "VARIANT_NOT_FOUND", message: `Variant ${input.variantId} does not exist.`, details: [{ field: "items", message: "Unknown product variant." }], }); return { productId: product.id, variantId: variant.id, name: product.name, quantity: input.quantity, unitPrice: variant.price, }; }); const subtotalCents = items.reduce( (sum, item) => sum + this.moneyToCents(item.unitPrice.amount) * item.quantity, 0, ); const promotion = dto.promotionCode ? this.promotions.find( (item) => item.code === dto.promotionCode && item.active, ) : undefined; const discountCents = promotion?.type === "percentage" ? Math.round((subtotalCents * Number(promotion.value)) / 100) : promotion ? this.moneyToCents(promotion.value) : 0; const id = `complex-order-${String(this.sequences.order++).padStart(3, "0")}`; const order: ComplexOrderDto = { id, organizationId, customerId: customer.id, status: ComplexOrderStatus.AwaitingPayment, items, subtotal: { amount: this.centsToMoney(subtotalCents), currency: organization.currency, }, discount: { amount: this.centsToMoney(discountCents), currency: organization.currency, }, total: { amount: this.centsToMoney(Math.max(0, subtotalCents - discountCents)), currency: organization.currency, }, shippingAddress: customer.defaultAddress, createdAt: new Date().toISOString(), version: 1, }; this.orders.unshift(order); this.orderIdempotency.set(`${organizationId}:${idempotencyKey}`, id); this.addAudit(organizationId, actorId, "order.created", "order", id, { idempotencyKey, }); return order; } cancelOrder( organizationId: string, actorId: string, id: string, ): ComplexOrderDto { const order = this.getOrder(organizationId, id); if ( [ComplexOrderStatus.Shipped, ComplexOrderStatus.Cancelled].includes( order.status, ) ) { throw new ConflictException({ code: "ORDER_CANNOT_BE_CANCELLED", message: `Order in ${order.status} status cannot be cancelled.`, }); } order.status = ComplexOrderStatus.Cancelled; order.version += 1; this.addAudit(organizationId, actorId, "order.cancelled", "order", id, { version: order.version, }); return order; } listPayments(organizationId: string): PaymentDto[] { const orderIds = new Set( this.orders .filter((order) => order.organizationId === organizationId) .map((order) => order.id), ); return this.payments.filter((payment) => orderIds.has(payment.orderId)); } listPromotions(): PromotionDto[] { return this.promotions; } listReviews(organizationId: string, productId?: string): ReviewDto[] { return this.reviews.filter( (review) => review.organizationId === organizationId && (!productId || review.productId === productId), ); } createReview(organizationId: string, dto: CreateReviewDto): ReviewDto { this.getProduct(organizationId, dto.productId); this.getCustomer(organizationId, dto.customerId); const review: TenantReview = { id: `review-${this.sequences.review++}`, organizationId, ...dto, status: "pending", createdAt: new Date().toISOString(), }; this.reviews.unshift(review); return review; } listNotifications(organizationId: string, cursor?: string, limit = 20) { return this.cursorPage( this.notifications.filter( (notification) => notification.organizationId === organizationId, ), cursor, limit, ); } markNotificationRead(organizationId: string, id: string): NotificationDto { const notification = this.notifications.find( (item) => item.id === id && item.organizationId === organizationId, ); if (!notification) throw new NotFoundException({ code: "NOTIFICATION_NOT_FOUND", message: "Notification not found.", }); notification.read = true; return notification; } listFiles(organizationId: string): FileMetadataDto[] { return this.files.filter((file) => file.organizationId === organizationId); } addFile(organizationId: string, file: Express.Multer.File): FileMetadataDto { const id = `file-${this.sequences.file++}`; const metadata: TenantFile = { id, organizationId, name: file.originalname, mimeType: file.mimetype, size: file.size, downloadUrl: `/api/v1/files/${id}/download`, createdAt: new Date().toISOString(), }; this.files.unshift(metadata); this.fileContents.set(id, file.buffer); return metadata; } getFile( organizationId: string, id: string, ): { metadata: FileMetadataDto; content: Buffer } { const metadata = this.files.find( (file) => file.id === id && file.organizationId === organizationId, ); const content = this.fileContents.get(id); if (!metadata || !content) throw new NotFoundException({ code: "FILE_NOT_FOUND", message: "File not found.", }); return { metadata, content }; } listAudit(organizationId: string, query: AuditQueryDto) { let events = this.auditEvents.filter( (event) => event.organizationId === organizationId, ); if (query.action) events = events.filter((event) => event.action === query.action); const total = events.length; const start = (query.page - 1) * query.limit; return { data: events.slice(start, start + query.limit), meta: { page: query.page, limit: query.limit, total, totalPages: Math.ceil(total / query.limit), }, }; } startOrdersExport(organizationId: string): JobDto { const now = new Date(); const job: TenantJob = { id: `job-${this.sequences.job++}`, organizationId, type: "orders-export", status: JobStatus.Pending, progress: 0, resultUrl: null, error: null, createdAt: now.toISOString(), updatedAt: now.toISOString(), startedAtMs: Date.now(), }; this.jobs.unshift(job); return job; } getJob(organizationId: string, id: string): JobDto { const job = this.jobs.find( (item) => item.id === id && item.organizationId === organizationId, ); if (!job) throw new NotFoundException({ code: "JOB_NOT_FOUND", message: "Background job not found.", }); const elapsed = Date.now() - job.startedAtMs; if (elapsed >= 800) { job.status = JobStatus.Completed; job.progress = 100; job.resultUrl = `/api/v1/jobs/${job.id}/result`; } else if (elapsed >= 200) { job.status = JobStatus.Processing; job.progress = Math.min(90, Math.max(20, Math.floor(elapsed / 8))); } job.updatedAt = new Date().toISOString(); return job; } jobResult(organizationId: string, id: string): Buffer { const job = this.getJob(organizationId, id); if (job.status !== JobStatus.Completed) throw new ConflictException({ code: "JOB_NOT_COMPLETED", message: "The export is not ready yet.", }); const rows = this.orders .filter((order) => order.organizationId === organizationId) .map((order) => `${order.id},${order.status},${order.total.amount}`); return Buffer.from(`id,status,total\n${rows.join("\n")}\n`); } listConversations(organizationId: string, userId: string): ConversationDto[] { return this.conversations.filter( (conversation) => conversation.organizationId === organizationId && conversation.participantIds.includes(userId), ); } getConversation( organizationId: string, userId: string, id: string, ): ConversationDto { const conversation = this.listConversations(organizationId, userId).find( (item) => item.id === id, ); if (!conversation) throw new NotFoundException({ code: "CONVERSATION_NOT_FOUND", message: "Conversation not found.", }); return conversation; } listMessages( organizationId: string, userId: string, conversationId: string, cursor?: string, limit = 30, ) { this.getConversation(organizationId, userId, conversationId); return this.cursorPage( this.messages.filter( (message) => message.conversationId === conversationId, ), cursor, limit, ); } sendMessage( organizationId: string, userId: string, conversationId: string, dto: SendMessageDto, ): ChatMessageDto { const conversation = this.getConversation( organizationId, userId, conversationId, ); const duplicate = this.messages.find( (message) => message.conversationId === conversationId && message.clientMessageId === dto.clientMessageId, ); if (duplicate) return duplicate; const message: ChatMessageDto = { id: `message-${this.sequences.message++}`, conversationId, senderId: userId, text: dto.text, clientMessageId: dto.clientMessageId, createdAt: new Date().toISOString(), }; this.messages.push(message); conversation.lastMessagePreview = dto.text; conversation.updatedAt = message.createdAt; return message; } private product( id: string, organizationId: string, name: string, amount: string, categoryId: string, brandId: string, status: ComplexProductStatus, ): ComplexProductDto { const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-"); const currency = organizationId === "org-acme" ? "USD" : "EUR"; return { id, organizationId, name, slug, description: `${name} demonstrates variants, tenant scope and optimistic updates.`, status, categoryId, brandId, price: { amount, currency }, variants: [ { id: `variant-${id}-default`, sku: `${id.toUpperCase()}-DEFAULT`, attributes: { color: "black", size: "standard" }, price: { amount, currency }, }, ], tags: status === ComplexProductStatus.Active ? ["featured"] : ["draft"], publishedAt: status === ComplexProductStatus.Active ? "2026-07-10T09:00:00.000Z" : null, createdAt: "2026-07-01T09:00:00.000Z", version: status === ComplexProductStatus.Active ? 3 : 1, }; } private cursorPage( items: T[], cursor: string | undefined, limit: number, ) { const cursorIndex = cursor ? items.findIndex((item) => item.id === cursor) : -1; const start = cursorIndex >= 0 ? cursorIndex + 1 : 0; const data = items.slice(start, start + limit); const hasMore = start + data.length < items.length; return { data, meta: { limit, nextCursor: hasMore ? (data.at(-1)?.id ?? null) : null, hasMore, }, }; } private addAudit( organizationId: string, actorId: string, action: string, resourceType: string, resourceId: string, metadata: Record, ): void { this.auditEvents.unshift({ id: `audit-${this.sequences.audit++}`, organizationId, action, actorId, resourceType, resourceId, metadata, createdAt: new Date().toISOString(), }); } private moneyToCents(amount: string): number { const [whole, fraction = "0"] = amount.split("."); return Number(whole) * 100 + Number(fraction.padEnd(2, "0").slice(0, 2)); } private centsToMoney(cents: number): string { return `${Math.floor(cents / 100)}.${String(cents % 100).padStart(2, "0")}`; } }