-- Rack & Ruin: HandEval.lua
-- Evaluates a 5-card poker hand and returns a comparable score + name.

RackAndRuin = RackAndRuin or {}

local HAND_NAMES = {
    [9] = "Straight Flush",
    [8] = "Four of a Kind",
    [7] = "Full House",
    [6] = "Flush",
    [5] = "Straight",
    [4] = "Three of a Kind",
    [3] = "Two Pair",
    [2] = "One Pair",
    [1] = "High Card",
}

-- Was accidentally declared as a global function (no "local"), which
-- silently overwrote Blizzard's own global tContains() helper used
-- throughout their UI code (including, apparently, their Settings panel's
-- category-list logic) - causing an assertion failure whenever Options was
-- opened. Kept local and renamed to avoid ever colliding with a Blizzard
-- global again.
local function RR_ArrayContains(t, val)
    for _, v in ipairs(t) do
        if v == val then return true end
    end
    return false
end

-- hand: array of 5 { rank=, suit= } card tables
-- returns: category (1-9), tiebreak array (high to low), display name
function RackAndRuin.EvaluateHand(hand)
    local values = {}
    local suitCounts = {}
    local valueCounts = {}

    for _, card in ipairs(hand) do
        local v = RackAndRuin.POKER_VALUE[card.rank]
        table.insert(values, v)
        suitCounts[card.suit] = (suitCounts[card.suit] or 0) + 1
        valueCounts[v] = (valueCounts[v] or 0) + 1
    end

    table.sort(values, function(a, b) return a > b end)

    local isFlush = false
    for _, count in pairs(suitCounts) do
        if count == 5 then isFlush = true end
    end

    -- straight check (handles wheel: A-2-3-4-5)
    local uniqueVals = {}
    for _, v in ipairs(values) do
        if not RR_ArrayContains(uniqueVals, v) then table.insert(uniqueVals, v) end
    end
    table.sort(uniqueVals, function(a,b) return a>b end)

    local isStraight = false
    local straightHigh = nil
    if #uniqueVals == 5 then
        if uniqueVals[1] - uniqueVals[5] == 4 then
            isStraight = true
            straightHigh = uniqueVals[1]
        elseif uniqueVals[1] == 14 and uniqueVals[2] == 5 and uniqueVals[3] == 4
            and uniqueVals[4] == 3 and uniqueVals[5] == 2 then
            -- wheel: A-2-3-4-5, Ace plays low
            isStraight = true
            straightHigh = 5
        end
    end

    -- group by count: build list of {value, count} sorted by count desc, then value desc
    local groups = {}
    for v, c in pairs(valueCounts) do
        table.insert(groups, { value = v, count = c })
    end
    table.sort(groups, function(a, b)
        if a.count ~= b.count then return a.count > b.count end
        return a.value > b.value
    end)

    local function tiebreakFromGroups()
        local tb = {}
        for _, g in ipairs(groups) do
            for i = 1, g.count do table.insert(tb, g.value) end
        end
        return tb
    end

    if isStraight and isFlush then
        return 9, { straightHigh }, HAND_NAMES[9]
    elseif groups[1].count == 4 then
        return 8, tiebreakFromGroups(), HAND_NAMES[8]
    elseif groups[1].count == 3 and groups[2] and groups[2].count == 2 then
        return 7, tiebreakFromGroups(), HAND_NAMES[7]
    elseif isFlush then
        return 6, values, HAND_NAMES[6]
    elseif isStraight then
        return 5, { straightHigh }, HAND_NAMES[5]
    elseif groups[1].count == 3 then
        return 4, tiebreakFromGroups(), HAND_NAMES[4]
    elseif groups[1].count == 2 and groups[2] and groups[2].count == 2 then
        return 3, tiebreakFromGroups(), HAND_NAMES[3]
    elseif groups[1].count == 2 then
        return 2, tiebreakFromGroups(), HAND_NAMES[2]
    else
        return 1, values, HAND_NAMES[1]
    end
end

-- Compares two evaluated hands. Returns 1 if hand A wins, -1 if B wins, 0 if tie.
function RackAndRuin.CompareHands(catA, tbA, catB, tbB)
    if catA ~= catB then
        return catA > catB and 1 or -1
    end
    for i = 1, math.max(#tbA, #tbB) do
        local a, b = tbA[i] or 0, tbB[i] or 0
        if a ~= b then
            return a > b and 1 or -1
        end
    end
    return 0
end
