import { Injectable } from '@nestjs/common'; import type { WhmcsDashboardStatsResponse } from '@hexahost/contracts'; import { getBillingProvider } from '../../billing/billing-provider'; import { PrismaService } from '../../prisma/prisma.service'; type InstallationRef = { id: string; integrationId: string; }; @Injectable() export class WhmcsDashboardService { constructor(private readonly prisma: PrismaService) {} async getStats(installation: InstallationRef): Promise { const [ linkedClients, linkedServices, pendingEvents, deadLetterEvents, openIssues, exportedUsagePeriods, syncCursor, lastReconciledLink, ] = await Promise.all([ this.prisma.whmcsClientLink.count({ where: { installationId: installation.id }, }), this.prisma.whmcsServiceLink.count({ where: { installationId: installation.id }, }), this.prisma.integrationEvent.count({ where: { installationId: installation.id, status: 'PENDING' }, }), this.prisma.integrationEvent.count({ where: { installationId: installation.id, status: 'DEAD_LETTER' }, }), this.prisma.reconciliationIssue.count({ where: { installationId: installation.id, resolvedAt: null }, }), this.prisma.whmcsUsageExport.count({ where: { installationId: installation.id, status: { in: ['EXPORTED', 'ADJUSTED'] } }, }), this.prisma.integrationSyncCursor.findUnique({ where: { installationId: installation.id }, }), this.prisma.whmcsServiceLink.findFirst({ where: { installationId: installation.id, lastReconciledAt: { not: null }, }, orderBy: { lastReconciledAt: 'desc' }, select: { lastReconciledAt: true }, }), ]); const suspendedServices = await this.prisma.whmcsServiceLink.count({ where: { installationId: installation.id, status: 'SUSPENDED' }, }); const terminatedServices = await this.prisma.whmcsServiceLink.count({ where: { installationId: installation.id, status: 'TERMINATED' }, }); return { integrationId: installation.integrationId, billingProvider: getBillingProvider(), moduleVersion: '1.0.0', linkedClients, linkedServices, suspendedServices, terminatedServices, pendingEvents, deadLetterEvents, openReconciliationIssues: openIssues, exportedUsagePeriods, eventsCursor: syncCursor?.eventsCursor ?? null, lastEventSyncAt: syncCursor?.updatedAt.toISOString() ?? null, lastReconciliationAt: lastReconciledLink?.lastReconciledAt?.toISOString() ?? null, }; } }