//
//  GameState.swift
//  Blackjack
//
//  Manages the game state machine for Blackjack.
//

import SwiftUI
import CasinoKit

/// Current phase of the game.
enum GamePhase: Equatable {
    case betting
    case dealing
    case insurance
    case playerTurn(handIndex: Int)
    case dealerTurn
    case roundComplete
}

/// Main game state manager.
@Observable
@MainActor
final class GameState {
    // MARK: - Core State
    
    /// Current player balance.
    private(set) var balance: Int
    
    /// Current game phase.
    private(set) var currentPhase: GamePhase = .betting
    
    /// The current bet amount (before deal).
    var currentBet: Int = 0
    
    /// Insurance bet amount.
    var insuranceBet: Int = 0
    
    // MARK: - Side Bets
    
    /// Perfect Pairs side bet amount.
    var perfectPairsBet: Int = 0
    
    /// 21+3 side bet amount.
    var twentyOnePlusThreeBet: Int = 0
    
    /// Result of Perfect Pairs bet (set after deal).
    var perfectPairsResult: PerfectPairsResult?
    
    /// Result of 21+3 bet (set after deal).
    var twentyOnePlusThreeResult: TwentyOnePlusThreeResult?
    
    /// Whether to show side bet toast notifications.
    var showSideBetToasts: Bool = false
    
    /// Whether to show the gameplay hint toast.
    var showHintToast: Bool = false
    
    /// Tracks the current hint display session to prevent race conditions.
    /// When a new hint arrives or is shown, increment this so old dismiss tasks become stale.
    var hintDisplayID: UUID = UUID()
    
    /// Whether a reshuffle notification should be shown.
    var showReshuffleNotification: Bool = false
    
    // MARK: - Hands
    
    /// Player's hands (can have multiple after splits).
    private(set) var playerHands: [BlackjackHand] = []
    
    /// Dealer's hand.
    private(set) var dealerHand: BlackjackHand = BlackjackHand()
    
    /// Index of the hand currently being played.
    private(set) var activeHandIndex: Int = 0
    
    /// The active player hand.
    var activeHand: BlackjackHand? {
        guard activeHandIndex < playerHands.count else { return nil }
        return playerHands[activeHandIndex]
    }
    
    /// Dealer's face-up card.
    var dealerUpCard: Card? {
        dealerHand.cards.first
    }
    
    // MARK: - UI State
    
    /// Whether to show the result banner.
    var showResultBanner: Bool = false
    
    /// The result of the last round.
    private(set) var lastRoundResult: RoundResult?
    
    /// Round history for statistics.
    private(set) var roundHistory: [RoundResult] = []
    
    // MARK: - Statistics (persisted)
    
    private(set) var totalWinnings: Int = 0
    private(set) var biggestWin: Int = 0
    private(set) var biggestLoss: Int = 0
    private(set) var blackjackCount: Int = 0
    private(set) var bustCount: Int = 0
    
    // MARK: - Persistence
    
    /// iCloud sync manager for game data.
    let persistence: CloudSyncManager<BlackjackGameData>
    
    // MARK: - Engine & Settings
    
    /// The game engine.
    let engine: BlackjackEngine
    
    /// Game settings.
    let settings: GameSettings
    
    /// Sound manager.
    private let sound = SoundManager.shared
    
    // MARK: - Computed Properties
    
    /// Total bet across all hands.
    var totalBet: Int {
        playerHands.reduce(0) { $0 + $1.bet * ($1.isDoubledDown ? 2 : 1) } + insuranceBet
    }
    
    /// Whether player can place a bet.
    /// True if in betting phase, have balance, and haven't hit max bet.
    /// Whether in betting phase (matches Baccarat's canPlaceBet).
    var canPlaceBet: Bool {
        currentPhase == .betting
    }
    
    /// Whether the main bet can accept more chips of the given amount.
    /// Matches Baccarat's canAddToBet pattern.
    func canAddToMainBet(amount: Int) -> Bool {
        currentBet + amount <= settings.maxBet
    }
    
