import { and, eq, sql } from "drizzle-orm"; import type { db as prodDb } from "../../db/index.ts"; import { categories, globalItems, items, setupItems, setups, users, } from "../../db/schema.ts"; import type { UpdateProfile } from "../../shared/types.ts"; type Db = typeof prodDb; export async function updateProfile( db: Db, userId: number, data: UpdateProfile, ) { const [existing] = await db.select().from(users).where(eq(users.id, userId)); if (!existing) return null; // If no fields to update, return existing user const hasUpdates = Object.values(data).some((v) => v !== undefined); if (!hasUpdates) return existing; const [updated] = await db .update(users) .set(data) .where(eq(users.id, userId)) .returning(); return updated; } export async function getPublicProfile(db: Db, userId: number) { const [user] = await db .select({ id: users.id, displayName: users.displayName, avatarUrl: users.avatarUrl, bio: users.bio, }) .from(users) .where(eq(users.id, userId)); if (!user) return null; const publicSetups = await db .select({ id: setups.id, name: setups.name, createdAt: setups.createdAt, itemCount: sql`COALESCE(( SELECT COUNT(*) FROM setup_items WHERE setup_items.setup_id = setups.id ), 0)`.as("item_count"), totalWeight: sql`COALESCE(( SELECT SUM( COALESCE( CASE WHEN items.global_item_id IS NOT NULL THEN global_items.weight_grams ELSE NULL END, items.weight_grams ) * items.quantity ) FROM setup_items JOIN items ON items.id = setup_items.item_id LEFT JOIN global_items ON global_items.id = items.global_item_id WHERE setup_items.setup_id = setups.id ), 0)`.as("total_weight"), totalCost: sql`COALESCE(( SELECT SUM( COALESCE( CASE WHEN items.global_item_id IS NOT NULL THEN global_items.price_cents ELSE NULL END, items.price_cents ) * items.quantity ) FROM setup_items JOIN items ON items.id = setup_items.item_id LEFT JOIN global_items ON global_items.id = items.global_item_id WHERE setup_items.setup_id = setups.id ), 0)`.as("total_cost"), }) .from(setups) .where(and(eq(setups.userId, userId), eq(setups.isPublic, true))); return { ...user, setups: publicSetups }; } export async function getPublicSetupWithItems(db: Db, setupId: number) { const [setup] = await db .select() .from(setups) .where(and(eq(setups.id, setupId), eq(setups.isPublic, true))); if (!setup) return null; const itemList = await db .select({ id: items.id, name: sql`COALESCE( CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.brand} || ' ' || ${globalItems.model} ELSE ${items.name} END, ${items.name} )`.as("name"), weightGrams: sql`COALESCE( CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.weightGrams} ELSE NULL END, ${items.weightGrams} )`.as("weight_grams"), priceCents: sql`COALESCE( CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.priceCents} ELSE NULL END, ${items.priceCents} )`.as("price_cents"), quantity: items.quantity, categoryId: items.categoryId, notes: items.notes, productUrl: items.productUrl, imageFilename: sql`COALESCE( ${items.imageFilename}, CASE WHEN ${items.globalItemId} IS NOT NULL THEN ${globalItems.imageUrl} ELSE NULL END )`.as("image_filename"), globalItemId: items.globalItemId, createdAt: items.createdAt, updatedAt: items.updatedAt, categoryName: categories.name, categoryIcon: categories.icon, classification: setupItems.classification, }) .from(setupItems) .innerJoin(items, eq(setupItems.itemId, items.id)) .innerJoin(categories, eq(items.categoryId, categories.id)) .leftJoin(globalItems, eq(items.globalItemId, globalItems.id)) .where(eq(setupItems.setupId, setupId)); return { ...setup, items: itemList }; }