// // StatisticsSheetView.swift // Blackjack // // Game statistics and history. // import SwiftUI import CasinoKit struct StatisticsSheetView: View { let state: GameState @Environment(\.dismiss) private var dismiss // MARK: - Computed Stats private var totalRounds: Int { state.roundHistory.count } private var wins: Int { state.roundHistory.filter { $0.mainHandResult.isWin }.count } private var losses: Int { state.roundHistory.filter { $0.mainHandResult == .lose || $0.mainHandResult == .bust }.count } private var pushes: Int { state.roundHistory.filter { $0.mainHandResult == .push }.count } private var blackjacks: Int { state.roundHistory.filter { $0.mainHandResult == .blackjack }.count } private var busts: Int { state.roundHistory.filter { $0.mainHandResult == .bust }.count } private var surrenders: Int { state.roundHistory.filter { $0.mainHandResult == .surrender }.count } private var winRate: Double { guard totalRounds > 0 else { return 0 } return Double(wins) / Double(totalRounds) * 100 } private var totalWinnings: Int { state.roundHistory.reduce(0) { $0 + $1.totalWinnings } } private var biggestWin: Int { state.roundHistory.map { $0.totalWinnings }.filter { $0 > 0 }.max() ?? 0 } private var biggestLoss: Int { state.roundHistory.map { $0.totalWinnings }.filter { $0 < 0 }.min() ?? 0 } var body: some View { SheetContainerView( title: String(localized: "Statistics"), content: { // Session Summary SheetSection(title: String(localized: "SESSION SUMMARY"), icon: "chart.bar.fill") { LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: Design.Spacing.medium) { StatBox(title: String(localized: "Rounds"), value: "\(totalRounds)", color: .white) StatBox(title: String(localized: "Win Rate"), value: formatPercent(winRate), color: winRate >= 50 ? .green : .orange) StatBox(title: String(localized: "Net"), value: formatMoney(totalWinnings), color: totalWinnings >= 0 ? .green : .red) StatBox(title: String(localized: "Balance"), value: "$\(state.balance)", color: Color.Settings.accent) } } // Win Distribution SheetSection(title: String(localized: "OUTCOMES"), icon: "chart.pie.fill") { VStack(spacing: Design.Spacing.small) { OutcomeRow(label: String(localized: "Blackjacks"), count: blackjacks, total: totalRounds, color: .yellow) OutcomeRow(label: String(localized: "Wins"), count: wins - blackjacks, total: totalRounds, color: .green) OutcomeRow(label: String(localized: "Pushes"), count: pushes, total: totalRounds, color: .blue) OutcomeRow(label: String(localized: "Losses"), count: losses - busts, total: totalRounds, color: .orange) OutcomeRow(label: String(localized: "Busts"), count: busts, total: totalRounds, color: .red) if surrenders > 0 { OutcomeRow(label: String(localized: "Surrenders"), count: surrenders, total: totalRounds, color: .gray) } } } // Biggest Swings if totalRounds > 0 { SheetSection(title: String(localized: "BIGGEST SWINGS"), icon: "arrow.up.arrow.down") { HStack(spacing: Design.Spacing.large) { VStack(spacing: Design.Spacing.xSmall) { Text(String(localized: "Best")) .font(.system(size: Design.BaseFontSize.small)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) Text(formatMoney(biggestWin)) .font(.system(size: Design.BaseFontSize.xLarge, weight: .bold, design: .rounded)) .foregroundStyle(.green) } .frame(maxWidth: .infinity) Divider() .frame(height: 40) .background(Color.white.opacity(Design.Opacity.hint)) VStack(spacing: Design.Spacing.xSmall) { Text(String(localized: "Worst")) .font(.system(size: Design.BaseFontSize.small)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) Text(formatMoney(biggestLoss)) .font(.system(size: Design.BaseFontSize.xLarge, weight: .bold, design: .rounded)) .foregroundStyle(.red) } .frame(maxWidth: .infinity) } } } }, onCancel: nil, onDone: { dismiss() }, doneButtonText: String(localized: "Done") ) } private func formatMoney(_ amount: Int) -> String { if amount >= 0 { return "+$\(amount)" } else { return "-$\(abs(amount))" } } private func formatPercent(_ value: Double) -> String { value.formatted(.number.precision(.fractionLength(1))) + "%" } } // MARK: - Stat Box struct StatBox: View { let title: String let value: String let color: Color var body: some View { VStack(spacing: Design.Spacing.xSmall) { Text(title) .font(.system(size: Design.BaseFontSize.small)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) Text(value) .font(.system(size: Design.BaseFontSize.xLarge, weight: .bold, design: .rounded)) .foregroundStyle(color) .lineLimit(1) .minimumScaleFactor(0.7) } .frame(maxWidth: .infinity) .padding(Design.Spacing.medium) .background( RoundedRectangle(cornerRadius: Design.CornerRadius.small) .fill(Color.white.opacity(Design.Opacity.subtle)) ) } } // MARK: - Outcome Row struct OutcomeRow: View { let label: String let count: Int let total: Int let color: Color private var percentage: Double { guard total > 0 else { return 0 } return Double(count) / Double(total) * 100 } private func formatPercentWhole(_ value: Double) -> String { value.formatted(.number.precision(.fractionLength(0))) + "%" } var body: some View { HStack { // Label Text(label) .font(.system(size: Design.BaseFontSize.body)) .foregroundStyle(.white.opacity(Design.Opacity.strong)) Spacer() // Count Text("\(count)") .font(.system(size: Design.BaseFontSize.body, weight: .bold)) .foregroundStyle(color) // Progress bar GeometryReader { geometry in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: Design.CornerRadius.xSmall) .fill(Color.white.opacity(Design.Opacity.subtle)) RoundedRectangle(cornerRadius: Design.CornerRadius.xSmall) .fill(color) .frame(width: geometry.size.width * CGFloat(percentage / 100)) } } .frame(width: 60, height: 8) // Percentage Text(formatPercentWhole(percentage)) .font(.system(size: Design.BaseFontSize.small, design: .rounded)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) .frame(width: 40, alignment: .trailing) } .padding(.vertical, Design.Spacing.xSmall) } } #Preview { StatisticsSheetView(state: GameState(settings: GameSettings())) }