import type { Db } from '../db/index'; import type { PortfolioData, PortfolioHolding } from '../types'; interface HoldingRow { ticker: string; shares: number; cost_basis: number; type: string; source: string; } export class PortfolioRepository { constructor(private readonly db: Db) {} exists(): boolean { const row = this.db.prepare('SELECT COUNT(*) AS n FROM holdings').get() as { n: number }; return row.n > 0; } read(): PortfolioData { const rows = this.db.prepare('SELECT * FROM holdings ORDER BY ticker').all() as HoldingRow[]; return { holdings: rows.map(PortfolioRepository.toHolding) }; } upsert(entry: PortfolioHolding): PortfolioHolding { const ticker = entry.ticker.toUpperCase().trim(); this.db .prepare( `INSERT INTO holdings (ticker, shares, cost_basis, type, source) VALUES (?, ?, ?, ?, ?) ON CONFLICT(ticker) DO UPDATE SET shares = excluded.shares, cost_basis = excluded.cost_basis, type = excluded.type, source = excluded.source`, ) .run( ticker, entry.shares, entry.costBasis ?? 0, entry.type ?? 'stock', entry.source ?? 'Manual', ); return { ...entry, ticker }; } remove(ticker: string): boolean { const result = this.db .prepare('DELETE FROM holdings WHERE ticker = ?') .run(ticker.toUpperCase()); return result.changes > 0; } private static toHolding(row: HoldingRow): PortfolioHolding { return { ticker: row.ticker, shares: row.shares, costBasis: row.cost_basis, type: row.type as PortfolioHolding['type'], source: row.source, }; } }