import { apiClient } from '../client'; import { SubscriptionLimits, FeatureCheckResponse, UsageCheckResponse, UsageSummary, AvailablePlans, PlanUpgradeValidation, PlanUpgradeResult, SUBSCRIPTION_PLANS, ANALYTICS_LEVELS, AnalyticsLevel, SubscriptionPlanKey, PLAN_HIERARCHY, ANALYTICS_HIERARCHY } from '../types/subscription'; // Map plan keys to analytics levels based on backend data const PLAN_TO_ANALYTICS_LEVEL: Record = { [SUBSCRIPTION_PLANS.STARTER]: ANALYTICS_LEVELS.BASIC, [SUBSCRIPTION_PLANS.PROFESSIONAL]: ANALYTICS_LEVELS.ADVANCED, [SUBSCRIPTION_PLANS.ENTERPRISE]: ANALYTICS_LEVELS.PREDICTIVE }; // Cache for available plans let cachedPlans: AvailablePlans | null = null; let lastFetchTime: number | null = null; const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes export class SubscriptionService { private readonly baseUrl = '/subscriptions'; async getSubscriptionLimits(tenantId: string): Promise { return apiClient.get(`${this.baseUrl}/${tenantId}/limits`); } async checkFeatureAccess( tenantId: string, featureName: string ): Promise { return apiClient.get( `${this.baseUrl}/${tenantId}/features/${featureName}/check` ); } async checkUsageLimit( tenantId: string, resourceType: 'users' | 'sales_records' | 'inventory_items' | 'api_requests', requestedAmount?: number ): Promise { const queryParams = new URLSearchParams(); if (requestedAmount !== undefined) { queryParams.append('requested_amount', requestedAmount.toString()); } const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/usage/${resourceType}/check?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/usage/${resourceType}/check`; return apiClient.get(url); } async recordUsage( tenantId: string, resourceType: 'users' | 'sales_records' | 'inventory_items' | 'api_requests', amount: number = 1 ): Promise<{ success: boolean; message: string }> { return apiClient.post<{ success: boolean; message: string }>( `${this.baseUrl}/${tenantId}/usage/${resourceType}/record`, { amount } ); } async getCurrentUsage(tenantId: string): Promise<{ users: number; sales_records: number; inventory_items: number; api_requests_this_hour: number; }> { return apiClient.get(`${this.baseUrl}/${tenantId}/usage/current`); } async getUsageSummary(tenantId: string): Promise { return apiClient.get(`${this.baseUrl}/${tenantId}/usage`); } async getAvailablePlans(): Promise { return apiClient.get('/plans'); } async validatePlanUpgrade(tenantId: string, planKey: string): Promise { return apiClient.get(`${this.baseUrl}/${tenantId}/validate-upgrade/${planKey}`); } async upgradePlan(tenantId: string, planKey: string): Promise { return apiClient.post(`${this.baseUrl}/${tenantId}/upgrade?new_plan=${planKey}`, {}); } async canAddLocation(tenantId: string): Promise<{ can_add: boolean; reason?: string; current_count?: number; max_allowed?: number }> { return apiClient.get(`${this.baseUrl}/${tenantId}/can-add-location`); } async canAddProduct(tenantId: string): Promise<{ can_add: boolean; reason?: string; current_count?: number; max_allowed?: number }> { return apiClient.get(`${this.baseUrl}/${tenantId}/can-add-product`); } async canAddUser(tenantId: string): Promise<{ can_add: boolean; reason?: string; current_count?: number; max_allowed?: number }> { return apiClient.get(`${this.baseUrl}/${tenantId}/can-add-user`); } async hasFeature(tenantId: string, featureName: string): Promise<{ has_feature: boolean; feature_value?: any; plan?: string; reason?: string }> { return apiClient.get(`${this.baseUrl}/${tenantId}/features/${featureName}`); } formatPrice(amount: number): string { return new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(amount); } /** * Fetch available subscription plans from the backend */ async fetchAvailablePlans(): Promise { const now = Date.now(); // Return cached data if it's still valid if (cachedPlans && lastFetchTime && (now - lastFetchTime) < CACHE_DURATION) { return cachedPlans; } try { const plans = await apiClient.get('/plans'); cachedPlans = plans; lastFetchTime = now; return plans; } catch (error) { console.error('Failed to fetch subscription plans:', error); throw error; } } /** * Get plan display information */ async getPlanDisplayInfo(planKey: string) { try { const plans = await this.fetchAvailablePlans(); const plan = plans.plans[planKey]; if (plan) { return { name: plan.name, color: this.getPlanColor(planKey), description: plan.description, monthlyPrice: plan.monthly_price }; } return { name: 'Desconocido', color: 'gray', description: '', monthlyPrice: 0 }; } catch (error) { console.error('Failed to get plan display info:', error); return { name: 'Desconocido', color: 'gray', description: '', monthlyPrice: 0 }; } } /** * Get plan color based on plan key */ getPlanColor(planKey: string): string { switch (planKey) { case SUBSCRIPTION_PLANS.STARTER: return 'blue'; case SUBSCRIPTION_PLANS.PROFESSIONAL: return 'purple'; case SUBSCRIPTION_PLANS.ENTERPRISE: return 'amber'; default: return 'gray'; } } /** * Check if a plan meets minimum requirements */ doesPlanMeetMinimum(plan: SubscriptionPlanKey, minimumRequired: SubscriptionPlanKey): boolean { return PLAN_HIERARCHY[plan] >= PLAN_HIERARCHY[minimumRequired]; } /** * Get analytics level for a plan */ getAnalyticsLevelForPlan(plan: SubscriptionPlanKey): AnalyticsLevel { return PLAN_TO_ANALYTICS_LEVEL[plan] || ANALYTICS_LEVELS.NONE; } /** * Check if analytics level meets minimum requirements */ doesAnalyticsLevelMeetMinimum(level: AnalyticsLevel, minimumRequired: AnalyticsLevel): boolean { return ANALYTICS_HIERARCHY[level] >= ANALYTICS_HIERARCHY[minimumRequired]; } /** * Get plan features */ async getPlanFeatures(planKey: string) { try { const plans = await this.fetchAvailablePlans(); const plan = plans.plans[planKey]; if (plan) { return plan.features || {}; } return {}; } catch (error) { console.error('Failed to get plan features:', error); return {}; } } /** * Check if a plan has a specific feature */ async planHasFeature(planKey: string, featureName: string) { try { const features = await this.getPlanFeatures(planKey); return featureName in features; } catch (error) { console.error('Failed to check plan feature:', error); return false; } } } export const subscriptionService = new SubscriptionService();