//
//  SideBet.swift
//  Blackjack
//
//  Side bet types and evaluation for Perfect Pairs and 21+3.
//

import Foundation
import CasinoKit

// MARK: - Side Bet Types

/// Available side bet types in Blackjack.
enum SideBetType: String, CaseIterable, Identifiable {
    case perfectPairs = "perfectPairs"
    case twentyOnePlusThree = "21+3"
    
    var id: String { rawValue }
    
    var displayName: String {
        switch self {
        case .perfectPairs: return String(localized: "Perfect Pairs")
        case .twentyOnePlusThree: return String(localized: "21+3")
        }
    }
    
    var shortName: String {
        switch self {
        case .perfectPairs: return "PP"
        case .twentyOnePlusThree: return "21+3"
        }
    }
    
    var description: String {
        switch self {
        case .perfectPairs:
            return String(localized: "Bet on your first two cards forming a pair")
        case .twentyOnePlusThree:
            return String(localized: "Poker hand from your first two cards + dealer's upcard")
        }
    }
}

// MARK: - Perfect Pairs Results

/// Perfect Pairs side bet outcomes.
enum PerfectPairsResult: CaseIterable {
    case perfectPair      // Same rank AND same suit (e.g., K♠ K♠)
    case coloredPair      // Same rank, same color, different suit (e.g., K♠ K♣)
    case mixedPair        // Same rank, different color (e.g., K♠ K♥)
    case noPair           // No pair
    
    var displayName: String {
        switch self {
        case .perfectPair: return String(localized: "Perfect Pair")
        case .coloredPair: return String(localized: "Colored Pair")
        case .mixedPair: return String(localized: "Mixed Pair")
        case .noPair: return String(localized: "No Pair")
        }
    }
    
    /// Payout multiplier (e.g., 25 means 25:1)
    var payout: Int {
        switch self {
        case .perfectPair: return 25
        case .coloredPair: return 12
        case .mixedPair: return 6
        case .noPair: return 0
        }
    }
    
    var isWin: Bool {
        self != .noPair
    }
}

// MARK: - 21+3 Results

/// 21+3 side bet outcomes (poker hands).
enum TwentyOnePlusThreeResult: CaseIterable {
    case suitedTrips      // Three of a kind, same suit (e.g., 7♠ 7♠ 7♠)
    case straightFlush    // Straight + Flush (e.g., 5♠ 6♠ 7♠)
    case threeOfAKind     // Three of a kind, different suits (e.g., 7♠ 7♥ 7♦)
    case straight         // Three consecutive ranks (e.g., 5♠ 6♥ 7♦)
    case flush            // Three cards of same suit (e.g., 2♠ 7♠ K♠)
    case nothing          // No qualifying hand
    
    var displayName: String {
        switch self {
        case .suitedTrips: return String(localized: "Suited Trips")
        case .straightFlush: return String(localized: "Straight Flush")
        case .threeOfAKind: return String(localized: "Three of a Kind")
        case .straight: return String(localized: "Straight")
        case .flush: return String(localized: "Flush")
        case .nothing: return String(localized: "No Hand")
        }
    }
    
    /// Payout multiplier (e.g., 100 means 100:1)
    var payout: Int {
        switch self {
        case .suitedTrips: return 100
        case .straightFlush: return 40
        case .threeOfAKind: return 30
        case .straight: return 10
        case .flush: return 5
        case .nothing: return 0
        }
    }
    
    var isWin: Bool {
        self != .nothing
    }
}

// MARK: - Side Bet Evaluation

/// Evaluates side bet outcomes.
enum SideBetEvaluator {
    
    /// Evaluates Perfect Pairs bet from player's first two cards.
    static func evaluatePerfectPairs(card1: Card, card2: Card) -> PerfectPairsResult {
        // Must be same rank for any pair
        guard card1.rank == card2.rank else {
            return .noPair
        }
        
        // Perfect Pair: same suit
        if card1.suit == card2.suit {
            return .perfectPair
        }
        
        // Colored Pair: same color (both red or both black), different suit
        if card1.suit.isRed == card2.suit.isRed {
            return .coloredPair
        }
        
        // Mixed Pair: different color
        return .mixedPair
    }
    
    /// Evaluates 21+3 bet from player's first two cards + dealer's upcard.
    static func evaluateTwentyOnePlusThree(playerCard1: Card, playerCard2: Card, dealerUpcard: Card) -> TwentyOnePlusThreeResult {
        let cards = [playerCard1, playerCard2, dealerUpcard]
        let ranks = cards.map { $0.rank }
        let suits = cards.map { $0.suit }
        
        let isFlush = suits.allSatisfy { $0 == suits[0] }
        let isStraight = checkStraight(ranks: ranks)
        let isThreeOfAKind = ranks.allSatisfy { $0 == ranks[0] }
        
        // Check from highest payout to lowest
        if isThreeOfAKind && isFlush {
            return .suitedTrips
        }
        
        if isStraight && isFlush {
            return .straightFlush
        }
        
        if isThreeOfAKind {
            return .threeOfAKind
        }
        
        if isStraight {
            return .straight
        }
        
        if isFlush {
            return .flush
        }
        
        return .nothing
    }
    
    /// Checks if three ranks form a straight (consecutive values).
    /// Handles A-2-3 and Q-K-A straights.
    private static func checkStraight(ranks: [Rank]) -> Bool {
        // Convert to numeric values (Ace can be 1 or 14)
        let values = ranks.map { rankValue($0) }.sorted()
        
        // Check normal straight (consecutive)
        if values[2] - values[1] == 1 && values[1] - values[0] == 1 {
            return true
        }
        
        // Check A-2-3 (values would be [1, 2, 3] after converting Ace to 1)
        // But our rankValue gives Ace = 14, so check [2, 3, 14]
        let sortedRanks = ranks.sorted { rankValue($0) < rankValue($1) }
        let hasAce = sortedRanks.contains(.ace)
        let hasTwo = sortedRanks.contains(.two)
        let hasThree = sortedRanks.contains(.three)
        
        if hasAce && hasTwo && hasThree {
            return true
        }
        
        return false
    }
    
    /// Converts rank to numeric value for straight checking.
    private static func rankValue(_ rank: Rank) -> Int {
        switch rank {
        case .ace: return 14  // High ace for Q-K-A
        case .two: return 2
        case .three: return 3
        case .four: return 4
        case .five: return 5
        case .six: return 6
        case .seven: return 7
        case .eight: return 8
        case .nine: return 9
        case .ten: return 10
        case .jack: return 11
        case .queen: return 12
        case .king: return 13
        }
    }
}

// MARK: - Suit Extension for Color

extension Suit {
    /// Whether this suit is red (hearts, diamonds).
    var isRed: Bool {
        self == .hearts || self == .diamonds
    }
}

