import type { Holding } from '@/lib/types'; import { asErrorMessage, jsonError } from '@/lib/server/http'; import { recalculateHolding } from '@/lib/server/portfolio'; import { getStoreSnapshot, withStore } from '@/lib/server/store'; function nowIso() { return new Date().toISOString(); } function asPositiveNumber(value: unknown) { const parsed = typeof value === 'number' ? value : Number(value); return Number.isFinite(parsed) && parsed > 0 ? parsed : null; } export async function GET() { const snapshot = await getStoreSnapshot(); const holdings = snapshot.holdings .slice() .sort((a, b) => Number(b.market_value) - Number(a.market_value)); return Response.json({ holdings }); } export async function POST(request: Request) { try { const payload = await request.json() as { ticker?: string; shares?: number; avgCost?: number; currentPrice?: number; }; if (!payload.ticker || payload.ticker.trim().length < 1) { return jsonError('ticker is required'); } const shares = asPositiveNumber(payload.shares); const avgCost = asPositiveNumber(payload.avgCost); if (shares === null) { return jsonError('shares must be a positive number'); } if (avgCost === null) { return jsonError('avgCost must be a positive number'); } const ticker = payload.ticker.trim().toUpperCase(); const now = nowIso(); let holding: Holding | null = null; await withStore((store) => { const existingIndex = store.holdings.findIndex((entry) => entry.ticker === ticker); const currentPrice = asPositiveNumber(payload.currentPrice) ?? avgCost; if (existingIndex >= 0) { const existing = store.holdings[existingIndex]; const updated = recalculateHolding({ ...existing, ticker, shares: shares.toFixed(6), avg_cost: avgCost.toFixed(6), current_price: currentPrice.toFixed(6), updated_at: now, last_price_at: now }); store.holdings[existingIndex] = updated; holding = updated; return; } store.counters.holdings += 1; const created = recalculateHolding({ id: store.counters.holdings, user_id: 1, ticker, shares: shares.toFixed(6), avg_cost: avgCost.toFixed(6), current_price: currentPrice.toFixed(6), market_value: '0', gain_loss: '0', gain_loss_pct: '0', last_price_at: now, created_at: now, updated_at: now }); store.holdings.unshift(created); holding = created; }); return Response.json({ holding }); } catch (error) { return jsonError(asErrorMessage(error, 'Failed to save holding')); } }