local addonName, NS = ...
local GR = _G["GRS_MainFrame"]

-- Import Constants
local COMM_PREFIX = GR.RR.CONST.COMM_PREFIX
local COMM_DELIM = GR.RR.CONST.COMM_DELIM
local MAX_COMM_CHUNK = GR.RR.CONST.MAX_COMM_CHUNK
local Sanitize = GR.RR.Sanitize
local GetFullPlayerName = GR.RR.GetFullPlayerName
local ParseDate = GR.RR.ParseDate

-- =========================================================================
-- HELPER: AUTHORITY & TOMBSTONING
-- =========================================================================

local function GetSenderRankIndex(senderName)
    if not senderName then return nil end
    local cleanSender = Ambiguate(senderName, "none")
    local num = GetNumGuildMembers()
    for i = 1, num do
        local name, _, rankIndex = GetGuildRosterInfo(i)
        if name then
            if Ambiguate(name, "none") == cleanSender then
                return rankIndex
            end
        end
    end
    return nil
end

local function IsSenderAuthority(senderName)
    local rankIndex = GetSenderRankIndex(senderName)
    if not rankIndex then return false end
    if rankIndex == 0 then return true end
    local flags = C_GuildInfo.GuildControlGetRankFlags(rankIndex + 1)
    if flags and flags[12] then return true end
    return false
end

local function TombstoneEvent(id)
    if not GRS_RaidRosterDB then return end
    if not GRS_RaidRosterDB.graveyard then GRS_RaidRosterDB.graveyard = {} end
    GRS_RaidRosterDB.graveyard[id] = GetServerTime()
    if GRS_RaidRosterDB.events then GRS_RaidRosterDB.events[id] = nil end
end

local function IsTombstoned(id)
    if GRS_RaidRosterDB and GRS_RaidRosterDB.graveyard and GRS_RaidRosterDB.graveyard[id] then
        return true
    end
    return false
end
-- Export for use in Comm layer
GR.RR.IsTombstoned = IsTombstoned 

-- =========================================================================
-- NETWORK LAYER (LIVE BROADCASTING & RECEPTION)
-- =========================================================================

function GR:RR_BroadcastEvent(eventData, target)
    if not IsInGuild() then return end
    if IsTombstoned(eventData.id) then return end
    if not eventData.creator then eventData.creator = GetFullPlayerName() end
    
    local timeStr = (eventData.startTime or "00:00") .. "-" .. (eventData.endTime or "00:00")
    local str = table.concat({
        "EVT", eventData.id, Sanitize(eventData.name), eventData.date, timeStr, 
        eventData.type, eventData.limit or 99, eventData.rankLimit or 99, Sanitize(eventData.def or ""),
        eventData.creator or "UNKNOWN"
    }, COMM_DELIM)
    
    local channel = target and "WHISPER" or "GUILD"
    if target and GR.db.showDebugOutput then 
        print("|cff00ccffGRS NET:|r Targeted Sync-Whisper to " .. target)
    end
    C_ChatInfo.SendAddonMessage(COMM_PREFIX, str, channel, target)
    GR:RR_BroadcastRosterFull(eventData.id, target)
end

function GR:RR_BroadcastDelete(idList)
    if not IsInGuild() or #idList == 0 then return end
    for _, id in ipairs(idList) do TombstoneEvent(id) end
    local str = "DEL" .. COMM_DELIM .. table.concat(idList, ",")
    C_ChatInfo.SendAddonMessage(COMM_PREFIX, str, "GUILD")
end

function GR:RR_BroadcastSignUp(eventId, role, status)
    if not IsInGuild() then return end
    if IsTombstoned(eventId) then return end
    local str = table.concat({"SIGNUP", eventId, role, status, GetServerTime()}, COMM_DELIM)
    C_ChatInfo.SendAddonMessage(COMM_PREFIX, str, "GUILD")
end

function GR:RR_BroadcastModStatus(eventId, targetName, targetRole, newStatus)
    if not IsInGuild() then return end
    if IsTombstoned(eventId) then return end
    local cleanTarget = Ambiguate(targetName, "none")
    local str = table.concat({"MOD_STATUS", eventId, cleanTarget, targetRole, newStatus, GetServerTime()}, COMM_DELIM)
    C_ChatInfo.SendAddonMessage(COMM_PREFIX, str, "GUILD")
end