    /// Whether a specific side bet can accept more chips of the given amount.
    /// Matches Baccarat's canAddToBet pattern.
    func canAddToSideBet(type: SideBetType, amount: Int) -> Bool {
        guard settings.sideBetsEnabled else { return false }
        let currentAmount: Int
        switch type {
        case .perfectPairs:
            currentAmount = perfectPairsBet
        case .twentyOnePlusThree:
            currentAmount = twentyOnePlusThreeBet
        }
        return currentAmount + amount <= settings.maxBet
    }
    
    /// Whether a bet type is at max.
    func isAtMax(main: Bool = false, sideType: SideBetType? = nil) -> Bool {
        if main {
            return currentBet >= settings.maxBet
        }
        if let type = sideType {
            switch type {
            case .perfectPairs:
                return perfectPairsBet >= settings.maxBet
            case .twentyOnePlusThree:
                return twentyOnePlusThreeBet >= settings.maxBet
            }
        }
        return false
    }
    
    /// The minimum bet level across all active bet types.
    /// Used by chip selector to determine if chips should be enabled.
    /// Returns the smallest bet so chips stay enabled if ANY bet type can accept more.
    var minBetForChipSelector: Int {
        if settings.sideBetsEnabled {
            // Return the minimum of all bet types so chips stay enabled if any can be increased
            return min(currentBet, perfectPairsBet, twentyOnePlusThreeBet)
        } else {
            return currentBet
        }
    }
    
    /// Whether the current hand can hit.
    var canHit: Bool {
        guard case .playerTurn = currentPhase else { return false }
        return activeHand?.canHit ?? false
    }
    
    /// Whether the current hand can stand.
    var canStand: Bool {
        guard case .playerTurn = currentPhase else { return false }
        return !(activeHand?.isBusted ?? true)
    }
    
    /// Whether the current hand can double.
    var canDouble: Bool {
        guard case .playerTurn = currentPhase else { return false }
        guard let hand = activeHand else { return false }
        return engine.canDoubleDown(hand: hand, balance: balance)
    }
    
    /// Whether the current hand can split.
    var canSplit: Bool {
        guard case .playerTurn = currentPhase else { return false }
        guard let hand = activeHand else { return false }
        let splitCount = playerHands.count - 1
        return engine.canSplit(hand: hand, balance: balance, currentSplitCount: splitCount)
    }
    
    /// Whether the player can surrender.
    var canSurrender: Bool {
        guard case .playerTurn = currentPhase else { return false }
        guard let hand = activeHand else { return false }
        return engine.canSurrender(hand: hand)
    }
    
    /// Whether the game is over (out of money and no active bet).
    var isGameOver: Bool {
        balance < settings.minBet && currentPhase == .betting && currentBet == 0
    }
    
    /// Total rounds played.
    var roundsPlayed: Int {
        roundHistory.count
    }
    
    /// Whether it's currently the player's turn.
    var isPlayerTurn: Bool {
        if case .playerTurn = currentPhase { return true }
        return false
    }
    
    /// Whether the dealer's hole card should be revealed.
    var shouldShowDealerHoleCard: Bool {
        switch currentPhase {
        case .dealerTurn, .roundComplete:
            return true
        default:
            return false
        }
    }
    
    // MARK: - Hints
    
    /// Current gameplay hint based on basic strategy.
    var currentHint: String? {
        guard settings.showHints else { return nil }
        guard isPlayerTurn else { return nil }
        guard let hand = activeHand,
              let upCard = dealerUpCard else { return nil }
        
        // Use count-adjusted hints when card counting is enabled
        if settings.showCardCount {
            return engine.getCountAdjustedHint(playerHand: hand, dealerUpCard: upCard)
        }
        return engine.getHint(playerHand: hand, dealerUpCard: upCard)
    }
    
    /// Whether the current bet is below the minimum required.
    var isBetBelowMinimum: Bool {
        currentBet > 0 && currentBet < settings.minBet
    }
    
    /// Amount needed to reach minimum bet.
    var amountNeededForMinimum: Int {
        max(0, settings.minBet - currentBet)
    }
    
    /// Whether the current bet has reached the maximum.
    var isBetAtMaximum: Bool {
        currentBet >= settings.maxBet
    }
    
