import SwiftUI
import AVFoundation

struct BlackjackGameView: View {
    @EnvironmentObject var gameState: GameState
    
    @State private var cardDealAudio: AVAudioPlayer?
    
    @State private var betIsZero = true
    @State private var bet = 0
    @State private var notEnoughMoney = false
    @State private var playing = false
    @State private var canDoubleDown = false
    @State private var playerStands = false
    @State private var playerCards = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
    @State private var currentPlayedCards = 2
    @State private var dealerCards = ["", "", "", "", "", "", ""]
    @State private var currentDealedCards = 2
    @State private var dealerSum = 0
    @State private var playerSum = 0
    @State private var dealerStands = false
    @State private var playerBust = false
    @State private var dealerBust = false
    @State private var playerHasBlackjack = false
    @State private var playerHas11Ace = false
    @State private var dealerHas11Ace = false
    @State private var cardValue = 0
    
    let cardSuites = ["Hearts", "Spades", "Diamonds", "Clubs"]
    let cardNames = ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]
    
    func initializeSounds() {
        guard let url = Bundle.main.url(forResource: "Card Deal SFX", withExtension: "mp3") else {
            return
        }
        
        cardDealAudio = try? AVAudioPlayer(contentsOf: url)
    }
    
    func generateRandomCard() -> String {
        return cardNames[Int.random(in: 0...12)] + " of " + cardSuites[Int.random(in: 0...3)]
    }
    
    func readCardValue(from string: String) -> Int {
        switch string.first {
        case "A":
            return 1
        case "2":
            return 2
        case "3":
            return 3
        case "4":
            return 4
        case "5":
            return 5
        case "6":
            return 6
        case "7":
            return 7
        case "8":
            return 8
        case "9":
            return 9
        case "1":
            return 10
        case "J":
            return 10
        case "Q":
            return 10
        case "K":
            return 10
        case .none:
            return -1
        default:
            return -1
        }
    }
    
    func calculateValidNextValue(from int: Int, from sum: Int) -> Int {
        if int == 1 && sum <= 10 {
            return 11
        } else {
            return int
        }
    }
    
    func dealDealerCards() -> Void {
        var expectedSum = dealerSum
        var cardsToDealDealer = 0
        var dealerHad11Ace = dealerHas11Ace
        
        for i in 2...7 {            
            if expectedSum >= 17 {
                cardsToDealDealer = i - 2
                
                if i == 2 {
                    dealerStands = true
                    
                    if playerSum > dealerSum {
                        bet *= 2
                    } else if dealerSum > playerSum {
                        bet = 0
                    }
                }
                break
            }
            
            cardValue = calculateValidNextValue(from: readCardValue(from: dealerCards[i]), from: expectedSum)
            
            if expectedSum + cardValue > 21 && dealerHas11Ace {
                expectedSum -= 10
                dealerHas11Ace = false
            }
            
            if cardValue == 11 {
                dealerHas11Ace = true
            }
            
            expectedSum += cardValue
        }
        
        for i in 0..<cardsToDealDealer {
            DispatchQueue.main.asyncAfter(deadline: .now() + Double(i + 1)) {
                cardValue = calculateValidNextValue(from: readCardValue(from: dealerCards[currentDealedCards]), from: dealerSum)
                
                cardDealAudio?.stop()
                cardDealAudio?.currentTime = 0
                cardDealAudio?.play()
                currentDealedCards += 1
                
                if dealerSum + cardValue > 21 && dealerHad11Ace {
                    dealerSum -= 10
                    dealerHad11Ace = false
                }
                
                if cardValue == 11 {
                    dealerHad11Ace = true
                }
                
                dealerSum += cardValue
                
                if dealerSum > 21 {
                    dealerBust = true
                    bet *= 2
                }
                
                if dealerSum >= 17 {
                    dealerStands = true
                    
                    if playerSum > dealerSum {
                        bet *= 2
                    } else if dealerSum > playerSum && dealerSum <= 21 {
                        bet = 0
                    }
                }
            }
        }
    }
    
    var body: some View {
        ZStack {
            Image("Blackjack Cloth")
                .resizable()
                .scaledToFill()
            
            VStack {
                Text("\n")
                
                Text("$\(gameState.money)")
                    .foregroundColor(.black)
                    .font(.system(.title, design: .rounded)).bold()
                    .padding(10)
                    .background(Color.yellow)
                    .cornerRadius(10)
                    .padding(.bottom, 50)
                
                HStack {
                    if !playing {
                        Image("Maroon Rectangle")
                            .resizable()
                            .scaledToFit()
                            .frame(maxWidth: 100)
                            .cornerRadius(15)
                        Image("Maroon Rectangle")
                            .resizable()
                            .scaledToFit()
                            .frame(maxWidth: 100)
                            .cornerRadius(15)
                    } else {                        
                        if playerStands {
                            ForEach(Array(dealerCards.prefix(currentDealedCards)), id: \.self) { dealedCard in
                                Image(dealedCard)
                                    .resizable()
                                    .scaledToFit()
                                    .frame(maxWidth: 100)
                                    .cornerRadius(15)
                            }
                        } else {
                            Image(dealerCards[0])
                                .resizable()
                                .scaledToFit()
                                .frame(maxWidth: 100)
                                .cornerRadius(15)
                            
                            if playerBust || playerHasBlackjack {
                                Image(dealerCards[1])
                                    .resizable()
                                    .scaledToFit()
                                    .frame(maxWidth: 100)
                                    .cornerRadius(15)
                            } else {
                                Image("Card Back")
                                    .resizable()
                                    .scaledToFit()
                                    .frame(maxWidth: 100)
                                    .cornerRadius(15)
                            }
                        }
                    }
                }
                
                if !playing {
                    Text("Dealer\n\nYour cards")
                        .multilineTextAlignment(.center)
                        .font(.system(.title2, design: .rounded)).bold()
                        .foregroundColor(.white)
                } else {
                    Text("Dealer - \(dealerSum)\n\nYour cards - \(playerSum)")
                        .multilineTextAlignment(.center)
                        .font(.system(.title2, design: .rounded)).bold()
                        .foregroundColor(.white)
                }
                
                HStack {
                    if !playing {
                        Image("Maroon Rectangle")
                            .resizable()
                            .scaledToFit()
                            .frame(maxWidth: 100)
                            .cornerRadius(15)
                        Image("Maroon Rectangle")
                            .resizable()
                            .scaledToFit()
                            .frame(maxWidth: 100)
                            .cornerRadius(15)
                    } else {
                        ForEach(Array(playerCards.prefix(currentPlayedCards)), id: \.self) { playerCard in
                            Image(playerCard)
                                .resizable()
                                .scaledToFit()
                                .frame(maxWidth: 100)
                                .cornerRadius(15)
                        }
                    }
                }
                
                Text("\n")
            
                HStack {
                    if !playing {
                        Button {
                            bet -= 10
                        } label: {
                            Text(" -  ")
                                .foregroundColor(.white)
                                .font(.system(.title3, design: .rounded)).bold()
                                .padding(5)
                                .background(Color.brown)
                                .cornerRadius(15)
                        }
                        .disabled(betIsZero)
                    }
                    
                    if dealerStands || playerBust || playerHasBlackjack {
                        if playerHasBlackjack {
                            Text("Blackjack! - You win +$\(bet)")
                                .foregroundColor(.white)
                                .font(.system(.title2, design: .rounded)).bold()
                        } else if playerBust {
                            Text("Player bust! - You loose")
                                .foregroundColor(.white)
                                .font(.system(.title2, design: .rounded)).bold()
                        } else if dealerBust {
                            Text("Dealer bust! - You win +$\(bet)")
                                .foregroundColor(.white)
                                .font(.system(.title2, design: .rounded)).bold()
                        } else {
                            if dealerSum > playerSum {
                                Text("Dealer won! - You lose")
                                    .foregroundColor(.white)
                                    .font(.system(.title2, design: .rounded)).bold()
                            } else if playerSum > dealerSum {
                                Text("Player won! - You win +$\(bet)")
                                    .foregroundColor(.white)
                                    .font(.system(.title2, design: .rounded)).bold()
                            } else {
                                Text("Push! - You win +$\(bet)")
                                    .foregroundColor(.white)
                                    .font(.system(.title2, design: .rounded)).bold()
                            }
                        }
                    } else {
                        Text("$\(bet)")
                            .foregroundColor(.white)
                            .font(.system(.title2, design: .rounded)).bold()
                    }
                    
                if !playing {
                        Button {
                            bet += 10
                        } label: {
                            Text(" +  ")
                                .foregroundColor(.white)
                                .font(.system(.title3, design: .rounded)).bold()
                                .padding(5)
                                .background(Color.brown)
                                .cornerRadius(15)
                        }
                        .disabled(notEnoughMoney)
                        
                        .onChange(of: bet) {
                            betIsZero = bet == 0
                            notEnoughMoney = (bet + 10) > gameState.money
                            canDoubleDown = (bet * 2) <= gameState.money
                        }
                    }
                }
                
                Text("\n")
                
                if !playing {
                    Button {
                        gameState.money -= bet
                        playing = true
                        
                        for i in 0...21 {
                            playerCards[i] = generateRandomCard()
                        }
                        for i in 0...6 {
                            dealerCards[i] = generateRandomCard()
                        }
                        
                        dealerSum = 0
                        playerSum = 0
                        
                        cardValue = calculateValidNextValue(from: readCardValue(from: dealerCards[0]), from: dealerSum)
                        
                        if cardValue == 11 {
                            dealerHas11Ace = true
                        }
                        
                        dealerSum += cardValue
                        
                        cardValue = calculateValidNextValue(from: readCardValue(from: playerCards[0]), from: playerSum)
                        
                        if cardValue == 11 {
                            playerHas11Ace = true
                        }
                        
                        playerSum += cardValue
                        
                        cardValue = calculateValidNextValue(from: readCardValue(from: playerCards[1]), from: playerSum)
                        
                        if cardValue == 11 {
                            playerHas11Ace = true
                        }
                        
                        playerSum += cardValue
                        
                        if playerSum == 21 {
                            playerHasBlackjack = true
                            bet *= 3
                            
                            dealerSum += calculateValidNextValue(from: readCardValue(from: dealerCards[1]), from: dealerSum)
                        }
                        
                        cardDealAudio?.stop()
                        cardDealAudio?.currentTime = 0
                        cardDealAudio?.play()
                        
                    } label: {
                        Text("Bet")
                            .foregroundColor(.black)
                            .font(.system(.title2, design: .rounded)).bold()
                            .padding(10)
                            .background(Color.orange)
                            .cornerRadius(20)
                    }
                    .disabled(betIsZero)
                } else {
                    if dealerStands || playerBust || playerHasBlackjack {
                        HStack {
                            Button {
                                gameState.money += bet
                                dealerStands = false
                                dealerBust = false
                                playerBust = false
                                currentPlayedCards = 2
                                currentDealedCards = 2
                                playerStands = false
                                canDoubleDown = false
                                playing = false
                                betIsZero = true
                                bet = 0
                                
                                if (gameState.money < 10) {
                                    notEnoughMoney = true
                                } else {
                                    notEnoughMoney = false
                                }
                                
                                playerHasBlackjack = false
                                playerHas11Ace = false
                                dealerHas11Ace = false
                                
                            } label: {
                                Text("Continue")
                                    .foregroundColor(.black)
                                    .font(.system(.title2, design: .rounded)).bold()
                                    .padding(10)
                                    .background(Color.red)
                                    .cornerRadius(20)
                            }
                        }
                    } else {
                        HStack {
                            Button {
                                playerStands = true
                                
                                cardValue = calculateValidNextValue(from: readCardValue(from: dealerCards[1]), from: dealerSum)
                                
                                if cardValue == 11 {
                                    dealerHas11Ace = true
                                }
                                
                                dealerSum += cardValue
                                
                                dealDealerCards()
                                
                                cardDealAudio?.stop()
                                cardDealAudio?.currentTime = 0
                                cardDealAudio?.play()
                                
                            } label: {
                                Text("Stand")
                                    .foregroundColor(.black)
                                    .font(.system(.title2, design: .rounded)).bold()
                                    .padding(10)
                                    .background(Color.orange)
                                    .cornerRadius(20)
                            }
                            .disabled(playerStands || playerBust)
                            
                            Button {
                                canDoubleDown = false
                                
                                cardValue = calculateValidNextValue(from: readCardValue(from: playerCards[currentPlayedCards]), from: playerSum)
                                
                                if playerSum + cardValue > 21 && playerHas11Ace {
                                    playerSum -= 10
                                    playerHas11Ace = false
                                }
                                
                                if cardValue == 11 {
                                    playerHas11Ace = true
                                }
                                
                                playerSum += cardValue
                                
                                currentPlayedCards += 1
                                
                                if playerSum > 21 {
                                    playerBust = true
                                    bet = 0
                                    
                                    dealerSum += calculateValidNextValue(from: readCardValue(from: dealerCards[1]), from: dealerSum)
                                }
                                
                                cardDealAudio?.stop()
                                cardDealAudio?.currentTime = 0
                                cardDealAudio?.play()
                                
                            } label: {
                                Text("Hit")
                                    .foregroundColor(.black)
                                    .font(.system(.title2, design: .rounded)).bold()
                                    .padding(10)
                                    .padding(.horizontal, 20)
                                    .background(Color.green)
                                    .cornerRadius(20)
                            }
                            .disabled(playerStands || playerBust)
                            
                            Button {
                                gameState.money -= bet
                                bet = bet * 2
                                playerStands = true
                                
                                cardValue = calculateValidNextValue(from: readCardValue(from: playerCards[currentPlayedCards]), from: playerSum)
                                
                                if playerSum + cardValue > 21 && playerHas11Ace {
                                    playerSum -= 10
                                    playerHas11Ace = false
                                }
                                
                                if cardValue == 11 {
                                    playerHas11Ace = true
                                }
                                
                                playerSum += cardValue
                                
                                currentPlayedCards += 1
                                
                                if playerSum > 21 {
                                    playerBust = true
                                    bet = 0
                                    
                                    dealerSum += calculateValidNextValue(from: readCardValue(from: dealerCards[1]), from: dealerSum)
                                } else {                                
                                    cardValue = calculateValidNextValue(from: readCardValue(from: dealerCards[1]), from: dealerSum)
                                    
                                    if cardValue == 11 {
                                        dealerHas11Ace = true
                                    }
                                    
                                    dealerSum += cardValue
                                    
                                    dealDealerCards()
                                }
                                
                                cardDealAudio?.stop()
                                cardDealAudio?.currentTime = 0
                                cardDealAudio?.play()
                                
                            } label: {
                                Text("Double down")
                                    .foregroundColor(.black)
                                    .font(.system(.title2, design: .rounded)).bold()
                                    .padding(10)
                                    .background(Color.red)
                                    .cornerRadius(20)
                            }
                            .disabled(!canDoubleDown || playerStands || playerBust)
                        }
                    }
                }
                
                Text("\n")
            }
        }
        .onDisappear() {
            cardDealAudio?.stop()
            cardDealAudio?.currentTime = 0
        }
        .onAppear() {
            if (gameState.money < 10) {
                notEnoughMoney = true
            } else {
                notEnoughMoney = false
            }
            
            initializeSounds()
        }
    }
}

#Preview {
    BlackjackGameView()
        .environmentObject(GameState())
}
