-- Timers.lua - Revised to ensure timers properly update their start point, and persistent data is clean for reload
BBTIMERS = BBTIMERS or {}

local ADDON_PREFIX = "RUFFSYNC"
C_ChatInfo.RegisterAddonMessagePrefix(ADDON_PREFIX)

local TimerListFrame = CreateFrame("Frame", "TimerListFrame", TimerFrame, "BackdropTemplate")
TimerListFrame:SetPoint("TOPLEFT", TimerFrame, "TOPLEFT", 5, -30)
TimerListFrame:SetPoint("BOTTOMRIGHT", TimerFrame, "BOTTOMRIGHT", -10, 20)

local scrollFrame = CreateFrame("ScrollFrame", "TimerScrollFrame", TimerListFrame, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", TimerListFrame, "TOPLEFT", 0, -3)
scrollFrame:SetPoint("BOTTOMRIGHT", TimerListFrame, "BOTTOMRIGHT", -20, 0)

local scrollChild = CreateFrame("Frame", nil, scrollFrame)
scrollChild:SetSize(1, 1)
scrollFrame:SetScrollChild(scrollChild)

local zoneAnnouncementMessages = {
    ["Azj-Kahet"]         = "!Azj-Kahet crate incoming!",
    ["Siren Isle"]        = "!Siren Isle crate incoming!",
    ["Isle of Dorn"]      = "!Isle of Dorn crate incoming!",
    ["The Ringing Deeps"] = "!Ringing Deeps crate incoming!",
    ["Hallowfall"]        = "!Hallowfall crate incoming!",
    ["Undermine"]         = "!Undermine crate incoming!",
}

local zoneNameRemap = {
    ["City of Threads"] = "Azj-Kahet",
    ["City of Threads - Lower"] = "Azj-Kahet",
    ["Slam Central Station"] = "Undermine",
    ["Blood Tower"] = "Azj-Kahet" -- Added remap for Blood Tower
}

local zoneColors = {
    ["Azj-Kahet"] = "|cffA330C9",
    ["Siren Isle"] = "|cffC79C6E",
    ["Isle of Dorn"] = "|cff40C7EB",
    ["The Ringing Deeps"] = "|cffC41F3B",
    ["Hallowfall"] = "|cff00FF96",
    ["Undermine"] = "|cffffa500",
}

local timers = {}
BlackBoxDB = BlackBoxDB or {}

-- Move debug mode function up here so it exists when we call it
local function IsDebugMode()
    return BlackBoxDB and BlackBoxDB.settings and BlackBoxDB.settings.debugMode
end

-- This function moves matching shard timer to the top of the list
local function MoveMatchingTimerToTop(shardID)
    for i, frame in ipairs(timers) do
        if frame.shardID == shardID then
            if i == 1 then return end
            table.remove(timers, i)
            table.insert(timers, 1, frame)
            UpdateTimerPositions()
            if IsDebugMode() then
                print("Debug: Moved timer with ShardID", shardID, "to top due to hover match.")
            end
            return
        end
    end
end


local function IsDebugMode()
    return BlackBoxDB and BlackBoxDB.settings and BlackBoxDB.settings.debugMode
end

local function FormatTime(seconds)
    local mins = math.floor(seconds / 60)
    local secs = math.floor(seconds % 60)
    return string.format("%02d:%02d", mins, secs)
end

local function UpdateTimerDisplay(timerFrame)
    local now = time()
    local cycles = math.floor((now - timerFrame.originalTimestamp) / timerFrame.duration)
    local cycleStart = timerFrame.originalTimestamp + cycles * timerFrame.duration
    local remaining = timerFrame.duration - (now - cycleStart)
    timerFrame.remainingSeconds = remaining

    local displayName = timerFrame.zoneName
    if displayName == "The Ringing Deeps" then
        displayName = "Ringing Deeps"
    end

    local colorCode = zoneColors[timerFrame.zoneName] or "|cffffffff"
    local coloredName = colorCode .. displayName .. "|r"

    local timeColor = remaining < 180 and "|cffff0000" or "|cffffffff"
    local formattedTime = timeColor .. FormatTime(remaining) .. "|r"

    timerFrame.text:SetText(string.format("%-25s", coloredName))
    timerFrame.time:SetText(string.format("%6s", formattedTime))
    timerFrame.shard:SetText(string.format("%6s", timerFrame.shardID))
end

local function RemoveTimerCompletely(timerFrame)
    timerFrame:SetScript("OnUpdate", nil)
    timerFrame:Hide()

    for i, frame in ipairs(timers) do
        if frame == timerFrame then
            table.remove(timers, i)
            break
        end
    end

    -- Remove from persistent storage
    for i = #BlackBoxDB, 1, -1 do
        local entry = BlackBoxDB[i]
        if entry.zone == timerFrame.zoneName and entry.shardID == timerFrame.shardID then
            table.remove(BlackBoxDB, i)
        end
    end

    UpdateTimerPositions()
end

local function MoveTimer(timerFrame, direction)
    local index
    for i, frame in ipairs(timers) do
        if frame == timerFrame then
            index = i
            break
        end
    end
    if not index then
        return
    end

    local targetIndex = (index + direction - 1) % #timers + 1
    local movedTimer = table.remove(timers, index)
    table.insert(timers, targetIndex, movedTimer)
    UpdateTimerPositions()
end

-- Helper function to update (or add) a persistent entry for a timer
local function UpdatePersistentEntry(zoneName, shardID, duration, timestamp)
    local key = zoneName .. "|" .. shardID
    local found = false
    for i = #BlackBoxDB, 1, -1 do
        local entry = BlackBoxDB[i]
        if entry.zone == zoneName and entry.shardID == shardID then
            if not found then
                entry.duration = duration
                entry.timestamp = timestamp
                found = true
            else
                table.remove(BlackBoxDB, i)
            end
        end
    end
    if not found then
        table.insert(BlackBoxDB, {
            zone = zoneName,
            shardID = shardID,
            duration = duration,
            timestamp = timestamp
        })
    end
end

local function CreateOrResetTimer(zoneName, shardID, duration, originalTimestamp, source)
    -- Remap the zone name if it exists in the remap table
    zoneName = zoneNameRemap[zoneName] or zoneName

    -- List of allowed zone names
    local allowedZones = {
        ["Azj-Kahet"] = true,
        ["Siren Isle"] = true,
        ["Isle of Dorn"] = true,
        ["The Ringing Deeps"] = true,
        ["Hallowfall"] = true,
        ["Undermine"] = true,
    }

    -- Validate the zone name
    if not allowedZones[zoneName] then
        if IsDebugMode() then
            print("Debug: Timer not started for invalid or unmapped zone:", zoneName)
        end
        return
    end

    duration = duration or 1097
    -- If no timestamp provided (manual restart), use current time.
    originalTimestamp = originalTimestamp or time()

    -- Look for an existing timer with the matching zone and shard.
    for i, frame in ipairs(timers) do
        if frame.shardID == shardID and frame.zoneName == zoneName then
            frame.originalTimestamp = originalTimestamp
            frame.duration = duration
            UpdateTimerDisplay(frame)
            UpdatePersistentEntry(zoneName, shardID, duration, originalTimestamp)

            -- Reorder the timer if not rebuilding from persistent data.
            if source ~= "PLAYER_LOGIN" and source ~= "GUILD" and source ~= "CHAT_MSG_ADDON" then
                table.remove(timers, i)
                table.insert(timers, 1, frame)
                UpdateTimerPositions()
            end
            return
        end
    end

    -- Create a new timer frame.
    local timerFrame = CreateFrame("Frame", nil, UIParent)

    -- Frame setup
    timerFrame:SetSize(320, 20)
    timerFrame:SetParent(scrollChild)
    timerFrame:SetClampedToScreen(true)

    -- Allow movement
    timerFrame:SetMovable(true)
    timerFrame:EnableMouse(true)
    timerFrame:RegisterForDrag("LeftButton")

    -- Drag highlight placeholder
    if not highlightFrame then
        highlightFrame = CreateFrame("Frame", nil, scrollChild, "BackdropTemplate")
        highlightFrame:SetSize(320, 20)
        highlightFrame:SetBackdrop({
            bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = true, tileSize = 16, edgeSize = 16,
            insets = { left = 4, right = 4, top = 4, bottom = 4 }
        })
        highlightFrame:SetBackdropColor(1, 1, 0, 0.3)
        highlightFrame:SetBackdropBorderColor(1, 1, 0, 1)
        highlightFrame:Hide()
    end

    local function GetDropIndex(cursorY)
        local scrollY = select(2, scrollChild:GetCenter())
        local relativeY = scrollY - cursorY
        local index = math.floor(relativeY / 25) + 1
        return math.max(1, math.min(#timers + 1, index))
    end

    -- Drag start
    timerFrame:SetScript("OnDragStart", function(self)
        self:StartMoving()
        self:SetFrameStrata("TOOLTIP")
        highlightFrame:Show()
        highlightFrame:SetFrameStrata("BACKGROUND")

        self:SetScript("OnUpdate", function()
            local _, y = GetCursorPosition()
            local scale = UIParent:GetEffectiveScale()
            y = y / scale
            local newIndex = GetDropIndex(y)
            highlightFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -25 * (newIndex - 1))
        end)
    end)

    -- Drag stop
    timerFrame:SetScript("OnDragStop", function(self)
        self:StopMovingOrSizing()
        self:SetFrameStrata("MEDIUM")
        self:SetScript("OnUpdate", nil)

        local _, y = GetCursorPosition()
        local scale = UIParent:GetEffectiveScale()
        y = y / scale
        local newIndex = GetDropIndex(y)

        for i, frame in ipairs(timers) do
            if frame == self then
                table.remove(timers, i)
                break
            end
        end

        table.insert(timers, newIndex, self)
        highlightFrame:Hide()
        UpdateTimerPositions()
    end)

    local timerText = timerFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    timerText:SetJustifyH("LEFT")
    timerText:SetPoint("LEFT", 0, 0)
    timerFrame.text = timerText

    local cancelButton = CreateFrame("Button", nil, timerFrame, "UIPanelButtonTemplate")
    cancelButton:SetSize(20, 20)
    cancelButton:SetPoint("RIGHT", timerFrame, "RIGHT", -100, 0)
    cancelButton:SetText("X")
    cancelButton:SetScript("OnClick", function()
        RemoveTimerCompletely(timerFrame)
        if IsDebugMode() then
            print("Debug: Timer deleted forever: " .. timerFrame.zoneName .. " (" .. timerFrame.shardID .. ")")
        end
    end)
    cancelButton:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText("Delete")
        GameTooltip:Show()
    end)
    cancelButton:SetScript("OnLeave", function() GameTooltip:Hide() end)

    local upButton = CreateFrame("Button", nil, timerFrame, "UIPanelButtonTemplate")
    upButton:SetSize(20, 20)
    upButton:SetPoint("LEFT", cancelButton, "RIGHT", 5, 0)
    upButton:SetText("^")
    upButton:SetScript("OnClick", function()
        MoveTimer(timerFrame, -1)
    end)

    local downButton = CreateFrame("Button", nil, timerFrame, "UIPanelButtonTemplate")
    downButton:SetSize(20, 20)
    downButton:SetPoint("LEFT", upButton, "RIGHT", 5, 0)
    downButton:SetText("v")
    downButton:SetScript("OnClick", function()
        MoveTimer(timerFrame, 1)
    end)

    local timerTime = timerFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    timerTime:SetJustifyH("RIGHT")
    timerTime:SetPoint("RIGHT", cancelButton, "LEFT", -5, 0)
    timerFrame.time = timerTime

    local timerShard = timerFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    timerShard:SetJustifyH("RIGHT")
    timerShard:SetPoint("RIGHT", timerFrame, "RIGHT", -170, 0)
    timerFrame.shard = timerShard

    timerText:SetScript("OnEnter", function(self)
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        GameTooltip:SetText("Right click to share")
        GameTooltip:Show()
    end)
    timerText:SetScript("OnLeave", function()
        GameTooltip:Hide()
    end)
    timerText:SetScript("OnMouseDown", function(self, button)
        if button == "RightButton" then
            if not self.menuFrame then
                local menuFrame = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
                menuFrame:SetSize(60, 30)
                menuFrame:SetBackdrop({
                    bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
                    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                    tile = true, tileSize = 16, edgeSize = 16,
                    insets = { left = 4, right = 4, top = 4, bottom = 4 }
                })
                menuFrame:SetBackdropColor(0, 0, 0, 1)
                menuFrame:SetBackdropBorderColor(1, 0.84, 0, 1)
                menuFrame:SetFrameStrata("TOOLTIP")

                local shareButton = CreateFrame("Button", nil, menuFrame)
                shareButton:SetSize(50, 20)
                shareButton:SetPoint("CENTER", menuFrame, "CENTER", 0, 0)
                shareButton:SetText("Share")
                shareButton:SetNormalFontObject("GameFontNormal")
                shareButton:SetHighlightFontObject("GameFontHighlight")
                shareButton:SetScript("OnClick", function()
                    local remaining = math.floor(timerFrame.remainingSeconds or 0)
                    if remaining <= 0 then
                        print("Cannot share a timer with no time remaining.")
                        return
                    end

                    local function SendTimerShare()
                        local message = string.format("%s;%s;%d", timerFrame.zoneName, timerFrame.shardID, remaining)
                        local channel = IsInRaid() and "RAID" or (IsInGroup() and "PARTY") or (IsInGuild() and "GUILD") or nil
                        if not channel then
                            print("You're not in a group. Shouting into the void isn't supported.")
                            return
                        end
                        C_ChatInfo.SendAddonMessage(ADDON_PREFIX, message, channel)
                        if IsDebugMode() then
                            print("Shared timer:", message)
                        end
                        if menuFrame.shareConfirmFrame then
                            menuFrame.shareConfirmFrame:Hide()
                        end
                        menuFrame:Hide()
                    end

                    if BlackBoxDB.settings and BlackBoxDB.settings.ignoreConfirm then
                        SendTimerShare()
                    else
                        if not menuFrame.shareConfirmFrame then
                            local confirm = CreateFrame("Frame", nil, menuFrame, "BackdropTemplate")
                            confirm:SetSize(200, 80)
                            confirm:SetPoint("BOTTOMLEFT", shareButton, "TOPLEFT", 0, 5)
                            confirm:SetBackdrop({
                                bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
                                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                                tile = true, tileSize = 16, edgeSize = 16,
                                insets = { left = 4, right = 4, top = 4, bottom = 4 }
                            })
                            confirm:SetBackdropColor(0, 0, 0, 1)
                            confirm:SetBackdropBorderColor(1, 1, 0, 1)

                            local confirmText = confirm:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                            confirmText:SetPoint("TOPLEFT", confirm, "TOPLEFT", 10, -10)
                            confirmText:SetWidth(180)
                            confirmText:SetJustifyH("LEFT")
                            confirmText:SetText("Are you sure this Timer is Accurate?")

                            local sendBtn = CreateFrame("Button", nil, confirm, "UIPanelButtonTemplate")
                            sendBtn:SetSize(60, 20)
                            sendBtn:SetPoint("BOTTOMLEFT", confirm, "BOTTOMLEFT", 10, 10)
                            sendBtn:SetText("Send")
                            sendBtn:SetScript("OnClick", function()
                                SendTimerShare()
                            end)

                            local cancelBtn = CreateFrame("Button", nil, confirm, "UIPanelButtonTemplate")
                            cancelBtn:SetSize(60, 20)
                            cancelBtn:SetPoint("BOTTOMRIGHT", confirm, "BOTTOMRIGHT", -10, 10)
                            cancelBtn:SetText("Cancel")
                            cancelBtn:SetScript("OnClick", function()
                                confirm:Hide()
                            end)

                            local ignoreCheck = CreateFrame("CheckButton", nil, confirm, "UICheckButtonTemplate")
                            ignoreCheck:SetSize(20, 20)
                            ignoreCheck:SetPoint("TOP", confirmText, "BOTTOM", -85, 2)
                            ignoreCheck.text:SetText("Ignore this message next time")
                            ignoreCheck.text:SetPoint("LEFT", ignoreCheck, "RIGHT", 5, 0)
                            ignoreCheck:SetScript("OnClick", function(self)
                                BlackBoxDB.settings = BlackBoxDB.settings or {}
                                BlackBoxDB.settings.ignoreConfirm = self:GetChecked()
                            end)

                            menuFrame.shareConfirmFrame = confirm
                        end
                        menuFrame.shareConfirmFrame:Show()
                    end
                end)
                -- Add tooltip to the "Share" button
                shareButton:SetScript("OnEnter", function(self)
                    GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
                    GameTooltip:SetText("Click to share this timer with your group and guild.\n|cff00ff00Shared to Group moves to top.|r\n|cffffffffShared to Guild moves to bottom.|r")
                    GameTooltip:Show()
                end)
                shareButton:SetScript("OnLeave", function()
                    GameTooltip:Hide()
                end)
                self.menuFrame = menuFrame
            end
            local x, y = GetCursorPosition()
            local scale = UIParent:GetEffectiveScale()
            self.menuFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", (x / scale) + 10, (y / scale) + 10)
            self.menuFrame:Show()
            C_Timer.After(3, function()
                if self.menuFrame and self.menuFrame:IsShown() then
                    self.menuFrame:Hide()
                end
            end)
        end
    end)

    timerFrame.zoneName = zoneName
    timerFrame.shardID = shardID
    timerFrame.duration = duration
    timerFrame.originalTimestamp = originalTimestamp
    timerFrame.startedFrom = source

    timerFrame:SetScript("OnUpdate", function(self)
        UpdateTimerDisplay(self)
    end)

    if source == "PLAYER_LOGIN" or source == "GUILD" then
        table.insert(timers, timerFrame)
    else
        table.insert(timers, 1, timerFrame)
    end

    UpdatePersistentEntry(zoneName, shardID, duration, originalTimestamp)
    UpdateTimerPositions()
end

function UpdateTimerPositions()
    local visibleIndex = 0
    local orderList = {}
    for _, timer in ipairs(timers) do
        visibleIndex = visibleIndex + 1
        timer:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -25 * (visibleIndex - 1))
        timer:Show()
        table.insert(orderList, timer.zoneName .. "|" .. timer.shardID)
    end
    BlackBoxDB.order = orderList
