-- Rack & Ruin: Blackjack.lua
-- Host (party leader, or self if solo) owns the deck and drives turn order.
-- Clients send Hit/Stand requests to the host; host broadcasts the full
-- table state back out and everyone renders from that.

RackAndRuin = RackAndRuin or {}

local ORANGE = { 1, 0.55, 0.1 }
local CARD_W, CARD_H = 92, 128
local MAX_SLOTS = 7
local SLOT_START_X = 150
local SLOT_GAP = 10
local GAME_ID = "BJ"

local frame = CreateFrame("Frame", "RackAndRuinBlackjackFrame", UIParent, "BackdropTemplate")
frame:SetSize(870, 600)
frame:SetPoint("CENTER")
frame:SetMovable(true)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", frame.StartMoving)
frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
frame:SetBackdrop({
    bgFile = "Interface\\Buttons\\WHITE8x8",
    edgeFile = "Interface\\Buttons\\WHITE8x8",
    edgeSize = 2,
})
frame:SetBackdropColor(0, 0, 0, 0.97)
frame:SetBackdropBorderColor(unpack(ORANGE))
frame:SetFrameStrata("HIGH")
frame:SetToplevel(true)
frame:Hide()

-- status text (whose turn / bust / blackjack / win / lose)
local statusText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
statusText:SetPoint("TOP", 0, -28)
statusText:SetTextColor(1, 0.85, 0.2)
statusText:SetText("")

-- Shows other seated players (multiplayer only) - left margin, vertical list
local tableText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
tableText:SetPoint("TOPLEFT", 36, -70)
tableText:SetWidth(90)
tableText:SetJustifyH("LEFT")
tableText:SetJustifyV("TOP")
tableText:SetTextColor(0.7, 0.7, 0.7)
tableText:SetText("")

-- Card slot creation
local function CreateCardSlot(parent, index, yOffset)
    local slot = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    slot:SetSize(CARD_W, CARD_H)
    slot:SetPoint("TOPLEFT", SLOT_START_X + (index - 1) * (CARD_W + SLOT_GAP), yOffset)
    slot:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 2,
    })
    slot:SetBackdropColor(0, 0, 0, 1)
    slot:SetBackdropBorderColor(unpack(ORANGE))

    local tex = slot:CreateTexture(nil, "ARTWORK")
    tex:SetAllPoints()
    tex:Hide()
    slot.texture = tex

    return slot
end

local houseSlots, playerSlots = {}, {}
for i = 1, MAX_SLOTS do
    houseSlots[i] = CreateCardSlot(frame, i, -100)
    playerSlots[i] = CreateCardSlot(frame, i, -270)
end

-- Row labels (sit directly above their card row, left-aligned to the row)
local houseLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
houseLabel:SetPoint("BOTTOMLEFT", houseSlots[1], "TOPLEFT", 0, 8)
houseLabel:SetText("House")
houseLabel:SetTextColor(1, 1, 1)

local houseScoreText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
houseScoreText:SetPoint("LEFT", houseLabel, "RIGHT", 8, 0)
houseScoreText:SetTextColor(unpack(ORANGE))

local playerLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
playerLabel:SetPoint("BOTTOMLEFT", playerSlots[1], "TOPLEFT", 0, 8)
playerLabel:SetText("Your Hand")
playerLabel:SetTextColor(1, 1, 1)

local playerScoreText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
playerScoreText:SetPoint("LEFT", playerLabel, "RIGHT", 8, 0)
playerScoreText:SetTextColor(unpack(ORANGE))

-- Buttons
local function StyledButton(parent, text, width)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetSize(width or 170, 42)
    btn:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 2,
    })
    btn:SetBackdropColor(0, 0, 0, 1)
    btn:SetBackdropBorderColor(unpack(ORANGE))

    local label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    label:SetPoint("CENTER")
    label:SetText(text)
    label:SetTextColor(1, 0.85, 0.6)
    btn.label = label

    btn:SetScript("OnEnter", function(self) self:SetBackdropColor(0.2, 0.1, 0, 1) end)
    btn:SetScript("OnLeave", function(self) self:SetBackdropColor(0, 0, 0, 1) end)

    return btn
end

local hitButton = StyledButton(frame, "Hit", 170)
hitButton:SetPoint("BOTTOM", frame, "BOTTOM", -95, 90)

local standButton = StyledButton(frame, "Stand", 170)
standButton:SetPoint("LEFT", hitButton, "RIGHT", 16, 0)

