import Foundation import Combine enum FreemiumLimits { static let maxSources = 5 static let maxHistoricalMonths = 12 } class FreemiumValidator: ObservableObject { private let iapService: IAPService init(iapService: IAPService) { self.iapService = iapService } // MARK: - Source Limits var isPremium: Bool { iapService.isPremium } func canAddSource(currentCount: Int) -> Bool { if iapService.isPremium { return true } return currentCount < FreemiumLimits.maxSources } func remainingSources(currentCount: Int) -> Int { if iapService.isPremium { return Int.max } return max(0, FreemiumLimits.maxSources - currentCount) } var sourceLimit: Int { iapService.isPremium ? Int.max : FreemiumLimits.maxSources } var sourceLimitDescription: String { if iapService.isPremium { return "Unlimited" } return "\(FreemiumLimits.maxSources) sources" } // MARK: - Historical Data Limits func filterSnapshots(_ snapshots: [Snapshot]) -> [Snapshot] { if iapService.isPremium { return snapshots } let cutoffDate = Calendar.current.date( byAdding: .month, value: -FreemiumLimits.maxHistoricalMonths, to: Date() ) ?? Date() return snapshots.filter { $0.date >= cutoffDate } } func isSnapshotAccessible(_ snapshot: Snapshot) -> Bool { if iapService.isPremium { return true } let cutoffDate = Calendar.current.date( byAdding: .month, value: -FreemiumLimits.maxHistoricalMonths, to: Date() ) ?? Date() return snapshot.date >= cutoffDate } var historicalLimit: Int { iapService.isPremium ? Int.max : FreemiumLimits.maxHistoricalMonths } var historicalLimitDescription: String { if iapService.isPremium { return "Full history" } return "Last \(FreemiumLimits.maxHistoricalMonths) months" } // MARK: - Feature Access func canExport() -> Bool { return iapService.isPremium } func canViewPredictions() -> Bool { return iapService.isPremium } func canViewAdvancedCharts() -> Bool { return iapService.isPremium } func canAccessFeature(_ feature: PremiumFeature) -> Bool { if iapService.isPremium { return true } switch feature { case .multipleAccounts: return false case .unlimitedSources: return false case .fullHistory: return false case .advancedCharts: return false case .predictions: return false case .export: return false case .noAds: return false } } // MARK: - Premium Features Enum enum PremiumFeature: String, CaseIterable, Identifiable { case multipleAccounts = "multiple_accounts" case unlimitedSources = "unlimited_sources" case fullHistory = "full_history" case advancedCharts = "advanced_charts" case predictions = "predictions" case export = "export" case noAds = "no_ads" var id: String { rawValue } var title: String { switch self { case .multipleAccounts: return "Multiple Accounts" case .unlimitedSources: return "Unlimited Sources" case .fullHistory: return "Full History" case .advancedCharts: return "Advanced Charts" case .predictions: return "Predictions" case .export: return "Export Data" case .noAds: return "No Ads" } } var icon: String { switch self { case .multipleAccounts: return "person.2" case .unlimitedSources: return "infinity" case .fullHistory: return "clock.arrow.circlepath" case .advancedCharts: return "chart.bar.xaxis" case .predictions: return "wand.and.stars" case .export: return "square.and.arrow.up" case .noAds: return "xmark.circle" } } var description: String { switch self { case .multipleAccounts: return "Track separate portfolios for family or business" case .unlimitedSources: return "Track as many investment sources as you want" case .fullHistory: return "Access your complete investment history" case .advancedCharts: return "5 types of detailed analytics charts" case .predictions: return "AI-powered 12-month forecasts" case .export: return "Export to CSV and JSON formats" case .noAds: return "Ad-free experience forever" } } } // MARK: - Paywall Triggers enum PaywallTrigger: String { case sourceLimit = "source_limit" case historyLimit = "history_limit" case advancedCharts = "advanced_charts" case predictions = "predictions" case export = "export" case settingsUpgrade = "settings_upgrade" case manualTap = "manual_tap" } func shouldShowPaywall(for trigger: PaywallTrigger) -> Bool { guard !iapService.isPremium else { return false } switch trigger { case .sourceLimit, .historyLimit, .advancedCharts, .predictions, .export: return true case .settingsUpgrade, .manualTap: return true } } }