-- ============================================================
-- Komo Hold'Em — Comms
-- Host-authoritative protocol over addon messages.
--
-- Channels:
--   PARTY/RAID broadcast — public state (tables, lobby, board,
--                          bets, pot, turn order, results)
--   WHISPER            — private hole cards, one message per player
--   GUILD              — guild leaderboard sync only
--
-- Message format:  v1^TYPE^payload
-- Payload fields separated by "~", lists by ";" — cheap to parse,
-- no serializer dependency. 255-byte limit per message respected
-- by chunking (CHUNK/END) for anything bigger.
-- ============================================================
local _, KH = ...
KH.Comms = {}
local Comms = KH.Comms

local PROTOCOL_VERSION = 1

-- message types (public broadcast unless noted)
Comms.MSG = {
    TABLE_ANNOUNCE = "TA",  -- host -> group: table exists (id, name, stakes, amount, seats, taken)
    TABLE_CLOSE    = "TC",  -- host -> group: table gone
    JOIN_REQUEST   = "JR",  -- player -> host (whisper)
    JOIN_ACCEPT    = "JA",  -- host -> player (whisper): seat granted + full config
    LOBBY_STATE    = "LS",  -- host -> group: seats + ready flags
    READY          = "RD",  -- player -> host (whisper): ready/unready
    GAME_START     = "GS",  -- host -> group
    HOLE_CARDS     = "HC",  -- host -> player (WHISPER ONLY — never broadcast)
    BOARD          = "BD",  -- host -> group: street + community cards
    ACTION_REQUEST = "AR",  -- host -> group: whose turn, call amount, min raise, deadline
    ACTION         = "AC",  -- player -> host (whisper): fold/check/call/bet + amount
    STATE          = "ST",  -- host -> group: pots, stacks, bets after each action
    SHOWDOWN       = "SD",  -- host -> group: revealed hands + winners
    RESYNC_REQ     = "RQ",  -- player -> host: I reloaded, resend everything public
    RESYNC         = "RS",  -- host -> player (whisper): full public snapshot (+ their holes)
    SETTLE         = "SE",  -- host -> group: final nets + transfer list
    GUILD_SYNC_REQ = "GQ",  -- player -> guild: send me your ledger heads
    GUILD_SYNC     = "GA",  -- player -> guild: aggregate rows (id-deduped)
}

function Comms:Init()
    C_ChatInfo.RegisterAddonMessagePrefix(KH.PREFIX)
    local f = CreateFrame("Frame")
    f:RegisterEvent("CHAT_MSG_ADDON")
    f:SetScript("OnEvent", function(_, _, prefix, text, channel, sender)
        if prefix ~= KH.PREFIX then return end
        Comms:OnMessage(text, channel, Ambiguate(sender, "none"))
    end)
end

-- ------------------------------------------------------------
-- send helpers
-- ------------------------------------------------------------
local function pack(msgType, ...)
    local fields = {}
    for i = 1, select("#", ...) do
        -- strip protocol separators from user-provided strings (table names etc.)
        fields[i] = tostring(select(i, ...)):gsub("[%^~]", "")
    end
    return "v" .. PROTOCOL_VERSION .. "^" .. msgType .. "^" .. table.concat(fields, "~")
end

function Comms:Broadcast(msgType, ...)
    local msg = pack(msgType, ...)
    local channel = IsInRaid() and "RAID" or IsInGroup() and "PARTY" or nil
    if channel then
        -- our own PARTY/RAID sends echo back to us, driving local UI
        C_ChatInfo.SendAddonMessage(KH.PREFIX, msg, channel)
        return true
    end
    -- solo: loop back locally so the host's own UI still updates
    self:OnMessage(msg, "LOOPBACK", KH.playerFull)
    return false
end

function Comms:Whisper(target, msgType, ...)
    C_ChatInfo.SendAddonMessage(KH.PREFIX, pack(msgType, ...), "WHISPER", target)
end

function Comms:Guild(msgType, ...)
    if not IsInGuild() then return false end
    C_ChatInfo.SendAddonMessage(KH.PREFIX, pack(msgType, ...), "GUILD")
    return true
end

-- ------------------------------------------------------------
-- receive & dispatch
-- ------------------------------------------------------------
local handlers = {}

function Comms:OnMessage(text, channel, sender)
    local version, msgType, payload = text:match("^v(%d+)%^(%u%u)%^?(.*)$")
    if not version then return end
    KH:Debug("RECV " .. msgType .. " from " .. sender .. " [" .. channel .. "] " .. (payload or ""))
    version = tonumber(version)
    if version > PROTOCOL_VERSION then
        -- newer client in group; we still parse what we understand
        KH:Debug("Newer protocol v" .. version .. " from " .. sender)
    end
    local list = handlers[msgType]
    if list then
        local fields = payload and { strsplit("~", payload) } or {}
        for _, fn in ipairs(list) do
            local ok, err = pcall(fn, sender, channel, fields)
            if not ok then
                KH:Print("|cfff08a80Handler error|r [" .. msgType .. "]: " .. tostring(err))
            end
        end
    end
end

function Comms:RegisterHandler(msgType, fn)
    handlers[msgType] = handlers[msgType] or {}
    table.insert(handlers[msgType], fn)
end

-- ------------------------------------------------------------
-- security note baked into the API shape:
-- SendHoleCards exists ONLY as a whisper. There is deliberately
-- no broadcast variant for HC — hole cards can never leak to the
-- group channel by API misuse.
-- ------------------------------------------------------------
function Comms:SendHoleCards(target, handId, card1, card2)
    self:Whisper(target, self.MSG.HOLE_CARDS, handId, card1, card2)
end
