import { getContainer } from '@bytelyst/cosmos'; import { config } from '../config/index.js'; import logger from '../utils/logger.js'; import { getLegacySupabaseClient } from './legacySupabaseClient.js'; import type { CapitalLedgerRecord } from './CapitalLedger.js'; const CONTAINER_NAME = 'capital_ledgers'; interface CapitalLedgerDocument { id: string; productId: string; profile_id: string; allocated_capital: number; reserved_for_orders: number; reserved_for_positions: number; realized_pnl: number; updated_at: string; } const toNumeric = (value: unknown): number => { const numeric = Number(value); return Number.isFinite(numeric) ? numeric : 0; }; function isCosmosConfigured(): boolean { return Boolean(config.COSMOS_ENDPOINT && config.COSMOS_KEY); } function getLegacyClient() { return getLegacySupabaseClient(); } function toLedgerRecord(doc: Partial | null | undefined): CapitalLedgerRecord | null { const profileId = String(doc?.profile_id || '').trim(); if (!profileId) return null; return { profile_id: profileId, allocated_capital: toNumeric(doc?.allocated_capital), reserved_for_orders: toNumeric(doc?.reserved_for_orders), reserved_for_positions: toNumeric(doc?.reserved_for_positions), realized_pnl: toNumeric(doc?.realized_pnl), updated_at: String(doc?.updated_at || new Date().toISOString()) }; } function toLedgerDocument(record: CapitalLedgerRecord): CapitalLedgerDocument { return { id: record.profile_id, productId: config.PRODUCT_ID, ...record }; } async function readFromCosmos(profileId: string): Promise { const container = getContainer(CONTAINER_NAME); try { const { resource } = await container.item(profileId, config.PRODUCT_ID).read(); return toLedgerRecord(resource); } catch (error) { const code = (error as { code?: number })?.code; if (code === 404) return null; throw error; } } async function writeToCosmos(record: CapitalLedgerRecord): Promise { const container = getContainer(CONTAINER_NAME); const { resource } = await container.items.upsert(toLedgerDocument(record)); return toLedgerRecord(resource as unknown as CapitalLedgerDocument); } async function readFromLegacy(profileId: string): Promise { const client = getLegacyClient(); if (!client) return null; const { data, error } = await client .from('capital_ledgers') .select('*') .eq('profile_id', profileId) .maybeSingle(); if (error) { logger.error(`[CapitalLedgerRepo] Legacy read failed for ${profileId}: ${error.message}`); return null; } return toLedgerRecord(data as CapitalLedgerDocument); } export async function getCapitalLedger(profileId: string): Promise { if (!profileId) return null; if (!isCosmosConfigured()) { return readFromLegacy(profileId); } if (isCosmosConfigured()) { try { const cosmosRecord = await readFromCosmos(profileId); if (cosmosRecord) return cosmosRecord; const legacyRecord = await readFromLegacy(profileId); if (!legacyRecord) return null; await writeToCosmos(legacyRecord); logger.info(`[CapitalLedgerRepo] Seeded capital ledger ${profileId} from legacy store into Cosmos.`); return legacyRecord; } catch (error) { logger.warn(`[CapitalLedgerRepo] Cosmos read/seed failed for ${profileId}: ${error instanceof Error ? error.message : 'unknown error'}`); return null; } } return null; } export async function upsertCapitalLedger( record: CapitalLedgerRecord ): Promise { if (!record.profile_id) return null; if (isCosmosConfigured()) { try { const saved = await writeToCosmos(record); if (saved) return saved; } catch (error) { logger.warn(`[CapitalLedgerRepo] Cosmos upsert failed, falling back to legacy store: ${error instanceof Error ? error.message : 'unknown error'}`); } } const client = getLegacyClient(); if (!client) return null; const { data, error } = await client .from('capital_ledgers') .upsert({ profile_id: record.profile_id, allocated_capital: record.allocated_capital, reserved_for_orders: record.reserved_for_orders, reserved_for_positions: record.reserved_for_positions, realized_pnl: record.realized_pnl, updated_at: record.updated_at }, { onConflict: 'profile_id' }) .select('*') .maybeSingle(); if (error) { logger.error(`[CapitalLedgerRepo] Legacy upsert failed for ${record.profile_id}: ${error.message}`); return null; } return toLedgerRecord(data as CapitalLedgerDocument); }