//
//  GameSettings.swift
//  Baccarat
//
//  User-configurable game settings.
//

import Foundation
import SwiftUI
import CasinoKit

/// The number of decks available for the shoe.
enum DeckCount: Int, CaseIterable, Identifiable {
    case one = 1
    case six = 6
    case eight = 8
    
    var id: Int { rawValue }
    
    var displayName: String {
        switch self {
        case .one: return "1 Deck"
        case .six: return "6 Decks"
        case .eight: return "8 Decks (Standard)"
        }
    }
    
    var description: String {
        switch self {
        case .one: return "Rare, for private games"
        case .six: return "Common in mini baccarat"
        case .eight: return "Casino standard"
        }
    }
}

/// The style used to reveal cards.
enum RevealStyle: String, CaseIterable, Codable, Identifiable {
    case auto           // Automatically reveal (original behavior)
    case tap            // Tap each card to reveal
    case squeeze        // Tap + Hold / Drag for pressure-based reveal
    
    var id: String { rawValue } // Keep Identifiable conformance for SwiftUI
    
    var displayName: String {
        switch self {
        case .auto: return String(localized: "Auto Reveal")
        case .tap: return String(localized: "Tap Reveal")
        case .squeeze: return String(localized: "Squeeze Reveal")
        }
    }
    
    var helpText: String {
        switch self {
        case .auto: return String(localized: "Cards flip automatically")
        case .tap: return String(localized: "Tap each card to reveal")
        case .squeeze: return String(localized: "Squeeze card to peek at value")
        }
    }
    
    var iconName: String {
        switch self {
        case .auto: return "bolt.fill"
        case .tap: return "hand.tap.fill"
        case .squeeze: return "hand.point.up.left.and.text"
        }
    }
}

// TableLimits is now provided by CasinoKit

/// Observable settings class for Baccarat configuration.
/// Conforms to GameSettingsProtocol for shared settings behavior.
@Observable
@MainActor
final class GameSettings: GameSettingsProtocol {
    // MARK: - Deck Settings
    
    /// Number of decks in the shoe.
    var deckCount: DeckCount = .eight
    
    // MARK: - Betting Limits
    
    /// The table limits preset.
    var tableLimits: TableLimits = .casual
    
    /// Minimum bet amount.
    var minBet: Int {
        tableLimits.minBet
    }
    
    /// Maximum bet amount per betting spot.
    var maxBet: Int {
        tableLimits.maxBet
    }
    
    // MARK: - Starting Balance
    
    /// The starting balance for new games.
    var startingBalance: Int = 1_000
    
    // MARK: - Animation Settings
    
    /// Whether to show dealing animations.
    var showAnimations: Bool = true
    
    /// Speed of card dealing (uses CasinoDesign.DealingSpeed constants)
    var dealingSpeed: Double = CasinoDesign.DealingSpeed.normal
    
    /// The style used to reveal cards.
    var revealStyle: RevealStyle = .auto
    
    // MARK: - Display Settings
    
    /// Whether to show the cards remaining indicator.
    var showCardsRemaining: Bool = true
    
    /// Whether to show the history road map.
    var showHistory: Bool = true
    
    /// Whether to show betting hints and recommendations.
    var showHints: Bool = true
    
    // MARK: - Road Map Settings
    
    /// The preferred road type to display.
    var preferredRoadType: RoadType = .big
    
    /// Whether to show streak alerts.
    var showStreakAlerts: Bool = true
    
    // MARK: - Sound Settings
    
    /// Whether sound effects are enabled.
    var soundEnabled: Bool = true
    
    /// Whether haptic feedback is enabled.
    var hapticsEnabled: Bool = true
    
    /// Volume level for sound effects (0.0 to 1.0).
    var soundVolume: Float = 1.0
    
    // MARK: - Persistence Keys
    
    private enum Keys {
        static let deckCount = "settings.deckCount"
        static let tableLimits = "settings.tableLimits"
        static let startingBalance = "settings.startingBalance"
        static let showAnimations = "settings.showAnimations"
        static let dealingSpeed = "settings.dealingSpeed"
        static let showCardsRemaining = "settings.showCardsRemaining"
        static let showHistory = "settings.showHistory"
        static let showHints = "settings.showHints"
        static let soundEnabled = "settings.soundEnabled"
        static let hapticsEnabled = "settings.hapticsEnabled"
        static let soundVolume = "settings.soundVolume"
        static let revealStyle = "settings.revealStyle"
        static let preferredRoadType = "settings.preferredRoadType"
        static let showStreakAlerts = "settings.showStreakAlerts"
    }
    
    // MARK: - iCloud
    
    /// Cached reference to iCloud store, lazily initialized
    private var _iCloudStore: NSUbiquitousKeyValueStore?
    private var _iCloudStoreInitialized = false
    
