---@diagnostic disable: undefined-field, undefined-global
---@type string
local AddonName = ...
---@class Private
local Private = select(2, ...)
---@class WTAPI
local WTAPI = WTAPI

local LibSerialize = LibStub("LibSerialize")
local LibDeflate = LibStub("LibDeflate")
local AceComm = LibStub("AceComm-3.0")
local wt = Private.Ace

-- Hosts the shared Private:Broadcast send API and the WT_MSG receive/dispatch
-- chokepoint, using the LibSerialize -> LibDeflate:CompressDeflate ->
-- LibDeflate:EncodeForWoWAddonChannel pipeline (and its inverse on receive).
-- WT_MSG is the addon's sole comm prefix -- the legacy dual-send/receive
-- prefixes were removed in COMM-F1 (phase 53); this file now speaks only
-- WT_MSG.
--
-- Expected Private.* dependencies: Private.Ace (wt, created by WildThings.lua),
-- and the error-containment helper added to Utility.lua in 24-01.
--
-- Timing: comm registration (the sole RegisterComm call, for WT_MSG_PREFIX)
-- happens inside Private:InitializeTransmission(), called once from
-- wt:OnInitialize() in WildThings.lua -- never at file load.

-- WT_MSG is the single consolidated comm prefix (COMM-01): one AceComm prefix,
-- event name as routing key, LibSerialize type-tagged args over the
-- LibSerialize -> LibDeflate:CompressDeflate -> LibDeflate:EncodeForWoWAddonChannel
-- pipeline. Registered exactly once, on the bare AceComm object, inside
-- Private:InitializeTransmission().
local WT_MSG_PREFIX = "WT_MSG"

-- GUILD-channel events are server-enforced-membership (Blizzard), so events on
-- this whitelist skip the raid/party membership check for GUILD distribution.
-- Everything else on GUILD is rejected; group channels always require
-- UnitInRaid/UnitInParty regardless of this whitelist. Empty today -- PartyMode
-- distributes over RAID, not GUILD -- kept for any future GUILD-distributed event.
local GUILD_WHITELIST = {}

-- Private:Broadcast(event, channel, ...): the consolidated WT_MSG send API.
-- Serializes (event, ...) with LibSerialize (type-tagged, arbitrary arity),
-- compresses, byte-encodes for the addon channel, and sends on WT_MSG. The
-- sender name is NEVER placed in the payload -- the receiver trusts AceComm's
-- server-supplied sender argument instead.
function Private:Broadcast(event, channel, ...)
    local serialized = LibSerialize:Serialize(event, ...)
    local compressed = LibDeflate:CompressDeflate(serialized, {level = 9})
    local encoded = LibDeflate:EncodeForWoWAddonChannel(compressed)

    AceComm:SendCommMessage(WT_MSG_PREFIX, encoded, channel)
end

-- Private:ValidateCommSender(event, distribution, sender): distribution-aware
-- sender/channel validation. GUILD distribution is server-enforced membership
-- (Blizzard), so it is accepted only for events on the explicit GUILD_WHITELIST
-- (empty today -- PartyMode never distributes over GUILD). RAID/PARTY/
-- INSTANCE_CHAT require the sender to actually be in our raid/party.
function Private:ValidateCommSender(event, distribution, sender)
    if distribution == "GUILD" then
        return GUILD_WHITELIST[event] == true
    end

    if distribution == "RAID" or distribution == "PARTY" or distribution == "INSTANCE_CHAT" then
        return UnitInRaid(sender) or UnitInParty(sender)
    end

    return false
end

-- DispatchCommEvent(event, distribution, sender, ...): the single
-- spoof-guarded chokepoint between the wire and business logic. Comm-delivered
-- messages reach Private.commHandlers ONLY through here -- a crafted
-- addon-channel message can never share a call path with a local/Blizzard-
-- event trigger.
local function DispatchCommEvent(event, distribution, sender, ...)
    if not event then return end
    if UnitIsUnit(sender, "player") then return end -- self-messages skipped centrally
    if not Private:ValidateCommSender(event, distribution, sender) then return end

    local handler = Private.commHandlers[event]
    if handler then
        handler(sender, ...)
    end
end

-- ReceiveWTMsg: WT_MSG receive callback. Guard-clause decode chain -- a
-- malformed, empty, or truncated payload is silently dropped, no Lua error,
-- no handler invocation. On decompression failure the decoded bytes are
-- treated as old-version plaintext (COMM-01's literal fallback rule; no
-- plaintext sender exists on WT_MSG today, this future-proofs the wire).
local function ReceiveWTMsg(_, payload, distribution, sender)
    local decoded = LibDeflate:DecodeForWoWAddonChannel(payload)
    if not decoded then return end

    local decompressed = LibDeflate:DecompressDeflate(decoded)
    if not decompressed then
        decompressed = decoded
    end

    -- LibSerialize:Deserialize is already internally protected-call-safe; its
    -- first return IS the success flag -- do not add redundant wrapping.
    local resultsN = select("#", LibSerialize:Deserialize(decompressed))
    local results = { LibSerialize:Deserialize(decompressed) }
    local success = results[1]
    if not success then return end

    local event = results[2]
    DispatchCommEvent(event, distribution, sender, unpack(results, 3, resultsN))
end

function Private:InitializeTransmission()
    AceComm:RegisterComm(WT_MSG_PREFIX, ReceiveWTMsg)
end
