local addonName, RF = ...

-------------------------------------------------------------------------------
-- SyncEngine: Share alt links, join dates, ban list, tags between guild members
-- Uses C_ChatInfo addon messages over GUILD channel.
-- Protocol: "TYPE:payload" per message (max 255 bytes)
--   ALT:alt:main:timestamp        -- alt link
--   UNALT:alt:timestamp           -- alt unlink
--   JOIN:name:timestamp           -- join date
--   BAN:name:timestamp:reason     -- ban entry
--   UNBAN:name:timestamp          -- ban removal
--   TAG:name:label:color:timestamp  -- add tag
--   UNTAG:name:label:timestamp      -- remove tag
--   NOTE:name:ts:part:total:text  -- roster note (multi-part)
--   NDEL:name:timestamp           -- note deletion
--   REQ                           -- request full sync
--   VER:version                   -- version announcement
-------------------------------------------------------------------------------

local PREFIX = "RForge"
local THROTTLE = 0.15  -- seconds between outgoing messages
local SYNC_COOLDOWN = 300 -- seconds between full syncs
local sendQueue = {}
local isSending = false
local lastFullSync = 0
local pendingNotes = {}  -- pendingNotes[name] = { ts=, total=, parts={} }

-- Sync tracking state
RF._syncUsers = {}
RF._lastSyncTime = 0
RF._lastSyncDataReceived = nil
RF._lastFullSyncSent = nil
RF._reqPendingAt = nil

-------------------------------------------------------------------------------
-- Register prefix on load
-------------------------------------------------------------------------------
RF:RegisterCallback("ADDON_LOADED", function()
    C_ChatInfo.RegisterAddonMessagePrefix(PREFIX)
end)

-------------------------------------------------------------------------------
-- Send permission helper
-------------------------------------------------------------------------------
local function CanSendSync()
    if RF.charDB and RF.charDB.syncEnabled == false then return false end
    if RF.charDB and RF.charDB.syncWriteEnabled == false then return false end
    return true
end

-------------------------------------------------------------------------------
-- Queue & Throttled Send
-------------------------------------------------------------------------------
local function QueueMessage(msg)
    if #msg > 255 then
        -- Truncate reason fields if needed
        msg = msg:sub(1, 255)
    end
    tinsert(sendQueue, msg)
    RF:ProcessSendQueue()
end

function RF:ProcessSendQueue()
    if isSending or #sendQueue == 0 then return end
    isSending = true

    local msg = tremove(sendQueue, 1)
    local result = C_ChatInfo.SendAddonMessage(PREFIX, msg, "GUILD")
    if result ~= Enum.SendAddonMessageResult.Success then
        RF:Debug("SyncEngine: SendAddonMessage failed, result=" .. tostring(result))
    end

    C_Timer.After(THROTTLE, function()
        isSending = false
        if #sendQueue > 0 then
            RF:ProcessSendQueue()
        else
            if RF._fullSyncInProgress then
                RF._fullSyncInProgress = false
                RF:Print("Sync complete — all data sent to guild.")
            end
        end
    end)
end

-------------------------------------------------------------------------------
-- Broadcast Functions (outgoing)
-------------------------------------------------------------------------------
function RF:BroadcastAltLink(alt, main)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    local ts = guildDB and guildDB.alts.timestamps and guildDB.alts.timestamps[alt] or time()
    QueueMessage(format("ALT:%s:%s:%d", alt, main, ts))
end

function RF:BroadcastAltUnlink(alt)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    local ts = guildDB and guildDB.alts.timestamps and guildDB.alts.timestamps[alt] or time()
    QueueMessage(format("UNALT:%s:%d", alt, ts))
end

function RF:BroadcastJoinDate(name, joinTimestamp)
    if not CanSendSync() then return end
    QueueMessage(format("JOIN:%s:%d", name, joinTimestamp))
end