    /// Betting recommendation based on the true count.
    var bettingHint: String? {
        guard settings.showCardCount else { return nil }
        guard currentPhase == .betting else { return nil }
        
        let tc = Int(engine.trueCount.rounded())
        
        switch tc {
        case ...(-2):
            return String(localized: "Bet minimum or sit out")
        case -1:
            return String(localized: "Bet minimum")
        case 0:
            return String(localized: "Bet minimum (neutral)")
        case 1:
            return String(localized: "Bet 2x minimum")
        case 2:
            return String(localized: "Bet 4x minimum")
        case 3:
            return String(localized: "Bet 6x minimum")
        case 4:
            return String(localized: "Bet 8x minimum")
        case 5...:
            return String(localized: "Bet maximum!")
        default:
            return nil
        }
    }
    
    // MARK: - Initialization
    
    init(settings: GameSettings) {
        self.settings = settings
        self.balance = settings.startingBalance
        self.engine = BlackjackEngine(settings: settings)
        self.persistence = CloudSyncManager<BlackjackGameData>()
        syncSoundSettings()
        loadSavedGame()
    }
    
    /// Syncs sound settings with SoundManager.
    private func syncSoundSettings() {
        sound.soundEnabled = settings.soundEnabled
        sound.hapticsEnabled = settings.hapticsEnabled
        sound.volume = settings.soundVolume
    }
    
    /// Called when deck count setting changes - reshuffles with new deck count.
    func applyDeckCountChange() {
        engine.reshuffle()
    }
    
    // MARK: - Persistence
    
    /// Loads saved game data from iCloud or local storage.
    private func loadSavedGame() {
        let data = persistence.load()
        self.balance = data.balance
        self.totalWinnings = data.totalWinnings
        self.biggestWin = data.biggestWin
        self.biggestLoss = data.biggestLoss
        self.blackjackCount = data.blackjackCount
        self.bustCount = data.bustCount
        
        // Set up callback for when iCloud data arrives later
        persistence.onCloudDataReceived = { [weak self] newData in
            guard let self else { return }
            self.balance = newData.balance
            self.totalWinnings = newData.totalWinnings
            self.biggestWin = newData.biggestWin
            self.biggestLoss = newData.biggestLoss
            self.blackjackCount = newData.blackjackCount
            self.bustCount = newData.bustCount
        }
    }
    
    /// Saves current game data to iCloud and local storage.
    private func saveGameData() {
        let savedRounds: [SavedRoundResult] = roundHistory.map { result in
            SavedRoundResult(
                date: Date(),
                mainResult: result.mainHandResult.saveName,
                hadSplit: result.hadSplit,
                totalWinnings: result.totalWinnings
            )
        }
        
        let data = BlackjackGameData(
            lastModified: Date(),
            balance: balance,
            roundHistory: savedRounds,
            totalWinnings: totalWinnings,
            biggestWin: biggestWin,
            biggestLoss: biggestLoss,
            blackjackCount: blackjackCount,
            bustCount: bustCount
        )
        persistence.save(data)
    }
    
    /// Clears all saved data.
    func clearAllData() {
        persistence.reset()
        balance = settings.startingBalance
        totalWinnings = 0
        biggestWin = 0
        biggestLoss = 0
        blackjackCount = 0
        bustCount = 0
        roundHistory = []
        newRound()
    }
    
    // MARK: - Betting
    
    /// Places a main bet.
    /// Places a main bet. Matches Baccarat's placeBet pattern.
    func placeBet(amount: Int) {
        guard canPlaceBet else { return }
        guard balance >= amount else { return }
        guard canAddToMainBet(amount: amount) else { return }
        
        currentBet += amount
        balance -= amount
        sound.play(.chipPlace)
    }
    
    /// Places a side bet (Perfect Pairs or 21+3).
    /// Matches Baccarat's placeBet pattern for side bets.
    func placeSideBet(type: SideBetType, amount: Int) {
        guard canPlaceBet else { return }
        guard balance >= amount else { return }
        guard canAddToSideBet(type: type, amount: amount) else { return }
        
        switch type {
        case .perfectPairs:
            perfectPairsBet += amount
        case .twentyOnePlusThree:
            twentyOnePlusThreeBet += amount
        }
        balance -= amount
        sound.play(.chipPlace)
    }
    
    /// Clears all bets (main and side bets).
    func clearBet() {
        balance += currentBet + perfectPairsBet + twentyOnePlusThreeBet
        currentBet = 0
        perfectPairsBet = 0
        twentyOnePlusThreeBet = 0
        sound.play(.chipPlace)
    }
    
