local MonkeyGames = LibStub("AceAddon-3.0"):GetAddon("MonkeyGames")
local L = MonkeyGames.L

local Blackjack = {}
MonkeyGames.Blackjack = Blackjack

-- ========================================================================
-- Card definitions
-- ========================================================================

local SUITS = { "hearts", "diamonds", "clubs", "spades" }
local RANKS = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }

local RANK_VALUES = {
    ["2"] = 2, ["3"] = 3, ["4"] = 4, ["5"] = 5, ["6"] = 6,
    ["7"] = 7, ["8"] = 8, ["9"] = 9, ["10"] = 10,
    ["J"] = 10, ["Q"] = 10, ["K"] = 10, ["A"] = 11,
}

local SUIT_SYMBOLS = {
    hearts = "|cffff4444\226\153\165|r",
    diamonds = "|cffff4444\226\153\166|r",
    clubs = "|cff222222\226\153\163|r",
    spades = "|cff222222\226\153\160|r",
}

local SUIT_COLORS = {
    hearts = { 1, 0.3, 0.3, 1 },
    diamonds = { 1, 0.3, 0.3, 1 },
    clubs = { 0.2, 0.2, 0.2, 1 },
    spades = { 0.2, 0.2, 0.2, 1 },
}

-- ========================================================================
-- Deck management
-- ========================================================================

