-- ============================================================
-- Komo Hold'Em — Game
-- Host-authoritative table state machine + hand evaluator.
--
-- Design decisions locked during mockups:
--   * No blinds UI — automatic ante of 1% of starting stack per
--     hand, seeded silently so casual players never think about
--     blind structure.
--   * Stakes: "chips" (imitation, free) or "gold" (real WoW gold,
--     buy-in; disclaimer shown & accepted via Ready in lobby).
--   * Game starts when 2+ seated and ALL seated players ready —
--     open seats never block.
--   * Config is immutable after the table opens.
--   * Turn timer (60/30/15s) auto-checks or auto-folds on expiry.
-- ============================================================
local _, KH = ...
KH.Game = {}
local Game  = KH.Game
local Cards = KH.Cards

Game.STATE = {
    LOBBY    = "LOBBY",
    DEALING  = "DEALING",
    PREFLOP  = "PREFLOP",
    FLOP     = "FLOP",
    TURN     = "TURN",
    RIVER    = "RIVER",
    SHOWDOWN = "SHOWDOWN",
    SETTLING = "SETTLING",
    CLOSED   = "CLOSED",
}

-- ------------------------------------------------------------
-- Table lifecycle (host side)
-- ------------------------------------------------------------
function Game:NewTable(config)
    -- config: { name, stakes = "chips"|"gold", amount, seats, timer }
    local tbl = {
        id       = KH.playerFull .. "-" .. time(),  -- unique per table for ledger dedupe
        host     = KH.playerFull,
        config   = config,
        state    = self.STATE.LOBBY,
        players  = {},   -- [name] = { seat, stack, ready, folded, bet, connected }
        seatOrder= {},
        handNo   = 0,
        dealerIx = 0,
        deck     = nil,
        board    = {},
        pot      = 0,
        ante     = math.max(1, math.floor(config.amount * 0.01 + 0.5)),
    }
    return tbl
end

function Game:CanStart(tbl)
    local seated, ready = 0, 0
    for _, p in pairs(tbl.players) do
        seated = seated + 1
        if p.ready then ready = ready + 1 end
    end
    return seated >= 2 and seated == ready, seated, ready
end