    /// Total amount bet (main + side bets).
    var totalBetAmount: Int {
        currentBet + perfectPairsBet + twentyOnePlusThreeBet
    }
    
    // MARK: - Dealing
    
    /// Whether the player can deal (betting phase with valid bet).
    var canDeal: Bool {
        currentPhase == .betting && currentBet >= settings.minBet
    }
    
    /// Deals the initial cards.
    func deal() async {
        guard canDeal else { return }
        
        // Ensure enough cards for a full hand - reshuffle if needed
        if !engine.canDealNewHand {
            engine.reshuffle()
            showReshuffleNotification = true
            
            Task {
                try? await Task.sleep(for: .seconds(2))
                showReshuffleNotification = false
            }
        }
        
        currentPhase = .dealing
        playerHands = [BlackjackHand(bet: currentBet)]
        dealerHand = BlackjackHand()
        activeHandIndex = 0
        insuranceBet = 0
        perfectPairsResult = nil
        twentyOnePlusThreeResult = nil
        
        let delay = settings.showAnimations ? 0.3 * settings.dealingSpeed : 0
        
        // European no-hole-card: deal 3 cards (player, dealer, player)
        // American style: deal 4 cards (player, dealer, player, dealer)
        let cardCount = settings.noHoleCard ? 3 : 4
        
        for i in 0..<cardCount {
            if let card = engine.dealCard() {
                if i % 2 == 0 {
                    playerHands[0].cards.append(card)
                } else {
                    dealerHand.cards.append(card)
                }
                sound.play(.cardDeal)
                if delay > 0 {
                    try? await Task.sleep(for: .seconds(delay))
                }
            }
        }
        
        // Evaluate side bets after initial cards are dealt
        evaluateSideBets()
        
        // Check for insurance offer (only in American style with hole card)
        // Skip if user has opted out of insurance prompts
        if !settings.noHoleCard,
           !settings.neverAskInsurance,
           let upCard = dealerUpCard,
           engine.shouldOfferInsurance(dealerUpCard: upCard) {
            currentPhase = .insurance
            return
        }
        
        // Check for immediate blackjacks (only in American style - European checks after player acts)
        if !settings.noHoleCard {
            await checkForBlackjacks()
        } else {
            // European: just go to player turn (blackjacks checked after player acts)
            if playerHands[0].isBlackjack {
                // Player blackjack - will be handled after dealer gets second card
                currentPhase = .playerTurn(handIndex: 0)
            } else {
                currentPhase = .playerTurn(handIndex: 0)
            }
        }
    }
    
    /// Checks for blackjacks and handles accordingly.
    private func checkForBlackjacks() async {
        let playerBJ = playerHands[0].isBlackjack
        let dealerBJ = dealerHand.isBlackjack
        
        if playerBJ || dealerBJ {
            // Reveal dealer card
            sound.play(.cardFlip)
            
            if playerBJ && dealerBJ {
                // Push
                playerHands[0].result = .push
                await completeRound()
            } else if playerBJ {
                // Player wins
                playerHands[0].result = .blackjack
                await completeRound()
            } else {
                // Dealer wins
                playerHands[0].result = .lose
                await completeRound()
            }
        } else {
            currentPhase = .playerTurn(handIndex: 0)
        }
    }
    
    // MARK: - Insurance
    
    /// Takes insurance bet.
    func takeInsurance() async {
        let insuranceAmount = currentBet / 2
        guard balance >= insuranceAmount else {
            declineInsurance()
            return
        }
        
        insuranceBet = insuranceAmount
        balance -= insuranceAmount
        sound.play(.chipPlace)
        
        // Check dealer blackjack
        if dealerHand.isBlackjack {
            sound.play(.cardFlip)
            // Insurance wins
            let payout = insuranceBet * 3
            balance += payout
            playerHands[0].result = .lose
            await completeRound()
        } else {
            // Insurance loses, continue game
            insuranceBet = 0  // Lost the insurance bet
            await checkForBlackjacks()
        }
    }
    
    /// Declines insurance.
    func declineInsurance() {
        Task {
            await checkForBlackjacks()
        }
    }
    
