import type { FinanceCommandService, HouseholdOnboardingService } from '@household/application' import type { Logger } from '@household/observability' import { allowedMiniAppOrigin, createMiniAppSessionService, miniAppErrorResponse, miniAppJsonResponse, readMiniAppRequestPayload } from './miniapp-auth' export function createMiniAppDashboardHandler(options: { allowedOrigins: readonly string[] botToken: string financeServiceForHousehold: (householdId: string) => FinanceCommandService onboardingService: HouseholdOnboardingService logger?: Logger }): { handler: (request: Request) => Promise } { const sessionService = createMiniAppSessionService({ botToken: options.botToken, onboardingService: options.onboardingService }) return { handler: async (request) => { const origin = allowedMiniAppOrigin(request, options.allowedOrigins) if (request.method === 'OPTIONS') { return miniAppJsonResponse({ ok: true }, 204, origin) } if (request.method !== 'POST') { return miniAppJsonResponse({ ok: false, error: 'Method Not Allowed' }, 405, origin) } try { const payload = await readMiniAppRequestPayload(request) if (!payload.initData) { return miniAppJsonResponse({ ok: false, error: 'Missing initData' }, 400, origin) } const session = await sessionService.authenticate(payload) if (!session) { return miniAppJsonResponse( { ok: false, error: 'Invalid Telegram init data' }, 401, origin ) } if (!session.authorized) { return miniAppJsonResponse( { ok: true, authorized: false, onboarding: session.onboarding }, 403, origin ) } if (!session.member) { return miniAppJsonResponse( { ok: false, error: 'Authenticated session is missing member context' }, 500, origin ) } const dashboard = await options .financeServiceForHousehold(session.member.householdId) .generateDashboard() if (!dashboard) { return miniAppJsonResponse( { ok: false, error: 'No billing cycle available' }, 404, origin ) } return miniAppJsonResponse( { ok: true, authorized: true, dashboard: { period: dashboard.period, currency: dashboard.currency, totalDueMajor: dashboard.totalDue.toMajorString(), members: dashboard.members.map((line) => ({ memberId: line.memberId, displayName: line.displayName, rentShareMajor: line.rentShare.toMajorString(), utilityShareMajor: line.utilityShare.toMajorString(), purchaseOffsetMajor: line.purchaseOffset.toMajorString(), netDueMajor: line.netDue.toMajorString(), explanations: line.explanations })), ledger: dashboard.ledger.map((entry) => ({ id: entry.id, kind: entry.kind, title: entry.title, amountMajor: entry.amount.toMajorString(), actorDisplayName: entry.actorDisplayName, occurredAt: entry.occurredAt })) } }, 200, origin ) } catch (error) { return miniAppErrorResponse(error, origin, options.logger) } } } }