local function CreateDeck(numDecks)
    numDecks = numDecks or 1
    local deck = {}
    for d = 1, numDecks do
        for _, suit in ipairs(SUITS) do
            for _, rank in ipairs(RANKS) do
                deck[#deck + 1] = { rank = rank, suit = suit }
            end
        end
    end
    return deck
end

local function ShuffleDeck(deck)
    for i = #deck, 2, -1 do
        local j = math.random(i)
        deck[i], deck[j] = deck[j], deck[i]
    end
end

local function DrawCard(state)
    if #state.deck == 0 then
        -- Reshuffle from discard
        state.deck = state.discardPile
        state.discardPile = {}
        ShuffleDeck(state.deck)
    end
    return table.remove(state.deck)
end

-- ========================================================================
-- Hand evaluation
-- ========================================================================

function Blackjack:HandValue(hand)
    local total = 0
    local aces = 0

    for _, card in ipairs(hand) do
        local val = RANK_VALUES[card.rank]
        total = total + val
        if card.rank == "A" then
            aces = aces + 1
        end
    end

    -- Downgrade aces from 11 to 1 if busting
    while total > 21 and aces > 0 do
        total = total - 10
        aces = aces - 1
    end

    return total
end

function Blackjack:IsBust(hand)
    return self:HandValue(hand) > 21
end

function Blackjack:IsBlackjack(hand)
    return #hand == 2 and self:HandValue(hand) == 21
end

function Blackjack:HandString(hand, hideSecond)
    local parts = {}
    for i, card in ipairs(hand) do
        if hideSecond and i == 2 then
            parts[#parts + 1] = "[??]"
        else
            parts[#parts + 1] = card.rank .. (SUIT_SYMBOLS[card.suit] or "")
        end
    end
    return table.concat(parts, " ")
end

-- ========================================================================
-- Game state
-- ========================================================================

function Blackjack:CreateGameState()
    local deck = CreateDeck(2) -- 2-deck shoe
    ShuffleDeck(deck)

    return {
        deck = deck,
        discardPile = {},
        playerHands = { {} },   -- array of hands (for splits)
        dealerHand = {},
        activeHandIndex = 1,
        bet = 100,
        bets = { 100 },         -- per-hand bets (for splits/double)
        balance = 1000,
        phase = "betting",       -- "betting" | "dealing" | "player_turn" | "dealer_turn" | "resolved"
        turn = "w",              -- compatibility field for GameManager
        dealerRevealed = false,
        results = {},            -- per-hand: "win"|"loss"|"push"|"blackjack"|"bust"
        canSplit = false,
        canDoubleDown = true,
        insuranceBet = 0,
        insuranceResult = nil,
    }
end

-- ========================================================================
-- Deal
-- ========================================================================

function Blackjack:Deal(state)
    -- Reset hands
    state.playerHands = { {} }
    state.dealerHand = {}
    state.activeHandIndex = 1
    state.results = {}
    state.dealerRevealed = false
    state.canDoubleDown = true
    state.insuranceBet = 0
    state.insuranceResult = nil

    -- Deduct bet from balance
    state.bets = { state.bet }
    state.balance = state.balance - state.bet

    -- Deal 2 cards each (player, dealer, player, dealer)
    state.playerHands[1][1] = DrawCard(state)
    state.dealerHand[1] = DrawCard(state)
    state.playerHands[1][2] = DrawCard(state)
    state.dealerHand[2] = DrawCard(state)

    -- Check for split possibility
    state.canSplit = (state.playerHands[1][1].rank == state.playerHands[1][2].rank)
        and (state.balance >= state.bet)
        and (#state.playerHands == 1)

    -- Check for dealer blackjack with ace showing
    if state.dealerHand[1].rank == "A" then
        state.phase = "insurance"
        return
    end

    -- Check for immediate blackjack
    if self:IsBlackjack(state.playerHands[1]) then
        state.dealerRevealed = true
        if self:IsBlackjack(state.dealerHand) then
            state.results[1] = "push"
            state.balance = state.balance + state.bet -- return bet
        else
            state.results[1] = "blackjack"
            state.balance = state.balance + state.bet + math.floor(state.bet * 1.5) -- 3:2 payout
        end
        state.phase = "resolved"
        return
    end

    if self:IsBlackjack(state.dealerHand) then
        state.dealerRevealed = true
        state.results[1] = "loss"
        state.phase = "resolved"
        return
    end

    state.phase = "player_turn"
end

-- ========================================================================
-- Player actions
-- ========================================================================

function Blackjack:Hit(state)
    if state.phase ~= "player_turn" then return end

    local hand = state.playerHands[state.activeHandIndex]
    hand[#hand + 1] = DrawCard(state)
    state.canDoubleDown = false
    state.canSplit = false

    if self:IsBust(hand) then
        state.results[state.activeHandIndex] = "bust"
        self:AdvanceHand(state)
    end
end

function Blackjack:Stand(state)
    if state.phase ~= "player_turn" then return end
    self:AdvanceHand(state)
end

function Blackjack:DoubleDown(state)
    if state.phase ~= "player_turn" then return end
    if not state.canDoubleDown then return end
    if state.balance < state.bets[state.activeHandIndex] then return end

    local additionalBet = state.bets[state.activeHandIndex]
    state.balance = state.balance - additionalBet
    state.bets[state.activeHandIndex] = state.bets[state.activeHandIndex] + additionalBet

    local hand = state.playerHands[state.activeHandIndex]
    hand[#hand + 1] = DrawCard(state)

    if self:IsBust(hand) then
        state.results[state.activeHandIndex] = "bust"
    end

    self:AdvanceHand(state)
end

function Blackjack:Split(state)
    if state.phase ~= "player_turn" then return end
    if not state.canSplit then return end
    if state.balance < state.bet then return end

    local hand = state.playerHands[state.activeHandIndex]
    local card2 = table.remove(hand, 2)

    -- Create second hand
    local newHand = { card2 }
    state.playerHands[#state.playerHands + 1] = newHand

    -- Deduct additional bet
    state.balance = state.balance - state.bet
    state.bets[#state.bets + 1] = state.bet

    -- Draw one card for each hand
    hand[#hand + 1] = DrawCard(state)
    newHand[#newHand + 1] = DrawCard(state)

    state.canSplit = false
    state.canDoubleDown = true

    -- If first hand busted, advance
    if self:IsBust(hand) then
        state.results[state.activeHandIndex] = "bust"
        self:AdvanceHand(state)
    end
end

function Blackjack:AcceptInsurance(state)
    if state.phase ~= "insurance" then return end
    state.insuranceBet = math.floor(state.bet / 2)
    state.balance = state.balance - state.insuranceBet
    self:ResolveInsurance(state)
end

function Blackjack:DeclineInsurance(state)
    if state.phase ~= "insurance" then return end
    state.insuranceBet = 0
    self:ResolveInsurance(state)
end

function Blackjack:ResolveInsurance(state)
    if self:IsBlackjack(state.dealerHand) then
        state.dealerRevealed = true
        if state.insuranceBet > 0 then
            state.insuranceResult = "win"
            state.balance = state.balance + state.insuranceBet * 3 -- 2:1 payout + original
        else
            state.insuranceResult = "loss"
        end

        -- Check player blackjack
        if self:IsBlackjack(state.playerHands[1]) then
            state.results[1] = "push"
            state.balance = state.balance + state.bet
        else
            state.results[1] = "loss"
        end
        state.phase = "resolved"
    else
        if state.insuranceBet > 0 then
            state.insuranceResult = "loss"
            -- Insurance bet is lost, continue playing
        end

        -- Check player blackjack
        if self:IsBlackjack(state.playerHands[1]) then
            state.dealerRevealed = true
            state.results[1] = "blackjack"
            state.balance = state.balance + state.bet + math.floor(state.bet * 1.5)
            state.phase = "resolved"
        else
            state.phase = "player_turn"
        end
    end
end

-- ========================================================================
-- Hand/turn advancement
-- ========================================================================

function Blackjack:AdvanceHand(state)
    -- Move to next hand if split
    if state.activeHandIndex < #state.playerHands then
        state.activeHandIndex = state.activeHandIndex + 1
        state.canDoubleDown = (#state.playerHands[state.activeHandIndex] == 2)
        return
    end

    -- All player hands done, dealer's turn
    state.phase = "dealer_turn"
    state.dealerRevealed = true
    self:PlayDealer(state)
end

-- ========================================================================
-- Dealer logic (standard rules: hit on 16 or less, stand on 17+)
-- ========================================================================

function Blackjack:PlayDealer(state)
    -- Only play dealer if at least one player hand is still alive
    local anyAlive = false
    for i, hand in ipairs(state.playerHands) do
        if not state.results[i] or state.results[i] == "" then
            anyAlive = true
            break
        end
    end

    if anyAlive then
        while self:HandValue(state.dealerHand) < 17 do
            state.dealerHand[#state.dealerHand + 1] = DrawCard(state)
        end
    end

    self:ResolveHands(state)
end

-- ========================================================================
-- Resolution
-- ========================================================================

function Blackjack:ResolveHands(state)
    local dealerValue = self:HandValue(state.dealerHand)
    local dealerBust = dealerValue > 21

    for i, hand in ipairs(state.playerHands) do
        if state.results[i] and state.results[i] ~= "" then
            -- Already resolved (bust)
        else
            local playerValue = self:HandValue(hand)

            if dealerBust then
                state.results[i] = "win"
                state.balance = state.balance + state.bets[i] * 2
            elseif playerValue > dealerValue then
                state.results[i] = "win"
                state.balance = state.balance + state.bets[i] * 2
            elseif playerValue == dealerValue then
                state.results[i] = "push"
                state.balance = state.balance + state.bets[i]
            else
                state.results[i] = "loss"
            end
        end
    end

    -- Discard all cards
    for _, hand in ipairs(state.playerHands) do
        for _, card in ipairs(hand) do
            state.discardPile[#state.discardPile + 1] = card
        end
    end
    for _, card in ipairs(state.dealerHand) do
        state.discardPile[#state.discardPile + 1] = card
    end

    state.phase = "resolved"
end

-- ========================================================================
-- GameManager interface compatibility
-- ========================================================================

-- Blackjack doesn't use moves in the chess sense.
-- Instead, actions (hit/stand/double/split) are called directly.
-- We provide stub functions for GameRegistry compatibility.

function Blackjack:ValidateMove(state, move)
    -- move.action = "hit"|"stand"|"double"|"split"|"deal"|"bet"|"insurance_yes"|"insurance_no"
    if not move or not move.action then return false end
    local action = move.action

    if action == "deal" then
        return state.phase == "betting"
    elseif action == "hit" then
        return state.phase == "player_turn"
    elseif action == "stand" then
        return state.phase == "player_turn"
    elseif action == "double" then
        return state.phase == "player_turn" and state.canDoubleDown and state.balance >= state.bets[state.activeHandIndex]
    elseif action == "split" then
        return state.phase == "player_turn" and state.canSplit and state.balance >= state.bet
    elseif action == "insurance_yes" then
        return state.phase == "insurance"
    elseif action == "insurance_no" then
        return state.phase == "insurance"
    elseif action == "bet" then
        return state.phase == "betting" and move.amount and move.amount > 0 and move.amount <= state.balance
    end

    return false
end

function Blackjack:ApplyMove(state, move)
    local action = move.action

    if action == "bet" then
        state.bet = move.amount
    elseif action == "deal" then
        self:Deal(state)
    elseif action == "hit" then
        self:Hit(state)
    elseif action == "stand" then
        self:Stand(state)
    elseif action == "double" then
        self:DoubleDown(state)
    elseif action == "split" then
        self:Split(state)
    elseif action == "insurance_yes" then
        self:AcceptInsurance(state)
    elseif action == "insurance_no" then
        self:DeclineInsurance(state)
    end
end

function Blackjack:GetGameStatus(state)
    if state.phase == "resolved" then
        -- Check if primary hand won/lost for stats
        local result = state.results[1]
        if result == "win" or result == "blackjack" then
            return "player_wins"
        elseif result == "loss" or result == "bust" then
            return "dealer_wins"
        else
            return "push"
        end
    end
    return "playing"
end

function Blackjack:CopyState(state)
    -- Deep copy for save/resume
    local copy = {}
    for k, v in pairs(state) do
        if type(v) == "table" then
            if k == "playerHands" then
                copy[k] = {}
                for i, hand in ipairs(v) do
                    copy[k][i] = {}
                    for j, card in ipairs(hand) do
                        copy[k][i][j] = { rank = card.rank, suit = card.suit }
                    end
                end
            elseif k == "dealerHand" or k == "deck" or k == "discardPile" then
                copy[k] = {}
                for i, card in ipairs(v) do
                    copy[k][i] = { rank = card.rank, suit = card.suit }
                end
            elseif k == "bets" or k == "results" then
                copy[k] = {}
                for i, val in ipairs(v) do
                    copy[k][i] = val
                end
            else
                copy[k] = v
            end
        else
            copy[k] = v
        end
    end
    return copy
end

-- ========================================================================
-- Self-registration (Plumber pattern)
-- ========================================================================

do
    local gameData = {
        name = L["Blackjack"],
        dbKey = "Blackjack",
        description = L["Classic Blackjack (21). Beat the dealer without going over 21."],
        icon = "Interface\\Icons\\INV_Misc_Dice_02",
        boardSize = nil, -- not a board game
        minPlayers = 1,
        maxPlayers = 2,

        createGameFunc = function()
            return Blackjack:CreateGameState()
        end,

        validateMoveFunc = function(state, move)
            return Blackjack:ValidateMove(state, move)
        end,

        getLegalMovesFunc = function(state)
            return {} -- Blackjack uses action buttons, not board moves
        end,

        getAllMovesFunc = function(state)
            return {}
        end,

        applyMoveFunc = function(state, move)
            Blackjack:ApplyMove(state, move)
        end,

        getGameStatusFunc = function(state)
            return Blackjack:GetGameStatus(state)
        end,

        getAIFunc = function()
            return MonkeyGames.BlackjackAI
        end,

        createUIFunc = function(parent)
            MonkeyGames.BlackjackUI:Create(parent)
        end,

        copyStateFunc = function(state)
            return Blackjack:CopyState(state)
        end,
    }

    MonkeyGames.GameRegistry:AddGame(gameData)
end