    /// Declines insurance and sets the "never ask again" preference.
    func neverAskInsurance() {
        settings.neverAskInsurance = true
        declineInsurance()
    }
    
    // MARK: - Player Actions
    
    /// Player hits (takes another card).
    func hit() async {
        guard canHit else { return }
        guard let card = engine.dealCard() else { return }
        
        playerHands[activeHandIndex].cards.append(card)
        sound.play(.cardDeal)
        
        // Check for bust or 21
        if playerHands[activeHandIndex].isBusted {
            playerHands[activeHandIndex].result = .bust
            await moveToNextHand()
        } else if playerHands[activeHandIndex].value == 21 {
            playerHands[activeHandIndex].isStanding = true
            await moveToNextHand()
        }
    }
    
    /// Player stands.
    func stand() async {
        guard canStand else { return }
        
        playerHands[activeHandIndex].isStanding = true
        await moveToNextHand()
    }
    
    /// Player doubles down.
    func doubleDown() async {
        guard canDouble else { return }
        
        let additionalBet = playerHands[activeHandIndex].bet
        balance -= additionalBet
        playerHands[activeHandIndex].isDoubledDown = true
        sound.play(.chipPlace)
        
        // Deal one card and stand
        if let card = engine.dealCard() {
            playerHands[activeHandIndex].cards.append(card)
            sound.play(.cardDeal)
        }
        
        if playerHands[activeHandIndex].isBusted {
            playerHands[activeHandIndex].result = .bust
        } else {
            playerHands[activeHandIndex].isStanding = true
        }
        
        await moveToNextHand()
    }
    
    /// Player splits the hand.
    func split() async {
        guard canSplit else { return }
        
        let originalHand = playerHands[activeHandIndex]
        let splitCard = originalHand.cards[1]
        
        // Create two new hands
        var hand1 = BlackjackHand(cards: [originalHand.cards[0]], bet: originalHand.bet)
        hand1.isSplit = true
        
        var hand2 = BlackjackHand(cards: [splitCard], bet: originalHand.bet)
        hand2.isSplit = true
        
        // Deduct bet for second hand
        balance -= originalHand.bet
        sound.play(.chipPlace)
        
        // Deal one card to each hand
        if let card1 = engine.dealCard() {
            hand1.cards.append(card1)
            sound.play(.cardDeal)
        }
        
        if let card2 = engine.dealCard() {
            hand2.cards.append(card2)
            sound.play(.cardDeal)
        }
        
        // Replace original with split hands
        playerHands.remove(at: activeHandIndex)
        playerHands.insert(hand1, at: activeHandIndex)
        playerHands.insert(hand2, at: activeHandIndex + 1)
        
        // If split aces, typically only one card each and stand
        if originalHand.cards[0].rank == .ace && !settings.resplitAces {
            playerHands[activeHandIndex].isStanding = true
            playerHands[activeHandIndex + 1].isStanding = true
            await moveToNextHand()
        } else {
            currentPhase = .playerTurn(handIndex: activeHandIndex)
        }
    }
    
    /// Player surrenders.
    func surrender() async {
        guard canSurrender else { return }
        
        playerHands[activeHandIndex].result = .surrender
        await completeRound()
    }
    
    // MARK: - Hand Progression
    
    /// Moves to the next hand or dealer turn.
    private func moveToNextHand() async {
        // Check if there are more hands to play
        let nextIndex = activeHandIndex + 1
        if nextIndex < playerHands.count {
            if !playerHands[nextIndex].isStanding && !playerHands[nextIndex].isBusted {
                activeHandIndex = nextIndex
                currentPhase = .playerTurn(handIndex: nextIndex)
                return
            }
        }
        
        // Check if all hands are busted
        let allBusted = playerHands.allSatisfy { $0.isBusted }
        if allBusted {
            await completeRound()
            return
        }
        
        // Dealer's turn
        await dealerTurn()
    }
    
    // MARK: - Dealer Turn
    