local newGameButton = StyledButton(frame, "New Game", 170)
newGameButton:SetPoint("TOP", hitButton, "BOTTOM", 0, -16)

local backButton = StyledButton(frame, "Back to Menu", 170)
backButton:SetPoint("LEFT", newGameButton, "RIGHT", 16, 0)
backButton:SetScript("OnClick", function()
    frame:Hide()
    RackAndRuin.ShowMenu()
end)

-- ============================================================
-- Scoring helper (used by both host simulation and local display)
-- ============================================================
local function ScoreHand(hand)
    local total, aces = 0, 0
    for _, card in ipairs(hand) do
        total = total + RackAndRuin.BJ_VALUE[card.rank]
        if card.rank == "A" then aces = aces + 1 end
    end
    while total > 21 and aces > 0 do
        total = total - 10
        aces = aces - 1
    end
    return total
end

-- ============================================================
-- HOST STATE (only meaningful/used on the host's client)
-- ============================================================
local host = {
    deck = nil,
    houseHand = nil,
    players = nil,      -- ordered list of names
    hands = nil,        -- name -> hand
    finished = nil,      -- name -> true once stood/busted
    turnIndex = nil,
    phase = "idle",      -- idle | playing | dealer | done
    statusMsg = "",
}

local function EncodeState()
    local t = {
        phase = host.phase,
        turn = host.players and host.players[host.turnIndex] or "",
        house = RackAndRuin.Net.EncodeHand(host.houseHand or {}),
        hide = (host.phase == "playing") and "1" or "0",
        players = table.concat(host.players or {}, ","),
        status = (host.statusMsg or ""):gsub(" ", "_"),
    }
    for name, hand in pairs(host.hands or {}) do
        t["hand_" .. name] = RackAndRuin.Net.EncodeHand(hand)
    end
    return RackAndRuin.Net.Encode(t)
end

local function BroadcastState()
    RackAndRuin.Net.Send(GAME_ID, "STATE", EncodeState())
    -- Host applies to its own UI directly too (no network round-trip needed)
    RackAndRuin.BJ_ApplyState(EncodeState())
end

local function HostAdvanceTurn()
    host.turnIndex = host.turnIndex + 1
    if host.turnIndex > #host.players then
        -- everyone has acted - dealer plays
        host.phase = "dealer"
        while ScoreHand(host.houseHand) < 17 do
            table.insert(host.houseHand, table.remove(host.deck))
        end
        local houseScore = ScoreHand(host.houseHand)
        local results = {}
        for _, name in ipairs(host.players) do
            local pScore = ScoreHand(host.hands[name])
            if pScore > 21 then
                table.insert(results, name .. ": Bust")
            elseif houseScore > 21 or pScore > houseScore then
                table.insert(results, name .. ": Win")
            elseif pScore < houseScore then
                table.insert(results, name .. ": Lose")
            else
                table.insert(results, name .. ": Push")
            end
        end
        host.phase = "done"
        host.statusMsg = "House: " .. houseScore .. " | " .. table.concat(results, ", ")
    else
        local nextPlayer = host.players[host.turnIndex]
        host.statusMsg = nextPlayer .. "'s turn"
        -- auto-skip anyone already finished (e.g. busted) - shouldn't normally happen
        if host.finished[nextPlayer] then
            HostAdvanceTurn()
            return
        end
    end
end

local function HostProcessHit(playerName)
    if host.phase ~= "playing" then return end
    if host.players[host.turnIndex] ~= playerName then return end

    table.insert(host.hands[playerName], table.remove(host.deck))
    local score = ScoreHand(host.hands[playerName])
    if score >= 21 then
        host.finished[playerName] = true
        HostAdvanceTurn()
    else
        host.statusMsg = playerName .. "'s turn"
    end
    BroadcastState()
end

local function HostProcessStand(playerName)
    if host.phase ~= "playing" then return end
    if host.players[host.turnIndex] ~= playerName then return end

    host.finished[playerName] = true
    HostAdvanceTurn()
    BroadcastState()
end

local function HostNewGame()
    host.deck = RackAndRuin.Shuffle(RackAndRuin.BuildDeck())
    host.players = RackAndRuin.Net.GetPlayers()
    host.hands = {}
    host.finished = {}
    for _, name in ipairs(host.players) do
        host.hands[name] = RackAndRuin.Draw(host.deck, 2)
    end
    host.houseHand = RackAndRuin.Draw(host.deck, 2)
    host.turnIndex = 1
    host.phase = "playing"
    host.statusMsg = host.players[1] .. "'s turn"
    BroadcastState()
end

-- ============================================================
-- CLIENT SIDE: render whatever state we last received
-- ============================================================
local myName = UnitName("player")
local lastState = nil

local function RenderCards(hand, slots)
    for i, slot in ipairs(slots) do
        local card = hand[i]
        if card then
            slot.texture:SetTexture(card.texture)
            slot.texture:Show()
        else
            slot.texture:Hide()
        end
    end
end

function RackAndRuin.BJ_ApplyState(payload)
    local t = RackAndRuin.Net.Decode(payload)
    lastState = t
    if not t.phase or t.phase == "" then return end

    local players = {}
    for name in string.gmatch(t.players or "", "[^,]+") do
        table.insert(players, name)
    end

    local myHand = RackAndRuin.Net.DecodeHand(t["hand_" .. myName] or "")
    local houseHand = RackAndRuin.Net.DecodeHand(t.house or "")
    local hideHole = t.hide == "1"

    RenderCards(myHand, playerSlots)
    playerScoreText:SetText(tostring(ScoreHand(myHand)))

    if hideHole then
        local shown = {}
        for i, c in ipairs(houseHand) do
            if i == 2 then
                shown[i] = { texture = RackAndRuin.GetCardBackTexture() }
            else
                shown[i] = c
            end
        end
        RenderCards(shown, houseSlots)
        houseScoreText:SetText("?")
    else
        RenderCards(houseHand, houseSlots)
        houseScoreText:SetText(tostring(ScoreHand(houseHand)))
    end

    statusText:SetText((t.status or ""):gsub("_", " "))

    -- other seated players list
    local others = {}
    for _, name in ipairs(players) do
        if name ~= myName then table.insert(others, name) end
    end
    if #others > 0 then
        tableText:SetText("At the table:\n" .. table.concat(others, "\n"))
    else
        tableText:SetText("")
    end

    local myTurn = (t.phase == "playing") and (t.turn == myName)
    if myTurn then
        hitButton:Enable()
        standButton:Enable()
        hitButton.label:SetTextColor(1, 0.85, 0.6)
        standButton.label:SetTextColor(1, 0.85, 0.6)
    else
        hitButton:Disable()
        standButton:Disable()
        hitButton.label:SetTextColor(0.5, 0.5, 0.5)
        standButton.label:SetTextColor(0.5, 0.5, 0.5)
    end

    if RackAndRuin.Net.IsHost() then
        newGameButton:Enable()
        newGameButton.label:SetTextColor(1, 0.85, 0.6)
    else
        newGameButton:Disable()
        newGameButton.label:SetTextColor(0.5, 0.5, 0.5)
    end
end

-- ============================================================
-- Wire up network handlers (host listens for requests, everyone listens for state)
-- ============================================================
RackAndRuin.Net.RegisterHandler(GAME_ID, "HIT", function(sender, payload)
    if RackAndRuin.Net.IsHost() then HostProcessHit(sender) end
end)

RackAndRuin.Net.RegisterHandler(GAME_ID, "STAND", function(sender, payload)
    if RackAndRuin.Net.IsHost() then HostProcessStand(sender) end
end)

RackAndRuin.Net.RegisterHandler(GAME_ID, "NEWGAME", function(sender, payload)
    if RackAndRuin.Net.IsHost() then HostNewGame() end
end)

RackAndRuin.Net.RegisterHandler(GAME_ID, "STATE", function(sender, payload)
    RackAndRuin.BJ_ApplyState(payload)
end)

-- ============================================================
-- Button wiring
-- ============================================================
hitButton:SetScript("OnClick", function()
    if RackAndRuin.Net.IsHost() then
        HostProcessHit(myName)
    else
        RackAndRuin.Net.Send(GAME_ID, "HIT", "")
    end
end)

standButton:SetScript("OnClick", function()
    if RackAndRuin.Net.IsHost() then
        HostProcessStand(myName)
    else
        RackAndRuin.Net.Send(GAME_ID, "STAND", "")
    end
end)

newGameButton:SetScript("OnClick", function()
    if RackAndRuin.Net.IsHost() then
        HostNewGame()
    else
        RackAndRuin.Net.Send(GAME_ID, "NEWGAME", "")
    end
end)

function RackAndRuin.ShowBlackjack()
    frame:Show()
    if RackAndRuin.Net.IsHost() then
        HostNewGame()
    else
        statusText:SetText("Waiting for host to deal...")
        tableText:SetText("")
        hitButton:Disable()
        standButton:Disable()
        newGameButton:Disable()
    end
end

RackAndRuin.BlackjackFrame = frame