function GR:RR_BroadcastRemove(eventId, targetName)
    if not IsInGuild() then return end
    local cleanTarget = Ambiguate(targetName, "none")
    local str = table.concat({"REMOVE_PLAYER", eventId, cleanTarget}, COMM_DELIM)
    C_ChatInfo.SendAddonMessage(COMM_PREFIX, str, "GUILD")
end

function GR:RR_BroadcastRosterFull(eventId, target)
    if not IsInGuild() then return end
    if IsTombstoned(eventId) then return end
    local evt = GRS_RaidRosterDB.events[eventId]
    if not evt then return end
    
    local channel = target and "WHISPER" or "GUILD"
    -- REMOVED: ROSTER_CLEAR is no longer sent. Receiver uses additive merge.
    -- RR_UpdateMemberStatus() already deduplicates via RR_RemoveMember().
    
    -- Build all chunks first
    local chunks = {}
    local buffer = ""
    local function FlushBuffer()
        if buffer ~= "" then
            table.insert(chunks, buffer)
            buffer = ""
        end
    end
    for roleKey, list in pairs(evt.roster) do
        for _, member in ipairs(list) do
            local entry = table.concat({member.name or "Unknown", member.role or "DPS", member.status or "BENCH", member.ts or 0}, ":")
            if #buffer + #entry + 1 > MAX_COMM_CHUNK then
                FlushBuffer()
                buffer = entry
            else
                if buffer == "" then buffer = entry else buffer = buffer .. ";" .. entry end
            end
        end
    end
    FlushBuffer()
    
    -- Paced send: one chunk per 0.3s to avoid throttle drops
    for i, chunk in ipairs(chunks) do
        C_Timer.After((i - 1) * 0.3, function()
            C_ChatInfo.SendAddonMessage(COMM_PREFIX, "ROSTER_DAT"..COMM_DELIM..eventId..COMM_DELIM..chunk, channel, target)
        end)
    end
end