end

local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("PLAYER_LOGIN")
eventFrame:RegisterEvent("CHAT_MSG_ADDON")
eventFrame:RegisterEvent("CHAT_MSG_RAID")
eventFrame:RegisterEvent("CHAT_MSG_RAID_LEADER")
eventFrame:RegisterEvent("CHAT_MSG_PARTY")
eventFrame:RegisterEvent("CHAT_MSG_PARTY_LEADER")
eventFrame:RegisterEvent("CHAT_MSG_MONSTER_SAY")

eventFrame:SetScript("OnEvent", function(_, event, arg1, ...)
    if event == "PLAYER_LOGIN" then
        local tempTimers = {}
        for _, entry in ipairs(BlackBoxDB) do
            if type(entry) == "table" and entry.zone and entry.shardID then
                local key = entry.zone .. "|" .. entry.shardID
                tempTimers[key] = entry
            end
        end
        if BlackBoxDB.order then
            for _, key in ipairs(BlackBoxDB.order) do
                local data = tempTimers[key]
                if data then
                    CreateOrResetTimer(data.zone, data.shardID, data.duration, data.timestamp, "PLAYER_LOGIN")
                    tempTimers[key] = nil
                end
            end
        end
        for _, data in pairs(tempTimers) do
            CreateOrResetTimer(data.zone, data.shardID, data.duration, data.timestamp, "PLAYER_LOGIN")
        end

    elseif event == "CHAT_MSG_ADDON" then
        local prefix, msg, channel, sender = arg1, ...
        if prefix == ADDON_PREFIX then
            if msg == "TICKER_REQUEST" or msg:sub(1,13) == "TICKER_REPLY:" then
                return
            end
            if IsDebugMode() then
                print("Received Addon Message:", msg, "| Channel:", channel, "| Sender:", sender)
            end
            local zone, shard, timeRemaining = strsplit(";", msg)
            local fullDuration = 1097
            local remaining = tonumber(timeRemaining)
            local originalTimestamp = time() - (fullDuration - remaining)
            CreateOrResetTimer(zone, shard, fullDuration, originalTimestamp, channel)
            if IsDebugMode() then
                print("Debug: Received shared timer from " .. sender .. " for " .. zone .. " (" .. shard .. ") with " .. remaining .. " seconds remaining")
            end
        end

    elseif event:find("CHAT_MSG") then
        local msg = arg1
        for zoneName, pattern in pairs(zoneAnnouncementMessages) do
            if msg:lower():find(pattern:lower(), 1, true) then
                local shardID = msg:match("%((%d+)%)")
                if shardID then
                    CreateOrResetTimer(zoneName, shardID, nil, nil, event)
                end
                PlaySoundFile("Interface/AddOns/BlackBox/Crate.mp3", "Master")
            end
        end

        if event == "CHAT_MSG_MONSTER_SAY" then
            local text, npcName = arg1, select(5, ...)
            if npcName == "Ruffious" or
               text:find("Opportunity's knocking!") or
               text:find("I see some valuable resources") or
               text:find("treasure nearby") or
               text:find("cache of resources nearby") then
                local zone = zoneNameRemap[GetZoneText()] or GetZoneText()
                local shard = tostring(BlackBoxDB.shardID or "0")
                if shard:match("^%d+$") then
                    CreateOrResetTimer(zone, shard, nil, nil, "Ruffious")
                    if shard == "N/A" then
                        return -- Do not announce if shard is N/A
                    end

                    local announce = zoneAnnouncementMessages[zone]
                    if announce then
                        local channel = IsInRaid() and "RAID" or IsInGroup() and "PARTY" or nil
                        if channel then
                            SendChatMessage(announce .. " (" .. shard .. ")", channel)
                        end
                    end
                    PlaySoundFile("Interface/AddOns/BlackBox/Crate.mp3", "Master")
                end
            end
        end
    end
end)

SLASH_RUFFIOUS1 = "/rufftest"
SlashCmdList["RUFFIOUS"] = function()
    eventFrame:GetScript("OnEvent")(eventFrame, "CHAT_MSG_MONSTER_SAY", "treasure nearby", nil, nil, nil, "Ruffious")
end

SLASH_RUFFWIPE1 = "/ruffwipe"
SlashCmdList["RUFFWIPE"] = function()
    BlackBoxDB = {}
    for _, t in ipairs(timers) do
        t:SetScript("OnUpdate", nil)
        t:Hide()
    end
    timers = {}
    print("All saved timers wiped. Nothing remains. Not even regret.")
end

-- Expose functions and persistent data for external modules
BBTIMERS.CreateOrResetTimer = CreateOrResetTimer
BBTIMERS.UpdateTimerPositions = UpdateTimerPositions
BBTIMERS.RemoveTimerCompletely = RemoveTimerCompletely
BBTIMERS.IsDebugMode = IsDebugMode
BBTIMERS.BlackBoxDB = BlackBoxDB
BBTIMERS.MoveMatchingTimerToTop = MoveMatchingTimerToTop
_G["timers"] = timers