function Game:StartHand(tbl)
    tbl.handNo   = tbl.handNo + 1
    tbl.dealerIx = (tbl.dealerIx % #tbl.seatOrder) + 1
    tbl.deck     = Cards:Shuffle(Cards:NewDeck())
    tbl.board    = {}
    tbl.pot      = 0
    tbl.state    = self.STATE.DEALING

    -- silent ante from every seated player
    for _, name in ipairs(tbl.seatOrder) do
        local p = tbl.players[name]
        local ante = math.min(tbl.ante, p.stack)
        p.stack = p.stack - ante
        p.bet   = 0
        p.folded = false
        tbl.pot = tbl.pot + ante
    end

    -- hole cards go out by WHISPER only (see Comms.SendHoleCards)
    for _, name in ipairs(tbl.seatOrder) do
        local c1, c2 = Cards:Draw(tbl.deck), Cards:Draw(tbl.deck)
        tbl.players[name].hole = { c1, c2 }
        if name ~= KH.playerFull then
            KH.Comms:SendHoleCards(name, tbl.handNo, c1, c2)
        end
    end

    tbl.state = self.STATE.PREFLOP
end

function Game:DealStreet(tbl)
    if tbl.state == self.STATE.PREFLOP then
        Cards:Draw(tbl.deck) -- burn
        for i = 1, 3 do tbl.board[#tbl.board + 1] = Cards:Draw(tbl.deck) end
        tbl.state = self.STATE.FLOP
    elseif tbl.state == self.STATE.FLOP then
        Cards:Draw(tbl.deck)
        tbl.board[#tbl.board + 1] = Cards:Draw(tbl.deck)
        tbl.state = self.STATE.TURN
    elseif tbl.state == self.STATE.TURN then
        Cards:Draw(tbl.deck)
        tbl.board[#tbl.board + 1] = Cards:Draw(tbl.deck)
        tbl.state = self.STATE.RIVER
    end
    return tbl.board
end

-- ------------------------------------------------------------
-- Hand evaluator — 7 cards -> best 5-card rank
-- Returns: category (1..10, higher better), tiebreak list, label
-- Categories: 1 high card .. 9 straight flush, 10 royal flush
-- ------------------------------------------------------------
local CATEGORY_LABEL = {
    [1]="High Card", [2]="Pair", [3]="Two Pair", [4]="Three of a Kind",
    [5]="Straight", [6]="Flush", [7]="Full House", [8]="Four of a Kind",
    [9]="Straight Flush", [10]="Royal Flush",
}

local function evaluate5(cards)
    -- cards: array of 5 ids
    local vals, suits = {}, {}
    for _, id in ipairs(cards) do
        local r, s = Cards.Split(id)
        vals[#vals + 1] = Cards.RANK_VALUE[r]
        suits[s] = (suits[s] or 0) + 1
    end
    table.sort(vals, function(a, b) return a > b end)

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

    -- straight (ace-low handled)
    local isStraight, straightHigh = true, vals[1]
    for i = 2, 5 do
        if vals[i] ~= vals[i - 1] - 1 then isStraight = false break end
    end
    if not isStraight and vals[1] == 14 and vals[2] == 5 and vals[3] == 4
        and vals[4] == 3 and vals[5] == 2 then
        isStraight, straightHigh = true, 5
    end

    -- counts
    local count = {}
    for _, v in ipairs(vals) do count[v] = (count[v] or 0) + 1 end
    local groups = {}
    for v, n in pairs(count) do groups[#groups + 1] = { v = v, n = n } end
    table.sort(groups, function(a, b)
        if a.n ~= b.n then return a.n > b.n end
        return a.v > b.v
    end)

    local tiebreak = {}
    for _, g in ipairs(groups) do tiebreak[#tiebreak + 1] = g.v end

    if isStraight and isFlush then
        if straightHigh == 14 then return 10, { 14 } end
        return 9, { straightHigh }
    elseif groups[1].n == 4 then return 8, tiebreak
    elseif groups[1].n == 3 and groups[2].n == 2 then return 7, tiebreak
    elseif isFlush then return 6, vals
    elseif isStraight then return 5, { straightHigh }
    elseif groups[1].n == 3 then return 4, tiebreak
    elseif groups[1].n == 2 and groups[2].n == 2 then return 3, tiebreak
    elseif groups[1].n == 2 then return 2, tiebreak
    end
    return 1, vals
end

-- best of all C(7,5)=21 combinations
function Game:Evaluate(hole, board)
    local seven = { hole[1], hole[2] }
    for _, c in ipairs(board) do seven[#seven + 1] = c end

    local bestCat, bestTie
    local n = #seven
    for a = 1, n - 4 do
      for b = a + 1, n - 3 do
        for c = b + 1, n - 2 do
          for d = c + 1, n - 1 do
            for e = d + 1, n do
                local cat, tie = evaluate5({ seven[a], seven[b], seven[c], seven[d], seven[e] })
                local better = false
                if not bestCat or cat > bestCat then better = true
                elseif cat == bestCat then
                    for i = 1, #tie do
                        local t1, t2 = tie[i], bestTie[i] or 0
                        if t1 ~= t2 then better = t1 > t2 break end
                    end
                end
                if better then bestCat, bestTie = cat, tie end
            end
          end
        end
      end
    end
    return bestCat, bestTie, CATEGORY_LABEL[bestCat]
end

-- compare two evaluated hands: 1 if A wins, -1 if B, 0 tie
function Game:Compare(catA, tieA, catB, tieB)
    if catA ~= catB then return catA > catB and 1 or -1 end
    for i = 1, math.max(#tieA, #tieB) do
        local a, b = tieA[i] or 0, tieB[i] or 0
        if a ~= b then return a > b and 1 or -1 end
    end
    return 0
end
