'use strict'; const { getDb } = require('../db/knex'); const { AppError } = require('../utils/errors'); function formatItem(row) { return { id: row.id, item_name: row.item_name, quantity: row.quantity, last_modified: row.last_modified, created_at: row.created_at, }; } /** * Returns all pantry items for the authenticated user, ordered alphabetically. */ async function getPantryItems(userId) { const db = getDb(); const items = await db('pantry_items') .where({ user_id: userId }) .orderBy('item_name_lower', 'asc'); return items.map(formatItem); } /** * Adds a new pantry item. * Rejects with 409 if an item with the same name already exists (case-insensitive). * Auto-merge is intentionally not performed — explicit > implicit. */ async function addPantryItem(userId, { item_name, quantity }) { const db = getDb(); const trimmedName = item_name.trim(); const existing = await db('pantry_items') .where({ user_id: userId }) .whereRaw('item_name_lower = LOWER(?)', [trimmedName]) .first(); if (existing) { throw new AppError( 409, 'DUPLICATE_ITEM', `'${existing.item_name}' already exists in your pantry.`, { existing_item: formatItem(existing) } ); } const [item] = await db('pantry_items') .insert({ user_id: userId, item_name: trimmedName, quantity }) .returning('*'); return formatItem(item); } /** * Updates the quantity of an existing pantry item. * Server sets last_modified = NOW() — server clock is authoritative. * Verifies ownership: item must belong to the requesting user. */ async function updatePantryItem(userId, itemId, { quantity }) { const db = getDb(); const existing = await db('pantry_items') .where({ id: itemId, user_id: userId }) .first(); if (!existing) { throw new AppError(404, 'NOT_FOUND', 'Pantry item not found.'); } const now = new Date(); const [item] = await db('pantry_items') .where({ id: itemId, user_id: userId }) .update({ quantity, last_modified: now, updated_at: now }) .returning('*'); return formatItem(item); } /** * Deletes a pantry item and records a tombstone for sync. * Verifies ownership before deletion. */ async function deletePantryItem(userId, itemId) { const db = getDb(); const existing = await db('pantry_items') .where({ id: itemId, user_id: userId }) .first(); if (!existing) { throw new AppError(404, 'NOT_FOUND', 'Pantry item not found.'); } await db.transaction(async (trx) => { await trx('pantry_items').where({ id: itemId, user_id: userId }).delete(); await trx('deleted_records').insert({ user_id: userId, record_type: 'pantry_item', record_id: itemId, }); }); } module.exports = { getPantryItems, addPantryItem, updatePantryItem, deletePantryItem, };