    private var iCloudStore: NSUbiquitousKeyValueStore? {
        // Return cached value if already attempted initialization
        if _iCloudStoreInitialized {
            return _iCloudStore
        }
        
        _iCloudStoreInitialized = true
        
        // Only access the store if iCloud is actually available
        guard iCloudAvailable else {
            return nil
        }
        
        _iCloudStore = NSUbiquitousKeyValueStore.default
        return _iCloudStore
    }
    
    /// Whether iCloud is available.
    var iCloudAvailable: Bool {
        // NSUbiquitousKeyValueStore only requires iCloud sign-in (token)
        // It does NOT require iCloud Drive/Documents to be enabled
        FileManager.default.ubiquityIdentityToken != nil
    }
    
    // MARK: - Initialization
    
    init() {
        load()
        
        // Register for iCloud changes (only if available)
        if iCloudAvailable, let store = iCloudStore {
            NotificationCenter.default.addObserver(
                forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
                object: store,
                queue: .main
            ) { [weak self] _ in
                // Already on main queue, safe to call
                self?.loadFromiCloud()
            }
            
            // Trigger iCloud sync
            store.synchronize()
        }
    }
    
    // MARK: - Persistence
    
    /// Loads settings from UserDefaults, then checks iCloud for newer settings.
    func load() {
        // First load from local UserDefaults
        loadFromLocal()
        
        // Then check iCloud for potentially newer settings
        if iCloudAvailable {
            loadFromiCloud()
        }
    }
    
    /// Loads settings from local UserDefaults.
    private func loadFromLocal() {
        let defaults = UserDefaults.standard
        
        if let rawDeckCount = defaults.object(forKey: Keys.deckCount) as? Int,
           let deckCount = DeckCount(rawValue: rawDeckCount) {
            self.deckCount = deckCount
        }
        
        if let rawTableLimits = defaults.string(forKey: Keys.tableLimits),
           let tableLimits = TableLimits(rawValue: rawTableLimits) {
            self.tableLimits = tableLimits
        }
        
        if let balance = defaults.object(forKey: Keys.startingBalance) as? Int {
            self.startingBalance = Self.validatedStartingBalance(balance)
        }
        
        if defaults.object(forKey: Keys.showAnimations) != nil {
            self.showAnimations = defaults.bool(forKey: Keys.showAnimations)
        }
        
        if let speed = defaults.object(forKey: Keys.dealingSpeed) as? Double {
            self.dealingSpeed = Self.validatedDealingSpeed(speed)
        }
        
        if defaults.object(forKey: Keys.showCardsRemaining) != nil {
            self.showCardsRemaining = defaults.bool(forKey: Keys.showCardsRemaining)
        }
        
        if defaults.object(forKey: Keys.showHistory) != nil {
            self.showHistory = defaults.bool(forKey: Keys.showHistory)
        }
        
        if defaults.object(forKey: Keys.showHints) != nil {
            self.showHints = defaults.bool(forKey: Keys.showHints)
        }
        
        if defaults.object(forKey: Keys.soundEnabled) != nil {
            self.soundEnabled = defaults.bool(forKey: Keys.soundEnabled)
        }
        
        if defaults.object(forKey: Keys.hapticsEnabled) != nil {
            self.hapticsEnabled = defaults.bool(forKey: Keys.hapticsEnabled)
        }
        
        if let volume = defaults.object(forKey: Keys.soundVolume) as? Float {
            self.soundVolume = volume
        }
        
        if let rawStyle = defaults.string(forKey: Keys.revealStyle),
           let style = RevealStyle(rawValue: rawStyle) {
            self.revealStyle = style
        }
        
        if let rawRoadType = defaults.string(forKey: Keys.preferredRoadType),
           let roadType = RoadType(rawValue: rawRoadType) {
            self.preferredRoadType = roadType
        }
        
        if defaults.object(forKey: Keys.showStreakAlerts) != nil {
            self.showStreakAlerts = defaults.bool(forKey: Keys.showStreakAlerts)
        }
    }
    
