import type { TenantPackageSummary } from '../api'; export type PackageUsageMetricKey = 'events' | 'guests' | 'photos' | 'gallery'; export type PackageUsageMetric = { key: PackageUsageMetricKey; limit: number | null; used: number | null; remaining: number | null; }; export type PackageUsageState = 'ok' | 'warning' | 'danger'; const toNumber = (value: unknown): number | null => { if (typeof value === 'number' && Number.isFinite(value)) { return value; } if (typeof value === 'string' && value.trim() !== '') { const parsed = Number(value); if (Number.isFinite(parsed)) { return parsed; } } return null; }; const resolveLimitValue = (limits: Record | null, key: string): number | null => { const value = limits ? toNumber(limits[key]) : null; if (value === null || value <= 0) { return null; } return value; }; const resolveUsageValue = (value: unknown): number | null => { const normalized = toNumber(value); if (normalized === null || normalized < 0) { return null; } return normalized; }; const deriveUsedFromRemaining = (limit: number | null, remaining: number | null): number | null => { if (limit === null || remaining === null) { return null; } return Math.max(limit - remaining, 0); }; const deriveRemainingFromUsed = (limit: number | null, used: number | null): number | null => { if (limit === null || used === null) { return null; } return Math.max(limit - used, 0); }; const normalizeCount = (value: number | null, precision: 'round' | 'floor' = 'round'): number | null => { if (value === null) { return null; } const safe = Number.isFinite(value) ? value : null; if (safe === null) { return null; } return Math.max(0, precision === 'floor' ? Math.floor(safe) : Math.round(safe)); }; export const buildPackageUsageMetrics = (pkg: TenantPackageSummary): PackageUsageMetric[] => { const limits = pkg.package_limits ?? {}; const eventLimit = resolveLimitValue(limits, 'max_events_per_year'); const eventRemaining = resolveUsageValue(pkg.remaining_events); const eventUsed = resolveUsageValue(pkg.used_events) ?? deriveUsedFromRemaining(eventLimit, eventRemaining); const resolvedEventRemaining = eventRemaining ?? deriveRemainingFromUsed(eventLimit, eventUsed); const guestLimit = resolveLimitValue(limits, 'max_guests'); const guestRemaining = resolveUsageValue(limits['remaining_guests']); const guestUsed = resolveUsageValue(limits['used_guests']) ?? deriveUsedFromRemaining(guestLimit, guestRemaining); const resolvedGuestRemaining = guestRemaining ?? deriveRemainingFromUsed(guestLimit, guestUsed); const photoLimit = resolveLimitValue(limits, 'max_photos'); const photoRemaining = resolveUsageValue(limits['remaining_photos']); const photoUsed = resolveUsageValue(limits['used_photos']) ?? deriveUsedFromRemaining(photoLimit, photoRemaining); const resolvedPhotoRemaining = photoRemaining ?? deriveRemainingFromUsed(photoLimit, photoUsed); const galleryLimit = resolveLimitValue(limits, 'gallery_days'); const galleryRemaining = resolveUsageValue(limits['remaining_gallery_days']); const galleryUsed = resolveUsageValue(limits['used_gallery_days']) ?? deriveUsedFromRemaining(galleryLimit, galleryRemaining); const resolvedGalleryRemaining = normalizeCount(galleryRemaining ?? deriveRemainingFromUsed(galleryLimit, galleryUsed)); const resolvedGalleryUsed = normalizeCount(galleryUsed); return [ { key: 'events' as const, limit: eventLimit, used: eventUsed, remaining: resolvedEventRemaining }, { key: 'guests' as const, limit: guestLimit, used: guestUsed, remaining: resolvedGuestRemaining }, { key: 'photos' as const, limit: photoLimit, used: photoUsed, remaining: resolvedPhotoRemaining }, { key: 'gallery' as const, limit: galleryLimit, used: resolvedGalleryUsed, remaining: resolvedGalleryRemaining }, ].filter((metric) => metric.limit !== null); }; export const usagePercent = (metric: PackageUsageMetric): number => { if (!metric.limit || metric.limit <= 0) { return 0; } if (metric.used === null || metric.used < 0) { return 100; } return Math.min(100, Math.max(0, Math.round((metric.used / metric.limit) * 100))); }; export const getUsageState = (metric: PackageUsageMetric): PackageUsageState => { if (!metric.limit || metric.limit <= 0) { return 'ok'; } if (metric.remaining !== null && metric.remaining <= 0) { return 'danger'; } if (metric.used === null) { return 'ok'; } const percent = usagePercent(metric); if (percent >= 95) { return 'danger'; } if (percent >= 80) { return 'warning'; } return 'ok'; };