//
//  CasinoGameState.swift
//  CasinoKit
//
//  Protocol defining common state management patterns for casino games.
//  Extends SessionManagedGame with additional shared functionality.
//

import Foundation

/// Protocol for casino game state classes that manage game flow,
/// session tracking, and common behaviors.
///
/// This extends `SessionManagedGame` to add additional requirements
/// that are common across all casino games in this app.
@MainActor
public protocol CasinoGameState: SessionManagedGame {
    /// The settings object conforming to GameSettingsProtocol.
    associatedtype GameSettingsType: GameSettingsProtocol
    
    /// Game-specific settings.
    var settings: GameSettingsType { get }
    
    /// The shared sound manager.
    var soundManager: SoundManager { get }
    
    /// Onboarding state for first-time users.
    var onboarding: OnboardingState { get }
    
    /// Resets the game to starting conditions (balance, cards, etc.)
    /// without clearing session history.
    func resetGame()
    
    /// Clears all data including session history.
    func clearAllData()
    
    /// Aggregated statistics from all sessions.
    var aggregatedStats: AggregatedSessionStats { get }
    
    // MARK: - Chip Selection
    
    /// The minimum bet level across all bet types.
    /// Used by ChipSelectorView to determine if chips should be enabled.
    /// Returns the smallest bet so chips stay enabled if ANY bet type can accept more.
    ///
    /// Games with multiple bet types (main bet, side bets) should return the minimum
    /// bet amount across all placeable bet types, ensuring the chip selector remains
    /// responsive as long as at least one bet type has room for more chips.
    var minBetForChipSelector: Int { get }
}

// MARK: - Default Implementations

public extension CasinoGameState {
    /// Default implementation uses shared SoundManager.
    var soundManager: SoundManager { SoundManager.shared }
    
    /// Default aggregated stats computed from session history.
    var aggregatedStats: AggregatedSessionStats {
        sessionHistory.aggregatedStats()
    }
    
    /// Play a sound effect if enabled.
    func playSound(_ sound: GameSound) {
        soundManager.play(sound)
    }
    
    /// Play light haptic feedback if enabled.
    func hapticLight() {
        soundManager.hapticLight()
    }
    
    /// Play medium haptic feedback if enabled.
    func hapticMedium() {
        soundManager.hapticMedium()
    }
    
    /// Play success haptic feedback if enabled.
    func hapticSuccess() {
        soundManager.hapticSuccess()
    }
    
    /// Play error haptic feedback if enabled.
    func hapticError() {
        soundManager.hapticError()
    }
    
    /// Convenience: Check if we should play sounds.
    var isSoundEnabled: Bool {
        settings.soundEnabled && soundManager.soundEnabled
    }
    
    /// Convenience: Check if we should play haptics.
    var isHapticsEnabled: Bool {
        settings.hapticsEnabled && soundManager.hapticsEnabled
    }
}

// MARK: - Balance Management Extensions

public extension CasinoGameState {
    /// Whether the player is out of chips (balance is 0 or below minimum bet).
    var isBroke: Bool {
        balance < settings.minBet
    }
    
    /// Whether the player can afford a bet of the given amount.
    func canAffordBet(_ amount: Int) -> Bool {
        balance >= amount
    }
    
    /// The maximum bet the player can currently place.
    var maxAffordableBet: Int {
        min(balance, settings.maxBet)
    }
}

// MARK: - Game Flow Extensions

public extension CasinoGameState {
    /// Ends the current session due to running out of chips.
    func endSessionDueToBroke() {
        if currentSession != nil {
            endCurrentSession(reason: .brokeOut)
        }
    }
    
    /// Checks if the player is broke and handles session ending if needed.
    /// Returns true if the player is broke.
    @discardableResult
    func checkBrokeStatus() -> Bool {
        if isBroke {
            endSessionDueToBroke()
            return true
        }
        return false
    }
}

// MARK: - Game Reset Extensions

public extension CasinoGameState {
    /// Default implementation of resetGame that properly handles session ending.
    /// Games can override this but should call the helper methods in the correct order.
    ///
    /// The correct order is:
    /// 1. End current session (captures actual ending balance)
    /// 2. Reset balance to starting balance
    /// 3. Reset game-specific state (via resetForNewSession)
    /// 4. Start new session with fresh balance
    /// 5. Save data
    func performResetGame() {
        // 1. End current session FIRST while balance still shows actual state
        // Use .brokeOut if balance is 0, otherwise .manualEnd
        if currentSession != nil {
            let reason: SessionEndReason = balance <= 0 ? .brokeOut : .manualEnd
            endCurrentSession(reason: reason)
        }
        
        // 2. Reset balance to starting balance
        balance = startingBalance
        
        // 3. Let game reset its specific state (reshuffle deck, clear history, etc.)
        resetForNewSession()
        
        // 4. Create new session with fresh balance
        currentSession = GameSession<Stats>(
            gameStyle: currentGameStyle,
            startingBalance: balance
        )
        
        // 5. Save
        saveGameData()
    }
}

