//
//  BlackjackTests.swift
//  BlackjackTests
//
//  Created by Matt Bruce on 12/17/25.
//

import Testing
@testable import Blackjack
import CasinoKit

struct BlackjackTests {

    @Test func example() async throws {
        // Write your test here and use APIs like `#expect(...)` to check expected conditions.
    }
    
    // MARK: - Hand Value Tests
    
    @Test @MainActor func handValueCalculation() async throws {
        var hand = BlackjackHand()
        
        // Test empty hand
        #expect(hand.value == 0, "Empty hand should have value 0")
        
        // Test simple values
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .five),
            Card(suit: .hearts, rank: .seven)
        ])
        #expect(hand.value == 12, "5 + 7 should equal 12")
        #expect(!hand.isSoft, "Hand without ace should not be soft")
        
        // Test face cards
        hand = BlackjackHand(cards: [
            Card(suit: .clubs, rank: .king),
            Card(suit: .diamonds, rank: .queen)
        ])
        #expect(hand.value == 20, "King (10) + Queen (10) should equal 20")
        
        // Test blackjack
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .king)
        ])
        #expect(hand.value == 21, "Ace + King should equal 21")
        #expect(hand.isBlackjack, "Ace + King should be blackjack")
        #expect(hand.isSoft, "Blackjack hand should be soft")
        
        // Test soft hand (ace as 11)
        hand = BlackjackHand(cards: [
            Card(suit: .diamonds, rank: .ace),
            Card(suit: .clubs, rank: .six)
        ])
        #expect(hand.value == 17, "Ace + 6 should equal soft 17")
        #expect(hand.isSoft, "Ace + 6 should be soft")
        
        // Test hard hand (ace as 1)
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .five),
            Card(suit: .clubs, rank: .nine)
        ])
        #expect(hand.value == 15, "Ace + 5 + 9 = 15 (ace counts as 1)")
        #expect(!hand.isSoft, "Hand over 21 if ace=11 should be hard")
        
        // Test bust
        hand = BlackjackHand(cards: [
            Card(suit: .diamonds, rank: .king),
            Card(suit: .clubs, rank: .queen),
            Card(suit: .hearts, rank: .five)
        ])
        #expect(hand.value == 25, "10 + 10 + 5 = 25")
        #expect(hand.isBusted, "Hand over 21 should be busted")
        
        // Test multiple aces
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .ace)
        ])
        #expect(hand.value == 12, "Ace + Ace = 12 (one as 11, one as 1)")
        #expect(hand.isSoft, "Two aces should be soft")
        
        // Test three aces
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .ace),
            Card(suit: .clubs, rank: .ace)
        ])
        #expect(hand.value == 13, "Three aces = 13 (one as 11, two as 1)")
        #expect(hand.isSoft, "Three aces should be soft")
    }
    
    @Test @MainActor func handBlackjackDetection() async throws {
        // Natural blackjack
        var hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .ten)
        ])
        #expect(hand.isBlackjack, "Ace + 10 should be blackjack")
        
        // 21 with three cards is not blackjack
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .seven),
            Card(suit: .hearts, rank: .seven),
            Card(suit: .clubs, rank: .seven)
        ])
        #expect(hand.value == 21, "Should have value 21")
        #expect(!hand.isBlackjack, "21 with 3 cards is not blackjack")
        
        // Split hand 21 is not blackjack
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .king)
        ], bet: 100)
        hand.isSplit = true
        #expect(hand.value == 21, "Should have value 21")
        #expect(!hand.isBlackjack, "21 after split is not blackjack")
    }
    
    @Test @MainActor func handSplitDetection() async throws {
        // Splittable pair
        var hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .eight),
            Card(suit: .hearts, rank: .eight)
        ])
        #expect(hand.canSplit, "Pair of 8s should be splittable")
        
        // Face cards of same rank can split
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .king),
            Card(suit: .hearts, rank: .king)
        ])
        #expect(hand.canSplit, "Pair of Kings should be splittable")
        
        // Face cards of different ranks cannot split
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .king),
            Card(suit: .hearts, rank: .queen)
        ])
        #expect(!hand.canSplit, "King + Queen should not be splittable (different ranks)")
        
        // Not splittable - different ranks
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .nine),
            Card(suit: .hearts, rank: .ten)
        ])
        #expect(!hand.canSplit, "9 + 10 should not be splittable")
        
        // Not splittable - more than 2 cards
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .five),
            Card(suit: .hearts, rank: .five),
            Card(suit: .clubs, rank: .ace)
        ])
        #expect(!hand.canSplit, "Hand with 3 cards should not be splittable")
    }
    
    @Test @MainActor func handDoubleDownDetection() async throws {
        // Can double down with 2 cards
        var hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .five),
            Card(suit: .hearts, rank: .six)
        ])
        #expect(hand.canDoubleDown, "Should be able to double down with 2 cards")
        
        // Cannot double down after already doubled
        hand.isDoubledDown = true
        #expect(!hand.canDoubleDown, "Should not be able to double down twice")
        
        // Cannot double down with 3+ cards
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .five),
            Card(suit: .hearts, rank: .six),
            Card(suit: .clubs, rank: .two)
        ])
        #expect(!hand.canDoubleDown, "Should not be able to double down with 3 cards")
    }
    
    @Test @MainActor func handHitDetection() async throws {
        // Can hit with normal hand
        var hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .five),
            Card(suit: .hearts, rank: .six)
        ])
        #expect(hand.canHit, "Should be able to hit with 11")
        
        // Cannot hit after standing
        hand.isStanding = true
        #expect(!hand.canHit, "Should not be able to hit after standing")
        
        // Cannot hit when busted
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .king),
            Card(suit: .hearts, rank: .queen),
            Card(suit: .clubs, rank: .five)
        ])
        #expect(!hand.canHit, "Should not be able to hit when busted")
        
        // Cannot hit with blackjack
        hand = BlackjackHand(cards: [
            Card(suit: .spades, rank: .ace),
            Card(suit: .hearts, rank: .king)
        ])
        #expect(!hand.canHit, "Should not be able to hit with blackjack")
    }
    
    // MARK: - Card Value Tests
    
    @Test func cardHiLoValues() async throws {
        // Low cards (2-6) = +1
        #expect(Card(suit: .spades, rank: .two).hiLoValue == 1)
        #expect(Card(suit: .hearts, rank: .three).hiLoValue == 1)
        #expect(Card(suit: .clubs, rank: .four).hiLoValue == 1)
        #expect(Card(suit: .diamonds, rank: .five).hiLoValue == 1)
        #expect(Card(suit: .spades, rank: .six).hiLoValue == 1)
        
        // Neutral cards (7-9) = 0
        #expect(Card(suit: .hearts, rank: .seven).hiLoValue == 0)
        #expect(Card(suit: .clubs, rank: .eight).hiLoValue == 0)
        #expect(Card(suit: .diamonds, rank: .nine).hiLoValue == 0)
        
        // High cards (10, J, Q, K, A) = -1
        #expect(Card(suit: .spades, rank: .ten).hiLoValue == -1)
        #expect(Card(suit: .hearts, rank: .jack).hiLoValue == -1)
        #expect(Card(suit: .clubs, rank: .queen).hiLoValue == -1)
        #expect(Card(suit: .diamonds, rank: .king).hiLoValue == -1)
        #expect(Card(suit: .spades, rank: .ace).hiLoValue == -1)
    }
    
    // MARK: - Game State Tests
    
    @Test @MainActor func gameStateInitialization() async throws {
        let state = GameState(settings: GameSettings())
        
        // Initial state
        #expect(state.balance == 10000, "Should start with $10,000")
        #expect(state.currentBet == 0, "Should start with no bet")
        #expect(state.playerHands.isEmpty, "Should start with no hands")
        #expect(state.dealerHand.cards.isEmpty, "Should start with empty dealer hand")
        
        // Check phase enum
        switch state.currentPhase {
        case .betting:
            break // Expected
        default:
            Issue.record("Should start in betting phase")
        }
    }
    
    @Test @MainActor func gameStateBetting() async throws {
        let state = GameState(settings: GameSettings())
        
        // Place a bet
        state.placeBet(amount: 100)
        #expect(state.currentBet == 100, "Bet should be placed")
        #expect(state.balance == 9900, "Balance should decrease")
        
        // Add to bet
        state.placeBet(amount: 50)
        #expect(state.currentBet == 150, "Bet should increase")
        #expect(state.balance == 9850, "Balance should decrease more")
        
        // Clear bet
        state.clearBet()
        #expect(state.currentBet == 0, "Bet should be cleared")
        #expect(state.balance == 10000, "Balance should be restored")
    }
    
    @Test @MainActor func gameStateDealing() async throws {
        let state = GameState(settings: GameSettings())
        
        // Place bet and deal
        state.placeBet(amount: 100)
        await state.deal()
        
        // Verify hands were dealt
        #expect(state.playerHands.count == 1, "Should have 1 player hand")
        #expect(state.playerHands[0].cards.count == 2, "Player should have 2 cards")
        #expect(state.dealerHand.cards.count == 2, "Dealer should have 2 cards")
        
        // Verify values are valid
        #expect(state.playerHands[0].value >= 2, "Player hand should have valid value")
        #expect(state.playerHands[0].value <= 21, "Player hand should not exceed 21 initially")
    }

}
