local addonName, RF = ...

-------------------------------------------------------------------------------
-- TrackingEngine: Join dates, activity, ban list, rank changes
-- All data stored per-guild via RF:GetGuildDB()
-------------------------------------------------------------------------------

local previousRoster = {} -- snapshot of names from last refresh

-------------------------------------------------------------------------------
-- Snapshot Comparison: Detect joins, leaves, rank changes
-------------------------------------------------------------------------------
local function CompareSnapshots()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    local tracking = guildDB.tracking
    local currentNames = {}
    local now = time()

    -- Build current roster snapshot
    for _, entry in ipairs(RF.rosterData) do
        local name = entry.name
        currentNames[name] = true

        -- Initialize member tracking if new
        if not tracking.members[name] then
            tracking.members[name] = {
                joinDate = now,
                lastSeen = now,
                rankHistory = {},
                activity = {},
            }
            -- Log the join
            local shortName = Ambiguate(name, "guild")
            RF:AddLog("JOIN", shortName .. " joined the guild")

            -- Rejoin detection
            if RF.charDB and RF.charDB.rejoinDetection then
                if tracking.banList[name] then
                    RF:Print("|cFFFF4444WARNING:|r Banned player " .. name .. " has rejoined the guild!")
                    RF:AddLog("BAN_REJOIN", shortName .. " (banned) rejoined the guild")
                end
            end
        else
            -- Update last seen
            if entry.isOnline then
                local memberData = tracking.members[name]
                memberData.lastSeen = now

                -- Activity tracking (cap per member)
                if not memberData.activity then
                    memberData.activity = {}
                end
                -- Only log once per session (check last entry)
                local lastActivity = memberData.activity[#memberData.activity]
                if not lastActivity or (now - lastActivity.time) > 3600 then
                    tinsert(memberData.activity, { time = now, type = "login" })
                    -- Cap activity entries
                    while #memberData.activity > RF.MAX_ACTIVITY_PER_MEMBER do
                        tremove(memberData.activity, 1)
                    end
                end
            end

            -- Rank change detection
            local memberData = tracking.members[name]
            if memberData.lastRankIndex and memberData.lastRankIndex ~= entry.rankIndex then
                local change = {
                    time = now,
                    from = memberData.lastRankIndex,
                    fromName = memberData.lastRankName or "?",
                    to = entry.rankIndex,
                    toName = entry.rankName,
                }
                if not memberData.rankHistory then
                    memberData.rankHistory = {}
                end
                tinsert(memberData.rankHistory, change)
                -- Cap rank history
                while #memberData.rankHistory > RF.MAX_RANK_HISTORY do
                    tremove(memberData.rankHistory, 1)
                end

                local direction = change.to < change.from and "promoted" or "demoted"
                local shortName = Ambiguate(name, "guild")
                RF:AddLog("RANK", shortName .. " " .. direction .. " from " .. change.fromName .. " to " .. change.toName)
            end
            memberData.lastRankIndex = entry.rankIndex
            memberData.lastRankName = entry.rankName
        end
    end

    -- Detect leaves (members in previous but not current)
    for name in pairs(previousRoster) do
        if not currentNames[name] then
            local shortName = Ambiguate(name, "guild")
            RF:AddLog("LEAVE", shortName .. " left or was removed from the guild")
            -- Don't delete tracking data — keep for history
            if tracking.members[name] then
                tracking.members[name].leftDate = now
            end
        end
    end

    -- Update previous snapshot
    previousRoster = currentNames
end

-------------------------------------------------------------------------------
-- Log System
-------------------------------------------------------------------------------
function RF:AddLog(logType, message)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    -- Safety: strip any repeated realm suffix (e.g. "Name-Realm-Realm-Realm" → "Name-Realm")
    local word = message:match("^(%S+)")
    if word then
        local parts = {}
        for part in word:gmatch("[^%-]+") do
            tinsert(parts, part)
        end
        if #parts > 2 then
            local realm = parts[2]
            local dupeCount = 0
            for i = 3, #parts do
                if parts[i] == realm or realm:sub(1, #parts[i]) == parts[i] then
                    dupeCount = dupeCount + 1
                else
                    break
                end
            end
            if dupeCount > 0 then
                local cleanName = parts[1] .. "-" .. realm
                local escaped = word:gsub("([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
                message = message:gsub(escaped, cleanName)
            end
        end
    end

    tinsert(guildDB.tracking.log, {
        time = time(),
        type = logType,
        msg = message,
    })

    -- Rotate logs if too large
    while #guildDB.tracking.log > (RF.charDB and RF.charDB.maxHistoryEntries or 500) do
        tremove(guildDB.tracking.log, 1)
    end
end

-------------------------------------------------------------------------------
-- Log Rotation: Remove entries older than configured days
-------------------------------------------------------------------------------
function RF:RotateLogs()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    local maxAge = (RF.charDB and RF.charDB.logRotationDays or RF.LOG_ROTATION_DAYS) * 86400
    local cutoff = time() - maxAge
    local log = guildDB.tracking.log
    local newLog = {}

    for _, entry in ipairs(log) do
        if entry.time >= cutoff then
            tinsert(newLog, entry)
        end
    end

    guildDB.tracking.log = newLog
end

-------------------------------------------------------------------------------
-- Ban List
-------------------------------------------------------------------------------
function RF:AddBan(name, reason)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end

    guildDB.tracking.banList[name] = {
        time = time(),
        reason = reason or "",
        addedBy = UnitName("player"),
    }
    RF:AddLog("BAN", Ambiguate(name, "guild") .. " added to ban list" .. (reason and (": " .. reason) or ""))
    RF:Print(name .. " added to ban list.")
end

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

    guildDB.tracking.banList[name] = nil
    RF:AddLog("UNBAN", Ambiguate(name, "guild") .. " removed from ban list")
    RF:Print(name .. " removed from ban list.")
end

function RF:IsBanned(name)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return false end
    return guildDB.tracking.banList[name] ~= nil
end

function RF:GetBanList()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return {} end
    return guildDB.tracking.banList
end

-------------------------------------------------------------------------------
-- Member Data Access
-------------------------------------------------------------------------------
function RF:GetMemberTracking(name)
    local guildDB = RF:GetGuildDB()
    if not guildDB then return nil end
    return guildDB.tracking.members[name]
end

function RF:GetJoinDate(name)
    local data = RF:GetMemberTracking(name)
    if data and data.joinDate then
        return data.joinDate
    end
    return nil
end

function RF:GetRankHistory(name)
    local data = RF:GetMemberTracking(name)
    if data and data.rankHistory then
        return data.rankHistory
    end
    return {}
end

function RF:GetActivityLog(name)
    local data = RF:GetMemberTracking(name)
    if data and data.activity then
        return data.activity
    end
    return {}
end

function RF:GetGuildLog()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return {} end
    return guildDB.tracking.log
end

-------------------------------------------------------------------------------
-- Session Snapshot: Diff current roster against last session
-------------------------------------------------------------------------------
function RF:ComputeSessionDiff()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return nil end
    if not RF.rosterData or #RF.rosterData == 0 then return nil end

    local lastSnapshot = guildDB.lastSessionRoster
    if not lastSnapshot or not next(lastSnapshot) then return nil end

    local joined = {}
    local left = {}

    -- Current names
    local currentNames = {}
    for _, entry in ipairs(RF.rosterData) do
        currentNames[entry.name] = true
        if not lastSnapshot[entry.name] then
            tinsert(joined, entry.name)
        end
    end

    -- Check who left
    for name in pairs(lastSnapshot) do
        if not currentNames[name] then
            tinsert(left, name)
        end
    end

    if #joined == 0 and #left == 0 then return nil end
    return { joined = joined, left = left }
end

function RF:SaveSessionSnapshot()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end
    if not RF.rosterData or #RF.rosterData == 0 then return end

    local snapshot = {}
    for _, entry in ipairs(RF.rosterData) do
        snapshot[entry.name] = true
    end
    guildDB.lastSessionRoster = snapshot
end

-- Save snapshot on logout
local logoutFrame = CreateFrame("Frame")
logoutFrame:RegisterEvent("PLAYER_LOGOUT")
logoutFrame:SetScript("OnEvent", function()
    RF:SaveSessionSnapshot()
end)

-------------------------------------------------------------------------------
-- Hook into roster refresh
-------------------------------------------------------------------------------
RF:RegisterCallback("ROSTER_REFRESHED", function()
    CompareSnapshots()
end)

-- Clean up log entries with repeated realm names (migration v3)
local function CleanRepeatedRealms()
    local guildDB = RF:GetGuildDB()
    if not guildDB then return end
    if guildDB._logRealmCleanV3 then return end

    local log = guildDB.tracking.log
    for _, entry in ipairs(log) do
        if entry.msg then
            local word = entry.msg:match("^(%S+)")
            if word then
                local parts = {}
                for part in word:gmatch("[^%-]+") do
                    tinsert(parts, part)
                end
                if #parts > 2 then
                    local realm = parts[2]
                    local dupeCount = 0
                    for i = 3, #parts do
                        if parts[i] == realm or realm:sub(1, #parts[i]) == parts[i] then
                            dupeCount = dupeCount + 1
                        else
                            break
                        end
                    end
                    if dupeCount > 0 then
                        local cleanName = parts[1] .. "-" .. realm
                        local escaped = word:gsub("([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
                        entry.msg = entry.msg:gsub(escaped, cleanName)
                    end
                end
            end
        end
    end

    -- Also clean corrupted keys in tracking.members
    local members = guildDB.tracking.members
    local toClean = {}
    for name, data in pairs(members) do
        local mparts = {}
        for part in name:gmatch("[^%-]+") do
            tinsert(mparts, part)
        end
        if #mparts > 2 then
            local mrealm = mparts[2]
            local mdupes = 0
            for i = 3, #mparts do
                if mparts[i] == mrealm or mrealm:sub(1, #mparts[i]) == mparts[i] then
                    mdupes = mdupes + 1
                else
                    break
                end
            end
            if mdupes > 0 then
                toClean[name] = mparts[1] .. "-" .. mrealm
            end
        end
    end
    for oldName, cleanName in pairs(toClean) do
        if not members[cleanName] then
            members[cleanName] = members[oldName]
        end
        members[oldName] = nil
    end

    guildDB._logRealmCleanV3 = true
end

-- Log rotation on login
RF:RegisterCallback("PLAYER_ENTERING_WORLD", function()
    C_Timer.After(5, function()
        RF:RotateLogs()
        CleanRepeatedRealms()
    end)
end)
