import type { PrismaClient } from '@prisma/client'; import { DEFAULT_PREMIUM_TIER_CONFIGS, featureKeyForResource, isAssignmentActive, limitKeyForResource, parsePremiumFeatures, PremiumTierSchema, type PremiumLimitResource, type PremiumTier, type PremiumTierConfig } from '@nexumi/shared'; export class PremiumLimitError extends Error { constructor( public readonly code: 'feature_disabled' | 'limit_reached', public readonly resource: PremiumLimitResource, public readonly limit: number, public readonly current: number, public readonly tier: PremiumTier ) { super(code); } } function toConfig(row: { tier: string; maxTags: number; maxFeeds: number; maxBackups: number; features: unknown; }): PremiumTierConfig { return { tier: PremiumTierSchema.parse(row.tier), maxTags: row.maxTags, maxFeeds: row.maxFeeds, maxBackups: row.maxBackups, features: parsePremiumFeatures(row.features) }; } export async function ensurePremiumTierConfigs(prisma: PrismaClient): Promise { const configs: PremiumTierConfig[] = []; for (const tier of PremiumTierSchema.options) { const defaults = DEFAULT_PREMIUM_TIER_CONFIGS[tier]; const row = await prisma.premiumTierConfig.upsert({ where: { tier }, create: { tier, maxTags: defaults.maxTags, maxFeeds: defaults.maxFeeds, maxBackups: defaults.maxBackups, features: defaults.features }, update: {} }); configs.push(toConfig(row)); } return configs; } export async function resolveGuildTier(prisma: PrismaClient, guildId: string): Promise { const assignment = await prisma.guildPremiumAssignment.findUnique({ where: { guildId } }); if (!assignment || !isAssignmentActive(assignment.expiresAt)) { return 'FREE'; } const parsed = PremiumTierSchema.safeParse(assignment.tier); return parsed.success ? parsed.data : 'FREE'; } export async function resolveGuildPremiumConfig( prisma: PrismaClient, guildId: string ): Promise { const tier = await resolveGuildTier(prisma, guildId); const configs = await ensurePremiumTierConfigs(prisma); return configs.find((config) => config.tier === tier) ?? DEFAULT_PREMIUM_TIER_CONFIGS[tier]; } export async function assertPremiumLimit( prisma: PrismaClient, guildId: string, resource: PremiumLimitResource, currentCount: number ): Promise { const config = await resolveGuildPremiumConfig(prisma, guildId); const featureKey = featureKeyForResource(resource); if (!config.features[featureKey]) { throw new PremiumLimitError('feature_disabled', resource, 0, currentCount, config.tier); } const limit = config[limitKeyForResource(resource)]; if (currentCount >= limit) { throw new PremiumLimitError('limit_reached', resource, limit, currentCount, config.tier); } return config; }