//
//  SettingsViewModel.swift
//  SelfieCam
//
//  Created by Matt Bruce on 1/4/26.
//

import SwiftUI
import Bedrock
import MijickCamera

// MARK: - Settings ViewModel

/// Observable settings view model with iCloud sync across all devices.
/// Uses Bedrock's CloudSyncManager for automatic synchronization.
/// Premium features are automatically reset to defaults when user doesn't have premium.
///
/// Extensions:
/// - `SettingsViewModel+RingLight.swift` - Ring light settings
/// - `SettingsViewModel+Camera.swift` - Camera settings (flash, HDR, timer, etc.)
/// - `SettingsViewModel+Premium.swift` - Premium/debug toggle and reset logic
/// - `SettingsViewModel+CloudSync.swift` - iCloud sync conformance
@MainActor
@Observable
final class SettingsViewModel: RingLightConfigurable {
    
    // MARK: - Ring Size Limits
    
    /// Minimum ring border size in points
    static let minRingSize: CGFloat = 40
    
    /// Maximum ring border size in points
    static let maxRingSize: CGFloat = 120
    
    /// Default ring border size
    static let defaultRingSize: CGFloat = 40
    
    // MARK: - Premium Constants
    
    /// Default free color ID for non-premium users
    static let defaultFreeColorId = "pureWhite"
    
    /// Premium color IDs that require subscription
    static let premiumColorIds: Set<String> = ["iceBlue", "softPink", "warmAmber", "coolLavender", RingLightColor.customId]
    
    /// Premium timer options that require subscription
    static let premiumTimerOptions: Set<TimerOption> = [.five, .ten]
    
    /// Premium photo quality options that require subscription
    static let premiumPhotoQualities: Set<PhotoQuality> = [.high]
    
    // MARK: - Internal Dependencies
    
    /// Premium manager for checking subscription status
    @ObservationIgnored let premiumManager = PremiumManager()
    
    /// Manages iCloud sync for settings across all devices
    let cloudSync = CloudSyncManager<SyncedSettings>()
    
    /// Debounce task for slider values
    var debounceTask: Task<Void, Never>?
    
    /// Debounce delay for continuous slider updates (in seconds)
    private static let debounceDelay: Duration = .milliseconds(300)
    
    // MARK: - Cached Values (for immediate UI updates)
    
    /// Cached ring size for immediate UI updates (before debounced save)
    var _cachedRingSize: CGFloat?
    
    /// Cached light color ID for immediate UI updates
    var _cachedLightColorId: String?
    
    /// Cached custom color for immediate UI updates
    var _cachedCustomColor: Color?
    
    // MARK: - Premium Status
    
    /// Refresh token that changes when premium status needs to be re-evaluated
    /// Incrementing this forces SwiftUI to re-compute isPremiumUnlocked
    private var premiumRefreshToken: Int = 0
    
    /// Whether the user has premium access (computed from PremiumManager)
    /// This always reads the current status from RevenueCat's cached customer info
    var isPremiumUnlocked: Bool {
        // Access refresh token to create observation dependency
        _ = premiumRefreshToken
        return premiumManager.isPremiumUnlocked
    }
    
    /// Force SwiftUI to re-evaluate premium status
    /// Call this after a purchase completes or when returning from paywall
    func refreshPremiumStatus() {
        premiumRefreshToken += 1
    }
    
    // MARK: - Display Settings
    
    /// Whether the camera preview is flipped to show a true mirror (PREMIUM)
    var isMirrorFlipped: Bool {
        get { PremiumGate.get(cloudSync.data.isMirrorFlipped, default: false, isPremium: isPremiumUnlocked) }
        set { 
            guard PremiumGate.canSet(isPremium: isPremiumUnlocked) else { return }
            updateSettings { $0.isMirrorFlipped = newValue } 
        }
    }
    
    /// Whether skin smoothing filter is enabled (PREMIUM)
    var isSkinSmoothingEnabled: Bool {
        get { PremiumGate.get(cloudSync.data.isSkinSmoothingEnabled, default: false, isPremium: isPremiumUnlocked) }
        set { 
            guard PremiumGate.canSet(isPremium: isPremiumUnlocked) else { return }
            updateSettings { $0.isSkinSmoothingEnabled = newValue } 
        }
    }
    
    /// Whether the grid overlay is visible
    var isGridVisible: Bool {
        get { cloudSync.data.isGridVisible }
        set { updateSettings { $0.isGridVisible = newValue } }
    }
    
    /// Current camera zoom factor
    var currentZoomFactor: Double {
        get { cloudSync.data.currentZoomFactor }
        set { updateSettings { $0.currentZoomFactor = newValue } }
    }
    
    // MARK: - Capture Settings
    
    /// Whether captures are auto-saved to Photo Library
    var isAutoSaveEnabled: Bool {
        get { cloudSync.data.isAutoSaveEnabled }
        set { updateSettings { $0.isAutoSaveEnabled = newValue } }
    }
    
    // MARK: - Initialization
    
    init() {
        // CloudSyncManager handles syncing automatically
        // Premium status is always computed from premiumManager.isPremiumUnlocked
    }
    
    // MARK: - Internal Methods
    
    /// Updates settings and saves to cloud immediately
    func updateSettings(_ transform: (inout SyncedSettings) -> Void) {
        // Since we are using @Observable, we need to ensure the property access is tracked
        // The cloudSync.update call modifies the data, but we need to trigger the observation
        // We wrap the update in a way that SwiftUI's observation system can see the change
        cloudSync.update { settings in
            transform(&settings)
            settings.modificationCount += 1
        }
    }
    
    /// Debounces save operations for continuous values like sliders
    func debouncedSave(key: String, action: @escaping () -> Void) {
        // Cancel any pending debounce
        debounceTask?.cancel()
        
        // Schedule debounced save
        debounceTask = Task {
            try? await Task.sleep(for: Self.debounceDelay)
            
            guard !Task.isCancelled else { return }
            
            action()
        }
    }
    
    // MARK: - Validation
    
    var isValidConfiguration: Bool {
        ringSize >= Self.minRingSize
    }
}