    /// Plays out the dealer's hand.
    private func dealerTurn() async {
        currentPhase = .dealerTurn
        
        let delay = settings.showAnimations ? 0.5 * settings.dealingSpeed : 0
        
        // European no-hole-card: deal the second card now
        if settings.noHoleCard && dealerHand.cards.count == 1 {
            if let card = engine.dealCard() {
                dealerHand.cards.append(card)
                sound.play(.cardDeal)
                
                if delay > 0 {
                    try? await Task.sleep(for: .seconds(delay))
                }
            }
            
            // Check for dealer blackjack in European mode
            // Player loses everything (no early check in European)
            if dealerHand.isBlackjack {
                // Mark player hands as lost if they don't have blackjack
                for i in 0..<playerHands.count {
                    if playerHands[i].result == nil {
                        if playerHands[i].isBlackjack {
                            playerHands[i].result = .push
                        } else {
                            playerHands[i].result = .lose
                        }
                    }
                }
                await completeRound()
                return
            }
        } else {
            // American style: reveal hole card
            sound.play(.cardFlip)
            
            if delay > 0 {
                try? await Task.sleep(for: .seconds(delay))
            }
        }
        
        // Dealer draws
        while engine.dealerShouldHit(hand: dealerHand) {
            if let card = engine.dealCard() {
                dealerHand.cards.append(card)
                sound.play(.cardDeal)
                
                if delay > 0 {
                    try? await Task.sleep(for: .seconds(delay))
                }
            }
        }
        
        await completeRound()
    }
    
    // MARK: - Side Bets
    
    /// Evaluates side bets based on the initial deal.
    private func evaluateSideBets() {
        guard settings.sideBetsEnabled else { return }
        
        let playerCards = playerHands[0].cards
        guard playerCards.count >= 2 else { return }
        
        // Evaluate Perfect Pairs
        if perfectPairsBet > 0 {
            perfectPairsResult = SideBetEvaluator.evaluatePerfectPairs(
                card1: playerCards[0],
                card2: playerCards[1]
            )
        }
        
        // Evaluate 21+3 (requires dealer upcard)
        if twentyOnePlusThreeBet > 0, let dealerUpCard = dealerUpCard {
            twentyOnePlusThreeResult = SideBetEvaluator.evaluateTwentyOnePlusThree(
                playerCard1: playerCards[0],
                playerCard2: playerCards[1],
                dealerUpcard: dealerUpCard
            )
        }
        
        // Show toast notifications if any side bets were placed
        if perfectPairsBet > 0 || twentyOnePlusThreeBet > 0 {
            showSideBetToasts = true
            
            // Play sound for side bet result
            let ppWon = perfectPairsResult?.isWin ?? false
            let topWon = twentyOnePlusThreeResult?.isWin ?? false
            if ppWon || topWon {
                sound.play(.win)
            }
            
            // Auto-hide toasts after delay
            Task {
                try? await Task.sleep(for: Design.Toast.duration)
                showSideBetToasts = false
            }
        }
    }
    
    /// Calculates winnings from side bets.
    var sideBetWinnings: Int {
        var winnings = 0
        
        if let ppResult = perfectPairsResult, ppResult.isWin {
            winnings += perfectPairsBet * ppResult.payout
        }
        
        if let topResult = twentyOnePlusThreeResult, topResult.isWin {
            winnings += twentyOnePlusThreeBet * topResult.payout
        }
        
        return winnings
    }
    
    // MARK: - Round Completion
    