function RF:BroadcastBan(name, reason)
    if not CanSendSync() then return end
    reason = (reason or ""):gsub(":", " ")  -- strip colons from reason
    QueueMessage(format("BAN:%s:%d:%s", name, time(), reason))
end

function RF:BroadcastUnban(name)
    if not CanSendSync() then return end
    QueueMessage(format("UNBAN:%s:%d", name, time()))
end

function RF:BroadcastTag(name, label, color)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    local ts = guildDB and guildDB.tagTimestamps and guildDB.tagTimestamps[name .. ":" .. label] or time()
    QueueMessage(format("TAG:%s:%s:%s:%d", name, label, color or "3B82F6", ts))
end

function RF:BroadcastUntag(name, label)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    local ts = guildDB and guildDB.tagTimestamps and guildDB.tagTimestamps[name .. ":" .. label] or time()
    QueueMessage(format("UNTAG:%s:%s:%d", name, label, ts))
end

function RF:BroadcastNote(name)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end
    local text = guildDB.notes[name]
    if not text or text == "" then return end
    local ts = guildDB.noteTimestamps and guildDB.noteTimestamps[name] or time()

    -- Calculate chunk size: "NOTE:name:ts:partNum:totalParts:" header
    local header = format("NOTE:%s:%d:", name, ts)
    -- Reserve space for part numbers (e.g. "99:99:") — max ~6 chars
    local chunkSize = 255 - #header - 6
    if chunkSize < 20 then chunkSize = 20 end

    local parts = {}
    for i = 1, #text, chunkSize do
        tinsert(parts, text:sub(i, i + chunkSize - 1))
    end

    for i, chunk in ipairs(parts) do
        QueueMessage(format("NOTE:%s:%d:%d:%d:%s", name, ts, i, #parts, chunk))
    end
end

function RF:BroadcastNoteDelete(name)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    local ts = guildDB and guildDB.noteTimestamps and guildDB.noteTimestamps[name] or time()
    QueueMessage(format("NDEL:%s:%d", name, ts))
end

-------------------------------------------------------------------------------
-- Team Broadcasts
-------------------------------------------------------------------------------
function RF:BroadcastTeamCreate(teamID)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end
    local team = guildDB.teams[teamID]
    if not team then return end
    local ts = guildDB.teamTimestamps[teamID] or time()
    local customLabel = team.customTypeLabel or ""
    local officerFlag = team.officerOnly and "1" or "0"
    QueueMessage(format("TNEW:%s:%s:%s:%d:%d:%s:%s", teamID, team.name, team.teamType, team.maxSize, ts, customLabel, officerFlag))
end

function RF:BroadcastTeamDelete(teamID)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    local ts = guildDB and guildDB.teamTimestamps[teamID] or time()
    QueueMessage(format("TDEL:%s:%d", teamID, ts))
end

function RF:BroadcastTeamMember(teamID, memberName)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end
    local team = guildDB.teams[teamID]
    if not team or not team.members[memberName] then return end
    local ts = team.memberTimestamps[memberName] or time()
    QueueMessage(format("TMEM:%s:%s:%s:%d", teamID, memberName, team.members[memberName].role, ts))
end

function RF:BroadcastTeamMemberRemove(teamID, memberName)
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end
    local team = guildDB.teams[teamID]
    local ts = team and team.memberTimestamps[memberName] or time()
    QueueMessage(format("TREM:%s:%s:%d", teamID, memberName, ts))
end

function RF:BroadcastVersion()
    if RF.charDB and RF.charDB.syncEnabled == false then return end
    QueueMessage(format("VER:%s", RF.VERSION))
end

function RF:RequestFullSync()
    if RF.charDB and RF.charDB.syncEnabled == false then return end
    local now = time()
    if now - lastFullSync < SYNC_COOLDOWN then return end
    lastFullSync = now
    QueueMessage("REQ")
end

-------------------------------------------------------------------------------
-- Full Sync: Send all local data to guild
-------------------------------------------------------------------------------
function RF:SendFullSync()
    if not CanSendSync() then return end
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    RF._lastFullSyncSent = time()
    RF._fullSyncInProgress = true

    -- Broadcast version
    RF:BroadcastVersion()

    -- Alt links
    for alt, main in pairs(guildDB.alts.links) do
        local ts = guildDB.alts.timestamps and guildDB.alts.timestamps[alt] or time()
        QueueMessage(format("ALT:%s:%s:%d", alt, main, ts))
    end

    -- Join dates
    for name, data in pairs(guildDB.tracking.members) do
        if data.joinDate then
            QueueMessage(format("JOIN:%s:%d", name, data.joinDate))
        end
    end

    -- Ban list
    for name, info in pairs(guildDB.tracking.banList) do
        local reason = (info.reason or ""):gsub(":", " ")
        QueueMessage(format("BAN:%s:%d:%s", name, info.time or time(), reason))
    end

    -- Tags
    if guildDB.tags then
        for name, tags in pairs(guildDB.tags) do
            for _, tag in ipairs(tags) do
                local ts = guildDB.tagTimestamps and guildDB.tagTimestamps[name .. ":" .. tag.label] or time()
                QueueMessage(format("TAG:%s:%s:%s:%d", name, tag.label, tag.color or "3B82F6", ts))
            end
        end
    end

    -- RosterNotes
    for name, text in pairs(guildDB.notes) do
        RF:BroadcastNote(name)
    end

    -- Teams
    for teamID, team in pairs(guildDB.teams) do
        RF:BroadcastTeamCreate(teamID)
        for memberName in pairs(team.members) do
            RF:BroadcastTeamMember(teamID, memberName)
        end
    end
end

-------------------------------------------------------------------------------
-- Sync user tracking
-------------------------------------------------------------------------------
function RF:GetOnlineSyncUsers()
    local now = time()
    local count = 0
    for sender, info in pairs(RF._syncUsers) do
        if now - info.lastSeen > 600 then
            RF._syncUsers[sender] = nil
        else
            count = count + 1
        end
    end
    return count
end

function RF:ForceRequestFullSync()
    lastFullSync = 0
    RF:RequestFullSync()
    RF:BroadcastVersion()
end

-------------------------------------------------------------------------------
-- Receive Handler (incoming)
-------------------------------------------------------------------------------
local function OnAddonMessage(_, prefix, message, channel, sender)
    if prefix ~= PREFIX then return end
    if channel ~= "GUILD" then return end

    -- Don't process our own messages
    local myName = UnitName("player")
    sender = Ambiguate(sender or "", "guild")
    if sender == myName then return end

    -- Sync disabled: ignore everything
    if RF.charDB and RF.charDB.syncEnabled == false then return end

    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    local msgType, payload = message:match("^(%u+):?(.*)")
    if not msgType then return end

    -- Officer-only guard for data messages
    if RF.charDB and RF.charDB.syncOfficerOnly then
        if msgType ~= "VER" and msgType ~= "REQ" then
            local memberInfo = RF.rosterByName and RF.rosterByName[sender]
            if not memberInfo or memberInfo.rankIndex > 1 then
                if RF.charDB.debugMode then
                    RF:Print(format("Sync: Rejected %s from %s (officer-only mode)", msgType, sender))
                end
                return
            end
        end
    end

    if msgType == "ALT" then
        local alt, main, ts = payload:match("^(.-):(.+):(%d+)$")
        if alt and main and alt ~= "" then
            ts = tonumber(ts) or 0
            local localTs = guildDB.alts.timestamps and guildDB.alts.timestamps[alt] or 0
            -- Last-write-wins: only apply if incoming ts > local ts
            if ts > localTs then
                local existing = guildDB.alts.links[alt]
                if not existing or existing ~= main then
                    RF:LinkAlt(alt, main)
                    -- Overwrite timestamp with sender's ts (LinkAlt sets it to time())
                    if not guildDB.alts.timestamps then guildDB.alts.timestamps = {} end
                    guildDB.alts.timestamps[alt] = ts
                    RF._lastSyncDataReceived = time()
                    RF:FireCallbacks("SYNC_STATUS_CHANGED")
                    if RF.charDB and RF.charDB.debugMode then
                        RF:Print(format("Sync: %s linked %s as alt of %s", sender, alt, main))
                    end
                end
            end
        end

    elseif msgType == "UNALT" then
        local alt, ts = payload:match("^(.-):(%d+)$")
        if alt and alt ~= "" then
            ts = tonumber(ts) or 0
            local localTs = guildDB.alts.timestamps and guildDB.alts.timestamps[alt] or 0
            if ts > localTs then
                if guildDB.alts.links[alt] then
                    RF:UnlinkAlt(alt)
                    -- Overwrite timestamp with sender's ts
                    if not guildDB.alts.timestamps then guildDB.alts.timestamps = {} end
                    guildDB.alts.timestamps[alt] = ts
                    RF._lastSyncDataReceived = time()
                    RF:FireCallbacks("SYNC_STATUS_CHANGED")
                    if RF.charDB and RF.charDB.debugMode then
                        RF:Print(format("Sync: %s unlinked %s", sender, alt))
                    end
                end
            end
        end

    elseif msgType == "JOIN" then
        local name, ts = payload:match("^(.+):(%d+)$")
        if name then
            ts = tonumber(ts) or 0
            local memberData = guildDB.tracking.members[name]
            if memberData then
                -- Keep the earliest join date
                if not memberData.joinDate or ts < memberData.joinDate then
                    memberData.joinDate = ts
                end
            else
                -- New member we haven't tracked yet
                guildDB.tracking.members[name] = {
                    joinDate = ts,
                    lastSeen = 0,
                    rankHistory = {},
                    activity = {},
                }
            end
        end

    elseif msgType == "BAN" then
        local name, ts, reason = payload:match("^(.-):(%d+):(.*)$")
        if name and name ~= "" then
            ts = tonumber(ts) or time()
            if not guildDB.tracking.banList[name] then
                guildDB.tracking.banList[name] = {
                    time = ts,
                    reason = reason or "",
                    addedBy = sender,
                }
                if RF.charDB and RF.charDB.debugMode then
                    RF:Print(format("Sync: %s banned %s", sender, name))
                end
            end
        end

    elseif msgType == "UNBAN" then
        local name, ts = payload:match("^(.+):(%d+)$")
        if name then
            if guildDB.tracking.banList[name] then
                guildDB.tracking.banList[name] = nil
                if RF.charDB and RF.charDB.debugMode then
                    RF:Print(format("Sync: %s unbanned %s", sender, name))
                end
            end
        end

    elseif msgType == "TAG" then
        local name, label, color, ts = payload:match("^(.-):(.-):(.-):(%d+)$")
        if name and label and label ~= "" then
            ts = tonumber(ts) or 0
            if not guildDB.tagTimestamps then guildDB.tagTimestamps = {} end
            local localTs = guildDB.tagTimestamps[name .. ":" .. label] or 0
            if ts > localTs then
                RF:AddTag(name, label, color)
                guildDB.tagTimestamps[name .. ":" .. label] = ts
                RF._lastSyncDataReceived = time()
                RF:FireCallbacks("SYNC_STATUS_CHANGED")
                if RF.charDB and RF.charDB.debugMode then
                    RF:Print(format("Sync: %s tagged %s as %s", sender, name, label))
                end
            end
        end

    elseif msgType == "UNTAG" then
        local name, label, ts = payload:match("^(.-):(.+):(%d+)$")
        if name and label and label ~= "" then
            ts = tonumber(ts) or 0
            if not guildDB.tagTimestamps then guildDB.tagTimestamps = {} end
            local localTs = guildDB.tagTimestamps[name .. ":" .. label] or 0
            if ts > localTs then
                RF:RemoveTag(name, label)
                guildDB.tagTimestamps[name .. ":" .. label] = ts
                RF._lastSyncDataReceived = time()
                RF:FireCallbacks("SYNC_STATUS_CHANGED")
                if RF.charDB and RF.charDB.debugMode then
                    RF:Print(format("Sync: %s removed tag %s from %s", sender, label, name))
                end
            end
        end

    elseif msgType == "NOTE" then
        local name, ts, partNum, totalParts, chunk = payload:match("^(.-):(%d+):(%d+):(%d+):(.*)$")
        if name and name ~= "" and chunk then
            ts = tonumber(ts) or 0
            partNum = tonumber(partNum) or 1
            totalParts = tonumber(totalParts) or 1
            if not guildDB.noteTimestamps then guildDB.noteTimestamps = {} end
            local localTs = guildDB.noteTimestamps[name] or 0
            if ts > localTs then
                -- Initialize or validate pending assembly
                if not pendingNotes[name] or pendingNotes[name].ts ~= ts then
                    pendingNotes[name] = { ts = ts, total = totalParts, parts = {} }
                end
                pendingNotes[name].parts[partNum] = chunk

                -- Check if all parts received
                local pn = pendingNotes[name]
                local complete = true
                for i = 1, pn.total do
                    if not pn.parts[i] then complete = false; break end
                end

                if complete then
                    local fullText = table.concat(pn.parts)
                    guildDB.notes[name] = fullText
                    guildDB.noteTimestamps[name] = ts
                    pendingNotes[name] = nil
                    RF._lastSyncDataReceived = time()
                    RF:FireCallbacks("SYNC_STATUS_CHANGED")
                    RF:FireCallbacks("NOTE_UPDATED", name, fullText)
                    if RF.charDB and RF.charDB.debugMode then
                        RF:Print(format("Sync: %s set RosterNote for %s", sender, name))
                    end
                end
            end
        end

    elseif msgType == "NDEL" then
        local name, ts = payload:match("^(.+):(%d+)$")
        if name then
            ts = tonumber(ts) or 0
            if not guildDB.noteTimestamps then guildDB.noteTimestamps = {} end
            local localTs = guildDB.noteTimestamps[name] or 0
            if ts > localTs then
                guildDB.notes[name] = nil
                guildDB.noteTimestamps[name] = ts
                pendingNotes[name] = nil
                RF._lastSyncDataReceived = time()
                RF:FireCallbacks("SYNC_STATUS_CHANGED")
                RF:FireCallbacks("NOTE_UPDATED", name, nil)
                if RF.charDB and RF.charDB.debugMode then
                    RF:Print(format("Sync: %s deleted RosterNote for %s", sender, name))
                end
            end
        end

    elseif msgType == "TNEW" then
        -- Team sync gate
        if RF.charDB and RF.charDB.teamSyncEnabled == false then return end
        if RF.charDB and RF.charDB.teamSyncOfficerOnly then
            local memberInfo = RF.rosterByName and RF.rosterByName[sender]
            if not memberInfo or memberInfo.rankIndex > 1 then return end
        end
        local teamID, name, teamType, maxSize, ts, customLabel, officerFlag = payload:match("^(.-):(.-):(.-):(%d+):(%d+):?([^:]*):?(.*)$")
        if teamID and name and name ~= "" then
            ts = tonumber(ts) or 0
            maxSize = tonumber(maxSize) or 25
            if customLabel == "" then customLabel = nil end
            local officerOnly = officerFlag == "1"
            if not guildDB.teamTimestamps then guildDB.teamTimestamps = {} end
            local localTs = guildDB.teamTimestamps[teamID] or 0
            if ts > localTs then
                if not guildDB.teams[teamID] then
                    guildDB.teams[teamID] = {
                        name = name,
                        teamType = teamType or "custom",
                        maxSize = maxSize,
                        officerOnly = officerOnly,
                        customTypeLabel = customLabel,
                        createdBy = sender,
                        createdAt = ts,
                        updatedAt = ts,
                        members = {},
                        memberTimestamps = {},
                    }
                else
                    guildDB.teams[teamID].name = name
                    guildDB.teams[teamID].teamType = teamType or guildDB.teams[teamID].teamType
                    guildDB.teams[teamID].maxSize = maxSize
                    guildDB.teams[teamID].officerOnly = officerOnly
                    guildDB.teams[teamID].customTypeLabel = customLabel
                    guildDB.teams[teamID].updatedAt = ts
                end
                guildDB.teamTimestamps[teamID] = ts
                RF._lastSyncDataReceived = time()
                RF:FireCallbacks("SYNC_STATUS_CHANGED")
                RF:FireCallbacks("TEAMS_CHANGED")
            end
        end

    elseif msgType == "TDEL" then
        if RF.charDB and RF.charDB.teamSyncEnabled == false then return end
        if RF.charDB and RF.charDB.teamSyncOfficerOnly then
            local memberInfo = RF.rosterByName and RF.rosterByName[sender]
            if not memberInfo or memberInfo.rankIndex > 1 then return end
        end
        local teamID, ts = payload:match("^(.-):(%d+)$")
        if teamID then
            ts = tonumber(ts) or 0
            if not guildDB.teamTimestamps then guildDB.teamTimestamps = {} end
            local localTs = guildDB.teamTimestamps[teamID] or 0
            if ts > localTs then
                guildDB.teams[teamID] = nil
                guildDB.teamTimestamps[teamID] = ts
                RF._lastSyncDataReceived = time()
                RF:FireCallbacks("SYNC_STATUS_CHANGED")
                RF:FireCallbacks("TEAMS_CHANGED")
            end
        end

    elseif msgType == "TMEM" then
        if RF.charDB and RF.charDB.teamSyncEnabled == false then return end
        if RF.charDB and RF.charDB.teamSyncOfficerOnly then
            local memberInfo = RF.rosterByName and RF.rosterByName[sender]
            if not memberInfo or memberInfo.rankIndex > 1 then return end
        end
        local teamID, memberName, role, ts = payload:match("^(.-):(.-):(.-):(%d+)$")
        if teamID and memberName and memberName ~= "" then
            ts = tonumber(ts) or 0
            local team = guildDB.teams[teamID]
            if team then
                if not team.memberTimestamps then team.memberTimestamps = {} end
                local localTs = team.memberTimestamps[memberName] or 0
                if ts > localTs then
                    team.members[memberName] = { role = role or "DPS", addedAt = ts }
                    team.memberTimestamps[memberName] = ts
                    team.updatedAt = ts
                    RF._lastSyncDataReceived = time()
                    RF:FireCallbacks("SYNC_STATUS_CHANGED")
                    RF:FireCallbacks("TEAMS_CHANGED")
                end
            end
        end

    elseif msgType == "TREM" then
        if RF.charDB and RF.charDB.teamSyncEnabled == false then return end
        if RF.charDB and RF.charDB.teamSyncOfficerOnly then
            local memberInfo = RF.rosterByName and RF.rosterByName[sender]
            if not memberInfo or memberInfo.rankIndex > 1 then return end
        end
        local teamID, memberName, ts = payload:match("^(.-):(.+):(%d+)$")
        if teamID and memberName then
            ts = tonumber(ts) or 0
            local team = guildDB.teams[teamID]
            if team then
                if not team.memberTimestamps then team.memberTimestamps = {} end
                local localTs = team.memberTimestamps[memberName] or 0
                if ts > localTs then
                    team.members[memberName] = nil
                    team.memberTimestamps[memberName] = ts
                    team.updatedAt = ts
                    RF._lastSyncDataReceived = time()
                    RF:FireCallbacks("SYNC_STATUS_CHANGED")
                    RF:FireCallbacks("TEAMS_CHANGED")
                end
            end
        end

    elseif msgType == "REQ" then
        -- Fix REQ storm: skip if we recently sent a full sync
        if RF._lastFullSyncSent and (time() - RF._lastFullSyncSent) < 60 then return end
        -- Skip if we have no data to share
        if not next(guildDB.alts.links) and not next(guildDB.notes) then return end
        -- Random delay 2-15 seconds
        local reqReceivedAt = time()
        C_Timer.After(math.random(2, 15), function()
            -- If someone else responded since the REQ, skip
            if RF._lastSyncDataReceived and RF._lastSyncDataReceived > reqReceivedAt then
                return
            end
            RF:SendFullSync()
        end)

    elseif msgType == "VER" then
        -- Track sync users
        RF._syncUsers[sender] = {
            version = payload,
            lastSeen = time(),
        }
        RF:FireCallbacks("SYNC_STATUS_CHANGED")
        if RF.charDB and RF.charDB.debugMode then
            RF:Print(format("Sync: %s running RosterForge v%s", sender, payload))
        end
    end
end

-------------------------------------------------------------------------------
-- Hook into addon message event
-------------------------------------------------------------------------------
local msgFrame = CreateFrame("Frame")
msgFrame:RegisterEvent("CHAT_MSG_ADDON")
msgFrame:SetScript("OnEvent", function(self, event, ...)
    if event == "CHAT_MSG_ADDON" then
        OnAddonMessage(event, ...)
    end
end)

-------------------------------------------------------------------------------
-- Hook into existing functions to auto-broadcast changes
-------------------------------------------------------------------------------

-- Wrap AltManager functions to broadcast
local origLinkAlt = RF.LinkAlt
local origUnlinkAlt = RF.UnlinkAlt
local origSetMain = RF.SetMain
local origAddBan = RF.AddBan
local origRemoveBan = RF.RemoveBan

-- Flag to prevent re-broadcast when receiving synced data
RF._isSyncing = false

function RF:LinkAlt(alt, main)
    local result = origLinkAlt(self, alt, main)
    if result and not RF._isSyncing then
        RF:BroadcastAltLink(alt, main)
    end
    return result
end

function RF:UnlinkAlt(alt)
    local result = origUnlinkAlt(self, alt)
    if result and not RF._isSyncing then
        RF:BroadcastAltUnlink(alt)
    end
    return result
end

function RF:SetMain(name)
    local result = origSetMain(self, name)
    -- SetMain internally calls UnlinkAlt if needed, which broadcasts
    return result
end

function RF:AddBan(name, reason)
    origAddBan(self, name, reason)
    if not RF._isSyncing then
        RF:BroadcastBan(name, reason)
    end
end

function RF:RemoveBan(name)
    origRemoveBan(self, name)
    if not RF._isSyncing then
        RF:BroadcastUnban(name)
    end
end

-- Wrap note functions to broadcast
local origSetExtendedNote = RF.SetExtendedNote
local origDeleteExtendedNote = RF.DeleteExtendedNote

function RF:SetExtendedNote(name, text)
    local result = origSetExtendedNote(self, name, text)
    if result and not RF._isSyncing then
        if text and text:trim() ~= "" then
            RF:BroadcastNote(name)
        else
            RF:BroadcastNoteDelete(name)
        end
    end
    return result
end

function RF:DeleteExtendedNote(name)
    local result = origDeleteExtendedNote(self, name)
    if result and not RF._isSyncing then
        RF:BroadcastNoteDelete(name)
    end
    return result
end

-- Wrap team functions to broadcast
local origCreateTeam = RF.CreateTeam
local origDeleteTeam = RF.DeleteTeam
local origRenameTeam = RF.RenameTeam
local origAddTeamMember = RF.AddTeamMember
local origRemoveTeamMember = RF.RemoveTeamMember
local origSetTeamMemberRole = RF.SetTeamMemberRole

function RF:CreateTeam(name, teamType)
    local teamID = origCreateTeam(self, name, teamType)
    if teamID and not RF._isSyncing then
        RF:BroadcastTeamCreate(teamID)
    end
    return teamID
end

function RF:DeleteTeam(teamID)
    origDeleteTeam(self, teamID)
    if not RF._isSyncing then
        RF:BroadcastTeamDelete(teamID)
    end
end

function RF:RenameTeam(teamID, newName)
    origRenameTeam(self, teamID, newName)
    if not RF._isSyncing then
        RF:BroadcastTeamCreate(teamID)
    end
end

local origSetTeamOfficerOnly = RF.SetTeamOfficerOnly
function RF:SetTeamOfficerOnly(teamID, value)
    origSetTeamOfficerOnly(self, teamID, value)
    if not RF._isSyncing then
        RF:BroadcastTeamCreate(teamID)
    end
end

function RF:AddTeamMember(teamID, memberName, role)
    local result = origAddTeamMember(self, teamID, memberName, role)
    if result and not RF._isSyncing then
        RF:BroadcastTeamMember(teamID, memberName)
    end
    return result
end

function RF:RemoveTeamMember(teamID, memberName)
    local result = origRemoveTeamMember(self, teamID, memberName)
    if result and not RF._isSyncing then
        RF:BroadcastTeamMemberRemove(teamID, memberName)
    end
    return result
end

function RF:SetTeamMemberRole(teamID, memberName, role)
    local result = origSetTeamMemberRole(self, teamID, memberName, role)
    if result and not RF._isSyncing then
        RF:BroadcastTeamMember(teamID, memberName)
    end
    return result
end

-- Wrap tag functions to broadcast (deferred until all files are loaded)
local tagWrapped = false
local function WrapTagFunctions()
    if tagWrapped then return end
    if not RF.AddTag or not RF.RemoveTag then return end
    tagWrapped = true

    local origAddTag = RF.AddTag
    local origRemoveTag = RF.RemoveTag

    function RF:AddTag(name, label, color)
        local result = origAddTag(self, name, label, color)
        if result and not RF._isSyncing then
            local guildDB = RF:GetGuildDB()
            local storedColor = color
            if guildDB and guildDB.tags and guildDB.tags[name] then
                for _, t in ipairs(guildDB.tags[name]) do
                    if t.label == label then storedColor = t.color; break end
                end
            end
            RF:BroadcastTag(name, label, storedColor)
        end
        return result
    end

    function RF:RemoveTag(name, label)
        local result = origRemoveTag(self, name, label)
        if result and not RF._isSyncing then
            RF:BroadcastUntag(name, label)
        end
        return result
    end
end

RF:RegisterCallback("ADDON_LOADED", function() WrapTagFunctions() end)

-------------------------------------------------------------------------------
-- Wrap the receive handler to set sync flag
-------------------------------------------------------------------------------
local origOnAddonMessage = OnAddonMessage
OnAddonMessage = function(event, prefix, message, channel, sender)
    RF._isSyncing = true
    origOnAddonMessage(event, prefix, message, channel, sender)
    RF._isSyncing = false
end

-------------------------------------------------------------------------------
-- Auto-sync on login: request data and broadcast join dates
-------------------------------------------------------------------------------
RF:RegisterCallback("PLAYER_ENTERING_WORLD", function()
    C_Timer.After(10, function()
        if IsInGuild() then
            RF:BroadcastVersion()
            RF:RequestFullSync()
        end
    end)
end)

-- Broadcast new join dates as they're detected
RF:RegisterCallback("ROSTER_REFRESHED", function()
    -- We only broadcast join dates for newly detected members
    -- (handled by TrackingEngine creating new entries)
end)
