const pool = require("../db/pool"); exports.getUnboughtItems = async () => { const result = await pool.query( `SELECT gl.id, LOWER(gl.item_name) AS item_name, gl.quantity, gl.bought, ENCODE(gl.item_image, 'base64') as item_image, gl.image_mime_type, ARRAY_AGG(DISTINCT COALESCE(gh_user.name, creator.name)) FILTER (WHERE COALESCE(gh_user.name, creator.name) IS NOT NULL) as added_by_users, gl.modified_on as last_added_on, ic.item_type, ic.item_group, ic.zone FROM grocery_list gl LEFT JOIN users creator ON gl.added_by = creator.id LEFT JOIN grocery_history gh ON gl.id = gh.list_item_id LEFT JOIN users gh_user ON gh.added_by = gh_user.id LEFT JOIN item_classification ic ON gl.id = ic.id WHERE gl.bought = FALSE GROUP BY gl.id, gl.item_name, gl.quantity, gl.bought, gl.item_image, gl.image_mime_type, gl.modified_on, ic.item_type, ic.item_group, ic.zone ORDER BY gl.id ASC` ); return result.rows; }; exports.getItemByName = async (itemName) => { const result = await pool.query( "SELECT * FROM grocery_list WHERE item_name ILIKE $1", [itemName] ); return result.rows[0] || null; }; exports.addOrUpdateItem = async (itemName, quantity, userId, imageBuffer = null, mimeType = null) => { const lowerItemName = itemName.toLowerCase(); const result = await pool.query( "SELECT id, bought FROM grocery_list WHERE item_name ILIKE $1", [lowerItemName] ); if (result.rowCount > 0) { // Update existing item if (imageBuffer && mimeType) { await pool.query( `UPDATE grocery_list SET quantity = $1, bought = FALSE, item_image = $3, image_mime_type = $4, modified_on = NOW() WHERE id = $2`, [quantity, result.rows[0].id, imageBuffer, mimeType] ); } else { await pool.query( `UPDATE grocery_list SET quantity = $1, bought = FALSE, modified_on = NOW() WHERE id = $2`, [quantity, result.rows[0].id] ); } return result.rows[0].id; } else { // Insert new item const insert = await pool.query( `INSERT INTO grocery_list (item_name, quantity, added_by, item_image, image_mime_type) VALUES ($1, $2, $3, $4, $5) RETURNING id`, [lowerItemName, quantity, userId, imageBuffer, mimeType] ); return insert.rows[0].id; } }; exports.setBought = async (id, userId) => { await pool.query( "UPDATE grocery_list SET bought = TRUE, modified_on = NOW() WHERE id = $1", [id] ); }; exports.addHistoryRecord = async (itemId, quantity, userId) => { await pool.query( `INSERT INTO grocery_history (list_item_id, quantity, added_by, added_on) VALUES ($1, $2, $3, NOW())`, [itemId, quantity, userId] ); }; exports.getSuggestions = async (query) => { const result = await pool.query( `SELECT DISTINCT item_name FROM grocery_list WHERE item_name ILIKE $1 LIMIT 10`, [`%${query}%`] ); return result.rows; }; exports.getRecentlyBoughtItems = async () => { const result = await pool.query( `SELECT gl.id, LOWER(gl.item_name) AS item_name, gl.quantity, gl.bought, ENCODE(gl.item_image, 'base64') as item_image, gl.image_mime_type, ARRAY_AGG(DISTINCT COALESCE(gh_user.name, creator.name)) FILTER (WHERE COALESCE(gh_user.name, creator.name) IS NOT NULL) as added_by_users, gl.modified_on as last_added_on FROM grocery_list gl LEFT JOIN users creator ON gl.added_by = creator.id LEFT JOIN grocery_history gh ON gl.id = gh.list_item_id LEFT JOIN users gh_user ON gh.added_by = gh_user.id WHERE gl.bought = TRUE AND gl.modified_on >= NOW() - INTERVAL '24 hours' GROUP BY gl.id, gl.item_name, gl.quantity, gl.bought, gl.item_image, gl.image_mime_type, gl.modified_on ORDER BY gl.modified_on DESC` ); return result.rows; }; // Classification methods exports.getClassification = async (itemId) => { const result = await pool.query( `SELECT item_type, item_group, zone, confidence, source FROM item_classification WHERE id = $1`, [itemId] ); return result.rows[0] || null; }; exports.upsertClassification = async (itemId, classification) => { const { item_type, item_group, zone, confidence, source } = classification; const result = await pool.query( `INSERT INTO item_classification (id, item_type, item_group, zone, confidence, source) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (id) DO UPDATE SET item_type = EXCLUDED.item_type, item_group = EXCLUDED.item_group, zone = EXCLUDED.zone, confidence = EXCLUDED.confidence, source = EXCLUDED.source RETURNING *`, [itemId, item_type, item_group, zone, confidence, source] ); return result.rows[0]; }; exports.updateItem = async (id, itemName, quantity) => { const result = await pool.query( `UPDATE grocery_list SET item_name = $2, quantity = $3, modified_on = NOW() WHERE id = $1 RETURNING *`, [id, itemName, quantity] ); return result.rows[0]; };