    /// Completes the round and calculates payouts.
    private func completeRound() async {
        currentPhase = .roundComplete
        
        var roundWinnings = 0
        var wasBlackjack = false
        var hadBust = false
        var perHandWinnings: [Int] = []
        
        // Evaluate each hand
        for i in 0..<playerHands.count {
            if playerHands[i].result == nil {
                playerHands[i].result = engine.determineResult(
                    playerHand: playerHands[i],
                    dealerHand: dealerHand
                )
            }
            
            if let result = playerHands[i].result {
                let payout = engine.calculatePayout(
                    bet: playerHands[i].bet,
                    result: result,
                    isDoubled: playerHands[i].isDoubledDown
                )
                let totalBet = playerHands[i].bet * (playerHands[i].isDoubledDown ? 2 : 1)
                let handWinnings = payout - totalBet
                
                balance += payout
                roundWinnings += handWinnings
                perHandWinnings.append(handWinnings)
                
                if result == .blackjack {
                    wasBlackjack = true
                }
                if result == .bust {
                    hadBust = true
                }
            } else {
                perHandWinnings.append(0)
            }
        }
        
        // Calculate insurance result
        var insResult: HandResult? = nil
        var insWinnings = 0
        if insuranceBet > 0 {
            if dealerHand.isBlackjack {
                insResult = .insuranceWin
                insWinnings = insuranceBet * 2  // Insurance pays 2:1
                balance += insuranceBet * 3     // Return bet + 2x winnings
                roundWinnings += insWinnings
            } else {
                insResult = .insuranceLose
                insWinnings = -insuranceBet     // Lost insurance bet
                roundWinnings += insWinnings
            }
        }
        
        // Calculate side bet results
        let sideBetsTotal = perfectPairsBet + twentyOnePlusThreeBet
        let sideBetsWon = sideBetWinnings
        if sideBetsWon > 0 {
            balance += sideBetsWon + sideBetsTotal  // Return bet + winnings
            roundWinnings += sideBetsWon
        } else if sideBetsTotal > 0 {
            // Side bets lost - account for the loss in round winnings
            roundWinnings -= sideBetsTotal
        }
        
        // Update statistics
        totalWinnings += roundWinnings
        if roundWinnings > biggestWin {
            biggestWin = roundWinnings
        }
        if roundWinnings < biggestLoss {
            biggestLoss = roundWinnings
        }
        if wasBlackjack {
            blackjackCount += 1
        }
        if hadBust {
            bustCount += 1
        }
        
        // Create round result with all hand results, per-hand winnings, and side bets
        let allHandResults = playerHands.map { $0.result ?? .lose }
        
        // Calculate individual side bet winnings
        let ppWinnings: Int
        if let ppResult = perfectPairsResult, perfectPairsBet > 0 {
            ppWinnings = ppResult.isWin ? perfectPairsBet * ppResult.payout : -perfectPairsBet
        } else {
            ppWinnings = 0
        }
        
        let topWinnings: Int
        if let topResult = twentyOnePlusThreeResult, twentyOnePlusThreeBet > 0 {
            topWinnings = topResult.isWin ? twentyOnePlusThreeBet * topResult.payout : -twentyOnePlusThreeBet
        } else {
            topWinnings = 0
        }
        
        lastRoundResult = RoundResult(
            handResults: allHandResults,
            handWinnings: perHandWinnings,
            insuranceResult: insResult,
            insuranceWinnings: insWinnings,
            perfectPairsResult: perfectPairsBet > 0 ? perfectPairsResult : nil,
            perfectPairsWinnings: ppWinnings,
            twentyOnePlusThreeResult: twentyOnePlusThreeBet > 0 ? twentyOnePlusThreeResult : nil,
            twentyOnePlusThreeWinnings: topWinnings,
            totalWinnings: roundWinnings,
            wasBlackjack: wasBlackjack
        )
        
        roundHistory.append(lastRoundResult!)
        
        // Save game data to iCloud
        saveGameData()
        
        // Play appropriate sound
        if roundWinnings > 0 {
            sound.play(.win)
        } else if roundWinnings < 0 {
            sound.play(.lose)
        } else {
            sound.play(.push)
        }
        
        // Reset bet for next round
        currentBet = 0
        
        showResultBanner = true
        
        // Check if shoe needs reshuffling
        if engine.needsReshuffle {
            engine.reshuffle()
            showReshuffleNotification = true
            
            // Auto-dismiss after a delay
            Task {
                try? await Task.sleep(for: .seconds(2))
                showReshuffleNotification = false
            }
        }
    }
    
    // MARK: - New Round
    
    /// Starts a new round.
    func newRound() {
        // Reset all hand state
        playerHands = []
        dealerHand = BlackjackHand()
        activeHandIndex = 0
        
        // Reset bets
        currentBet = 0
        insuranceBet = 0
        perfectPairsBet = 0
        twentyOnePlusThreeBet = 0
        
        // Reset side bet results
        perfectPairsResult = nil
        twentyOnePlusThreeResult = nil
        showSideBetToasts = false
        
        // Reset UI state
        showResultBanner = false
        lastRoundResult = nil
        currentPhase = .betting
        
        sound.play(.newRound)
    }
    
    // MARK: - Game Reset
    
    /// Resets the entire game (keeps statistics).
    func resetGame() {
        balance = settings.startingBalance
        roundHistory = []
        engine.reshuffle()
        newRound()
        saveGameData()
    }
}