function GR:RR_OnComm(prefix, text, channel, sender)
    if prefix ~= COMM_PREFIX then return end
    local senderName = Ambiguate(sender, "none")
    local myName = GetFullPlayerName()
    if senderName == myName then return end
    
    local parts = { strsplit(COMM_DELIM, text) }
    local op = parts[1]
    
    -- =========================================================================
    -- HANDOFF: SYNC PROTOCOL (Routing to RR_Comm.lua)
    -- =========================================================================
    if op == "S_ASK" or op == "S_OFFER" or op == "S_FETCH" then
        if GR.db.showDebugOutput then print("|cff00ccffGRS NET:|r Detecting " .. op .. " from " .. senderName .. " via " .. channel) end
        
        -- Safe Payload Extraction (Reconstructing after split)
        local payload = nil
        local start, finish = string.find(text, "%" .. COMM_DELIM)
        if finish then
            payload = string.sub(text, finish + 1)
        end

        if GR.RR_HandleSyncComm then
            GR:RR_HandleSyncComm(op, payload, sender)
        else
            print("|cffFF0000GRS ERROR:|r RR_HandleSyncComm missing!")
        end
        return
    end

    -- =========================================================================
    -- LIVE DATA PROCESSING
    -- =========================================================================
    if op == "EVT" then
        local id = parts[2]
        if IsTombstoned(id) then
            if IsSenderAuthority(senderName) then
                if GRS_RaidRosterDB and GRS_RaidRosterDB.graveyard then
                    GRS_RaidRosterDB.graveyard[id] = nil
                end
            else
                GR:RR_BroadcastDelete({id})
                return
            end
        end
    end
    
    local isAuthority = IsSenderAuthority(senderName)
    
    if op == "EVT" then
        local id = parts[2]; if not id then return end
        local incomingCreator = parts[10]
        if not GRS_RaidRosterDB then GRS_RaidRosterDB = {} end
        if not GRS_RaidRosterDB.events then GRS_RaidRosterDB.events = {} end
        local evt = GRS_RaidRosterDB.events[id]
        local isNewToMe = (evt == nil)
        local allowed = isNewToMe or isAuthority
        
        -- BYPASS: Allow if creator matches or if channel is WHISPER (Targeted Sync)
        if not allowed then
            if incomingCreator and Ambiguate(incomingCreator, "none") == senderName then allowed = true
            elseif evt and evt.creator and Ambiguate(evt.creator, "none") == senderName then allowed = true end
        end
        if channel == "WHISPER" then allowed = true end
        
        if not allowed then return end
        
        if not evt then
            evt = { roster = { TANK={}, HEALER={}, DPS={}, BENCH={}, TENTATIVE={}, ABSENT={}, NOSHOW={} } }
            GRS_RaidRosterDB.events[id] = evt
        end
        evt.id = id; evt.name = parts[3]; evt.date = parts[4]
        local ts, te = strsplit("-", parts[5] or "")
        evt.startTime = ts; evt.endTime = te
        evt.time = parts[5]; evt.type = parts[6]; evt.limit = tonumber(parts[7])
        evt.rankLimit = tonumber(parts[8]); evt.def = parts[9]
        evt.creator = incomingCreator or evt.creator
        GR:RR_ScanDB()
        
    elseif op == "ROSTER_CLEAR" then
        local eid = parts[2]
        if IsTombstoned(eid) then return end
        local evt = GRS_RaidRosterDB.events[eid]
        if evt then
            local isCreator = (evt.creator and Ambiguate(evt.creator, "none") == senderName)
            if channel == "WHISPER" or (isAuthority or isCreator) then
                evt.roster = { TANK={}, HEALER={}, DPS={}, BENCH={}, TENTATIVE={}, ABSENT={}, NOSHOW={} }
                if GR.RR.selectedEvent and GR.RR.selectedEvent.id == eid then GR:RR_SelectEvent(evt) end
            end
        end
        
    elseif op == "ROSTER_DAT" then
        local eid = parts[2]
        if IsTombstoned(eid) then return end
        local evt = GRS_RaidRosterDB.events[eid]
        if evt then
            local isCreator = (evt.creator and Ambiguate(evt.creator, "none") == senderName)
            if channel == "WHISPER" or (isAuthority or isCreator) then
                local payload = parts[3]
                if payload then
                    for entry in string.gmatch(payload, "([^;]+)") do
                        local name, role, status, ts = strsplit(":", entry)
                        -- [FIX] Trim whitespace from network payloads
                        if status then status = strtrim(status) end
                        ts = tonumber(ts) or 0
                        GR:RR_UpdateMemberStatus(eid, name, role, status, ts)
                    end
                end
            end
        end
        
    elseif op == "DEL" then
        local ids = parts[2]
        if ids then
            for id in string.gmatch(ids, "([^,]+)") do
                local allowed = isAuthority
                if not allowed and GRS_RaidRosterDB.events then
                    local existing = GRS_RaidRosterDB.events[id]
                    if existing and existing.creator and Ambiguate(existing.creator, "none") == senderName then allowed = true end
                end
                if allowed then TombstoneEvent(id) end
            end
            GR:RR_ScanDB()
            if GR.RR.selectedEvent and not GRS_RaidRosterDB.events[GR.RR.selectedEvent.id] then GR:RR_SelectEvent(nil) end
        end
        
    elseif op == "SIGNUP" or op == "MOD_STATUS" then
        local eid, target, role, status, ts; local isAllowed = false
        if op == "SIGNUP" then
            eid = parts[2]; target = sender; role = parts[3]; status = parts[4]; ts = tonumber(parts[5]); isAllowed = true
        else
            eid = parts[2]; target = parts[3]; role = parts[4]; status = parts[5]; ts = tonumber(parts[6])
            local evt = GRS_RaidRosterDB.events[eid]
            local isCreator = evt and (evt.creator and Ambiguate(evt.creator, "none") == senderName)
            if isAuthority or isCreator then
                isAllowed = true
                local displayTarget = strsplit("-", Ambiguate(target, "none"))
                GR:RR_AddLogEntry(senderName, string.format("Set %s to %s for event %s", displayTarget, status, eid))
            end
        end
        if isAllowed then
            if IsTombstoned(eid) then GR:RR_BroadcastDelete({eid}) return end
            -- [FIX] Ensure whitespace is stripped
            if status then status = strtrim(status) end
            GR:RR_UpdateMemberStatus(eid, target, role, status, ts)
        end
        
    elseif op == "REMOVE_PLAYER" then
        local eid, target = parts[2], parts[3]
        if IsTombstoned(eid) then return end
        local evt = GRS_RaidRosterDB.events[eid]
        local isCreator = evt and (evt.creator and Ambiguate(evt.creator, "none") == senderName)
        if isAuthority or isCreator then GR:RR_RemoveMember(eid, target) end
    end
end