-- Rack & Ruin: Poker.lua
-- Solo: 5-Card Draw vs a simple AI dealer (unchanged from single-player version).
-- Grouped: real 5-Card Draw against your party. Host deals privately (whisper),
-- each player holds/discards locally, then a showdown reveals all hands.

RackAndRuin = RackAndRuin or {}

local ORANGE = { 1, 0.55, 0.1 }
local CARD_W, CARD_H = 92, 128
local GAME_ID = "POKER"

local frame = CreateFrame("Frame", "RackAndRuinPokerFrame", UIParent, "BackdropTemplate")
frame:SetSize(700, 620)
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()

local statusText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
statusText:SetPoint("TOP", 0, -28)
statusText:SetTextColor(1, 0.85, 0.2)
statusText:SetText("")

-- Multiline results/summary area, used in place of the dealer's card row
-- when playing against real players (multiple opponents can't fit 5 slots).
local resultsText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
resultsText:SetPoint("TOPLEFT", 36, -70)
resultsText:SetWidth(90)
resultsText:SetJustifyH("LEFT")
resultsText:SetJustifyV("TOP")
resultsText:SetTextColor(0.85, 0.85, 0.85)
resultsText:SetText("")
resultsText:Hide()

local holdHint = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
holdHint:SetText("Click your cards to hold them, then Draw.")
holdHint:SetTextColor(0.7, 0.7, 0.7)

local function CreateCardSlot(parent, index, yOffset, clickable)
    local slot = CreateFrame(clickable and "Button" or "Frame", nil, parent, "BackdropTemplate")
    slot:SetSize(CARD_W, CARD_H)
    slot:SetPoint("TOPLEFT", 130 + (index - 1) * (CARD_W + 10), 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

    local holdLabel = slot:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    holdLabel:SetPoint("TOP", slot, "BOTTOM", 0, -4)
    holdLabel:SetText("HELD")
    holdLabel:SetTextColor(1, 0.85, 0.2)
    holdLabel:Hide()
    slot.holdLabel = holdLabel

    return slot
end

local dealerSlots, playerSlots = {}, {}
for i = 1, 5 do
    dealerSlots[i] = CreateCardSlot(frame, i, -100, false)
    playerSlots[i] = CreateCardSlot(frame, i, -290, true)
end

local dealerLabel = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
dealerLabel:SetPoint("BOTTOMLEFT", dealerSlots[1], "TOPLEFT", 0, 8)
dealerLabel:SetText("Dealer")
dealerLabel:SetTextColor(1, 1, 1)

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

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

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

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 dealButton = StyledButton(frame, "Deal", 170)
dealButton:SetPoint("BOTTOM", frame, "BOTTOM", -95, 90)

local drawButton = StyledButton(frame, "Draw", 170)
drawButton:SetPoint("LEFT", dealButton, "RIGHT", 16, 0)

local backButton = StyledButton(frame, "Back to Menu", 170)
backButton:SetPoint("TOP", dealButton, "BOTTOM", 0, -16)
backButton:SetScript("OnClick", function()
    frame:Hide()
    RackAndRuin.ShowMenu()
end)

holdHint:SetPoint("LEFT", backButton, "RIGHT", 20, 0)
holdHint:SetWidth(180)
holdHint:SetJustifyH("LEFT")

local myName = UnitName("player")

-- ============================================================
-- Shared render helpers
-- ============================================================
local function RenderHandInto(hand, slots, heldFlags)
    for i, slot in ipairs(slots) do
        local card = hand[i]
        if card then
            slot.texture:SetTexture(card.texture)
            slot.texture:Show()
            if heldFlags then
                slot.holdLabel:SetShown(heldFlags[i] == true)
            end
        else
            slot.texture:Hide()
            if slot.holdLabel then slot.holdLabel:Hide() end
        end
    end
end

-- ============================================================
-- SOLO MODE (vs simple AI dealer) - unchanged behavior
-- ============================================================
local soloDeck, soloDealerHand, soloPlayerHand, soloHeld
local soloStage = "idle"

local function SoloSimpleDealerDiscard(hand)
    local counts = {}
    for _, c in ipairs(hand) do
        local v = RackAndRuin.POKER_VALUE[c.rank]
        counts[v] = (counts[v] or 0) + 1
    end
    local keepIdx = {}
    for i, c in ipairs(hand) do
        local v = RackAndRuin.POKER_VALUE[c.rank]
        if counts[v] >= 2 then keepIdx[i] = true end
    end
    local anyKept = false
    for _, v in pairs(keepIdx) do if v then anyKept = true end end
    if not anyKept then
        local sorted = {}
        for i, c in ipairs(hand) do table.insert(sorted, { i = i, v = RackAndRuin.POKER_VALUE[c.rank] }) end
        table.sort(sorted, function(a, b) return a.v > b.v end)
        keepIdx[sorted[1].i] = true
        keepIdx[sorted[2].i] = true
    end
    return keepIdx
end

local function SoloRenderPlayerHand()
    RenderHandInto(soloPlayerHand, playerSlots, soloHeld)
end

local function SoloRenderDealerHand(faceUp)
    local shown = {}
    for i, c in ipairs(soloDealerHand) do
        shown[i] = faceUp and c or { texture = RackAndRuin.GetCardBackTexture() }
    end
    RenderHandInto(shown, dealerSlots)
end

local function SoloEvaluateAndFinish()
    local dCat, dTb, dName = RackAndRuin.EvaluateHand(soloDealerHand)
    local pCat, pTb, pName = RackAndRuin.EvaluateHand(soloPlayerHand)

    dealerHandText:SetText(dName)
    playerHandText:SetText(pName)
    SoloRenderDealerHand(true)

    local result = RackAndRuin.CompareHands(pCat, pTb, dCat, dTb)
    if result > 0 then
        statusText:SetText("You win with " .. pName .. "!")
    elseif result < 0 then
        statusText:SetText("Dealer wins with " .. dName .. ".")
    else
        statusText:SetText("Push - identical hands.")
    end

    drawButton:Disable()
    drawButton.label:SetTextColor(0.5, 0.5, 0.5)
    soloStage = "done"
    holdHint:Hide()
end

local function SoloDeal()
    local deck = RackAndRuin.Shuffle(RackAndRuin.BuildDeck())
    soloDeck = deck
    soloDealerHand = RackAndRuin.Draw(deck, 5)
    soloPlayerHand = RackAndRuin.Draw(deck, 5)
    soloHeld = {}
    soloStage = "dealt"

    statusText:SetText("")
    dealerHandText:SetText("")
    playerHandText:SetText("")
    dealerLabel:SetText("Dealer")

    dealerSlots[1]:GetParent() -- no-op, keep slots as-is
    for _, s in ipairs(dealerSlots) do s:Show() end
    resultsText:Hide()

    SoloRenderPlayerHand()
    SoloRenderDealerHand(false)

    drawButton:Enable()
    drawButton.label:SetTextColor(1, 0.85, 0.6)
    dealButton:Enable()
    dealButton.label:SetTextColor(1, 0.85, 0.6)
    holdHint:Show()
end

local function SoloDrawPhase()
    if soloStage ~= "dealt" then return end

    for i = 1, 5 do
        if not soloHeld[i] then
            soloPlayerHand[i] = RackAndRuin.Draw(soloDeck, 1)[1]
        end
    end
    SoloRenderPlayerHand()

    local dealerKeep = SoloSimpleDealerDiscard(soloDealerHand)
    for i = 1, 5 do
        if not dealerKeep[i] then
            soloDealerHand[i] = RackAndRuin.Draw(soloDeck, 1)[1]
        end
    end

    SoloEvaluateAndFinish()
end

-- ============================================================
-- MULTIPLAYER MODE (host authoritative, real players)
-- ============================================================
local host = {
    deck = nil,
    players = nil,
    hands = nil,     -- name -> hand
    holds = nil,     -- name -> { [i]=true/false }
    ready = nil,     -- name -> true
    phase = "idle",  -- idle | dealt | done
}

local myHand = {}     -- this client's own hand (received via whisper)
local myHeld = {}
local mpStage = "idle" -- idle | dealt | waiting | done

local function HostBroadcastPublicState(extra)
    local t = {
        phase = host.phase,
        players = table.concat(host.players or {}, ","),
    }
    local readyNames = {}
    for name, v in pairs(host.ready or {}) do
        if v then table.insert(readyNames, name) end
    end
    t.ready = table.concat(readyNames, ",")
    if extra then
        for k, v in pairs(extra) do t[k] = v end
    end
    RackAndRuin.Net.Send(GAME_ID, "STATE", RackAndRuin.Net.Encode(t))
end

local function HostFinalizeShowdown()
    -- redraw non-held cards for every player per their stored holds
    for _, name in ipairs(host.players) do
        local hand = host.hands[name]
        local holds = host.holds[name] or {}
        for i = 1, 5 do
            if not holds[i] then
                hand[i] = table.remove(host.deck)
            end
        end
    end

    -- evaluate everyone, find best
    local evals = {}
    for _, name in ipairs(host.players) do
        local cat, tb, handName = RackAndRuin.EvaluateHand(host.hands[name])
        evals[name] = { cat = cat, tb = tb, name = handName }
    end

    local winners = { host.players[1] }
    for i = 2, #host.players do
        local name = host.players[i]
        local cmp = RackAndRuin.CompareHands(evals[name].cat, evals[name].tb, evals[winners[1]].cat, evals[winners[1]].tb)
        if cmp > 0 then
            winners = { name }
        elseif cmp == 0 then
            table.insert(winners, name)
        end
    end

    local lines = {}
    for _, name in ipairs(host.players) do
        table.insert(lines, name .. ": " .. evals[name].name)
    end

    host.phase = "done"

    local t = {
        phase = "done",
        players = table.concat(host.players, ","),
        winners = table.concat(winners, ","),
        summary = table.concat(lines, "|"):gsub(" ", "_"),
    }
    for _, name in ipairs(host.players) do
        t["hand_" .. name] = RackAndRuin.Net.EncodeHand(host.hands[name])
    end
    RackAndRuin.Net.Send(GAME_ID, "STATE", RackAndRuin.Net.Encode(t))
    RackAndRuin.POKER_ApplyPublicState(RackAndRuin.Net.Encode(t))
end

local function HostDeal()
    host.deck = RackAndRuin.Shuffle(RackAndRuin.BuildDeck())
    host.players = RackAndRuin.Net.GetPlayers()
    host.hands = {}
    host.holds = {}
    host.ready = {}
    for _, name in ipairs(host.players) do
        local hand = RackAndRuin.Draw(host.deck, 5)
        host.hands[name] = hand
        if name == myName then
            myHand = hand
            myHeld = {}
        else
            RackAndRuin.Net.SendWhisper(GAME_ID, "YOURHAND", RackAndRuin.Net.EncodeHand(hand), name)
        end
    end
    host.phase = "dealt"
    HostBroadcastPublicState()
end

local function HostReceiveHold(sender, payload)
    local holds = {}
    for i = 1, #payload do
        holds[i] = (payload:sub(i, i) == "1")
    end
    host.holds[sender] = holds
    host.ready[sender] = true
    HostBroadcastPublicState()

    local allReady = true
    for _, name in ipairs(host.players) do
        if not host.ready[name] then allReady = false end
    end
    if allReady then
        HostFinalizeShowdown()
    end
end

function RackAndRuin.POKER_ApplyPublicState(payload)
    local t = RackAndRuin.Net.Decode(payload)
    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

    if t.phase == "dealt" then
        mpStage = "dealt"
        dealerLabel:SetText("")
        dealerHandText:SetText("")
        for _, s in ipairs(dealerSlots) do s.texture:Hide() end

        local readySet = {}
        for name in string.gmatch(t.ready or "", "[^,]+") do readySet[name] = true end
        local waitingOn = {}
        local lines = {}
        for _, name in ipairs(players) do
            if name ~= myName then
                table.insert(lines, name .. (readySet[name] and " (ready)" or ""))
            end
            if not readySet[name] then table.insert(waitingOn, name) end
        end
        resultsText:SetText("At the table:\n" .. table.concat(lines, "\n"))
        resultsText:Show()

        RenderHandInto(myHand, playerSlots, myHeld)
        playerHandText:SetText("")
        holdHint:Show()

        if readySet[myName] then
            statusText:SetText("Waiting on: " .. table.concat(waitingOn, ", "))
            drawButton:Disable()
            drawButton.label:SetTextColor(0.5, 0.5, 0.5)
        else
            statusText:SetText("Choose cards to hold, then Draw.")
            drawButton:Enable()
            drawButton.label:SetTextColor(1, 0.85, 0.6)
        end
        dealButton:Disable()
        dealButton.label:SetTextColor(0.5, 0.5, 0.5)

    elseif t.phase == "done" then
        mpStage = "done"
        holdHint:Hide()
        local myFinalHand = RackAndRuin.Net.DecodeHand(t["hand_" .. myName] or "")
        RenderHandInto(myFinalHand, playerSlots, nil)
        for _, s in ipairs(playerSlots) do s.holdLabel:Hide() end

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

        local summaryLines = {}
        for line in string.gmatch((t.summary or ""):gsub("_", " "), "[^|]+") do
            table.insert(summaryLines, line)
        end

        for _, s in ipairs(dealerSlots) do s.texture:Hide() end
        dealerLabel:SetText("")
        dealerHandText:SetText("")
        resultsText:SetText(table.concat(summaryLines, "\n"))
        resultsText:Show()

        if #winners == 1 and winners[1] == myName then
            statusText:SetText("You win!")
        elseif #winners == 1 then
            statusText:SetText(winners[1] .. " wins!")
        else
            statusText:SetText("Split pot: " .. table.concat(winners, ", "))
        end

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

-- ============================================================
-- Network handlers
-- ============================================================
RackAndRuin.Net.RegisterHandler(GAME_ID, "YOURHAND", function(sender, payload)
    myHand = RackAndRuin.Net.DecodeHand(payload)
    myHeld = {}
end)

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

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

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

-- ============================================================
-- Button wiring - dispatches to solo or multiplayer logic
-- ============================================================
for i, slot in ipairs(playerSlots) do
    slot:SetScript("OnClick", function()
        if RackAndRuin.Net.IsGrouped() then
            if mpStage ~= "dealt" then return end
            myHeld[i] = not myHeld[i]
            RenderHandInto(myHand, playerSlots, myHeld)
        else
            if soloStage ~= "dealt" then return end
            soloHeld[i] = not soloHeld[i]
            SoloRenderPlayerHand()
        end
    end)
end

dealButton:SetScript("OnClick", function()
    if RackAndRuin.Net.IsGrouped() then
        if RackAndRuin.Net.IsHost() then
            HostDeal()
        else
            RackAndRuin.Net.Send(GAME_ID, "DEAL", "")
        end
    else
        SoloDeal()
    end
end)

drawButton:SetScript("OnClick", function()
    if RackAndRuin.Net.IsGrouped() then
        if mpStage ~= "dealt" then return end
        local bits = {}
        for i = 1, 5 do bits[i] = myHeld[i] and "1" or "0" end
        local payload = table.concat(bits)
        if RackAndRuin.Net.IsHost() then
            HostReceiveHold(myName, payload)
        else
            RackAndRuin.Net.Send(GAME_ID, "HOLD", payload)
        end
        drawButton:Disable()
        drawButton.label:SetTextColor(0.5, 0.5, 0.5)
    else
        SoloDrawPhase()
    end
end)

function RackAndRuin.ShowPoker()
    frame:Show()
    dealerSlots[1].texture:SetParent(dealerSlots[1]) -- no-op safety
    if RackAndRuin.Net.IsGrouped() then
        mpStage = "idle"
        resultsText:Hide()
        for _, s in ipairs(dealerSlots) do s.texture:Hide() end
        dealerLabel:SetText("")
        dealerHandText:SetText("")
        playerHandText:SetText("")
        for _, s in ipairs(playerSlots) do s.texture:Hide(); s.holdLabel:Hide() end
        drawButton:Disable()
        drawButton.label:SetTextColor(0.5, 0.5, 0.5)
        holdHint:Hide()

        if RackAndRuin.Net.IsHost() then
            statusText:SetText("Press Deal to start the hand.")
            dealButton:Enable()
            dealButton.label:SetTextColor(1, 0.85, 0.6)
        else
            statusText:SetText("Waiting for host to deal...")
            dealButton:Disable()
            dealButton.label:SetTextColor(0.5, 0.5, 0.5)
        end
    else
        dealerLabel:SetText("Dealer")
        for _, s in ipairs(dealerSlots) do s:Show() end
        resultsText:Hide()
        SoloDeal()
    end
end

RackAndRuin.PokerFrame = frame