    /// Loads settings from iCloud.
    private func loadFromiCloud() {
        guard iCloudAvailable, let store = iCloudStore else { return }
        
        if let rawDeckCount = store.object(forKey: Keys.deckCount) as? Int,
           let deckCount = DeckCount(rawValue: rawDeckCount) {
            self.deckCount = deckCount
        }
        
        if let rawTableLimits = store.string(forKey: Keys.tableLimits),
           let tableLimits = TableLimits(rawValue: rawTableLimits) {
            self.tableLimits = tableLimits
        }
        
        if let balance = store.object(forKey: Keys.startingBalance) as? Int {
            self.startingBalance = Self.validatedStartingBalance(balance)
        }
        
        if store.object(forKey: Keys.showAnimations) != nil {
            self.showAnimations = store.bool(forKey: Keys.showAnimations)
        }
        
        if let speed = store.object(forKey: Keys.dealingSpeed) as? Double {
            self.dealingSpeed = Self.validatedDealingSpeed(speed)
        }
        
        if store.object(forKey: Keys.showCardsRemaining) != nil {
            self.showCardsRemaining = store.bool(forKey: Keys.showCardsRemaining)
        }
        
        if store.object(forKey: Keys.showHistory) != nil {
            self.showHistory = store.bool(forKey: Keys.showHistory)
        }
        
        if store.object(forKey: Keys.showHints) != nil {
            self.showHints = store.bool(forKey: Keys.showHints)
        }
        
        if store.object(forKey: Keys.soundEnabled) != nil {
            self.soundEnabled = store.bool(forKey: Keys.soundEnabled)
        }
        
        if store.object(forKey: Keys.hapticsEnabled) != nil {
            self.hapticsEnabled = store.bool(forKey: Keys.hapticsEnabled)
        }
        
        if let volume = store.object(forKey: Keys.soundVolume) as? Double {
            self.soundVolume = Float(volume)
        }
        
        if let rawStyle = store.string(forKey: Keys.revealStyle),
           let style = RevealStyle(rawValue: rawStyle) {
            self.revealStyle = style
        }
        
        if let rawRoadType = store.string(forKey: Keys.preferredRoadType),
           let roadType = RoadType(rawValue: rawRoadType) {
            self.preferredRoadType = roadType
        }
        
        if store.object(forKey: Keys.showStreakAlerts) != nil {
            self.showStreakAlerts = store.bool(forKey: Keys.showStreakAlerts)
        }
    }
    
    /// Saves settings to UserDefaults and iCloud.
    func save() {
        // Save to local UserDefaults
        let defaults = UserDefaults.standard
        defaults.set(deckCount.rawValue, forKey: Keys.deckCount)
        defaults.set(tableLimits.rawValue, forKey: Keys.tableLimits)
        defaults.set(startingBalance, forKey: Keys.startingBalance)
        defaults.set(showAnimations, forKey: Keys.showAnimations)
        defaults.set(dealingSpeed, forKey: Keys.dealingSpeed)
        defaults.set(showCardsRemaining, forKey: Keys.showCardsRemaining)
        defaults.set(showHistory, forKey: Keys.showHistory)
        defaults.set(showHints, forKey: Keys.showHints)
        defaults.set(soundEnabled, forKey: Keys.soundEnabled)
        defaults.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
        defaults.set(soundVolume, forKey: Keys.soundVolume)
        defaults.set(revealStyle.rawValue, forKey: Keys.revealStyle)
        defaults.set(preferredRoadType.rawValue, forKey: Keys.preferredRoadType)
        defaults.set(showStreakAlerts, forKey: Keys.showStreakAlerts)
        
        // Also save to iCloud
        if iCloudAvailable, let store = iCloudStore {
            store.set(deckCount.rawValue, forKey: Keys.deckCount)
            store.set(tableLimits.rawValue, forKey: Keys.tableLimits)
            store.set(startingBalance, forKey: Keys.startingBalance)
            store.set(showAnimations, forKey: Keys.showAnimations)
            store.set(dealingSpeed, forKey: Keys.dealingSpeed)
            store.set(showCardsRemaining, forKey: Keys.showCardsRemaining)
            store.set(showHistory, forKey: Keys.showHistory)
            store.set(showHints, forKey: Keys.showHints)
            store.set(soundEnabled, forKey: Keys.soundEnabled)
            store.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
            store.set(Double(soundVolume), forKey: Keys.soundVolume)
            store.set(revealStyle.rawValue, forKey: Keys.revealStyle)
            store.set(preferredRoadType.rawValue, forKey: Keys.preferredRoadType)
            store.set(showStreakAlerts, forKey: Keys.showStreakAlerts)
            store.synchronize()
        }
    }
    
    /// Validates a dealing speed value, returning normal if invalid.
    /// This handles legacy data that may have been saved with incorrect values.
    private static func validatedDealingSpeed(_ speed: Double) -> Double {
        let validSpeeds = [
            CasinoDesign.DealingSpeed.fast,
            CasinoDesign.DealingSpeed.normal,
            CasinoDesign.DealingSpeed.slow
        ]
        return validSpeeds.contains(speed) ? speed : CasinoDesign.DealingSpeed.normal
    }
    
    /// Validates a starting balance value, returning default if invalid.
    /// This handles legacy data that may have been saved with incorrect values.
    private static func validatedStartingBalance(_ balance: Int) -> Int {
        let validBalances = [1_000, 5_000, 10_000, 25_000, 50_000, 100_000]
        return validBalances.contains(balance) ? balance : 1_000
    }
    
    /// Resets all settings to defaults.
    func resetToDefaults() {
        deckCount = .eight
        tableLimits = .casual
        startingBalance = 1_000
        showAnimations = true
        dealingSpeed = CasinoDesign.DealingSpeed.normal
        showCardsRemaining = true
        showHistory = true
        showHints = true
        soundEnabled = true
        hapticsEnabled = true
        soundVolume = 1.0
        revealStyle = .auto
        preferredRoadType = .big
        showStreakAlerts = true
        save()
    }
}
