local L = LibStub("AceLocale-3.0"):GetLocale("CritMatic")
local MESSAGE_SPACING = 3

-- The alert format strings are user-editable, so a stray or reordered format
-- specifier (e.g. "%d" before "%s", or a lone "%") would otherwise raise a Lua
-- error on every alert. Fall back to a plain "name amount" rendering instead.
local function SafeAlertFormat(fmt, spellName, amount)
    local ok, result = pcall(string.format, fmt, spellName, amount)
    if ok then
        return result
    end
    return tostring(spellName) .. " " .. tostring(amount)
end

local activeMessages = {}
--/run CritMatic.ShowNewCritMessage("Killing Spree", 300)CritMatic.ShowNewNormalMessage("Killing Spree",435)

-- Utility function to convert percentage to pixel offset
local function ConvertPercentageToOffset(xPercent, yPercent)
    local screenWidth = UIParent:GetWidth()
    local screenHeight = UIParent:GetHeight()
    
    -- Convert percentages to pixel offsets from center
    local xOffset = (xPercent - 50) * screenWidth / 100
    local yOffset = (yPercent - 50) * screenHeight / 100
    
    return xOffset, yOffset
end

-- Utility function to adjust message positions
local function AdjustMessagePositions()
    -- Get position settings from database
    local xPos = Critmatic.db.profile.alertNotificationFormat.position.xPos
    local yPos = Critmatic.db.profile.alertNotificationFormat.position.yPos
    
    -- Convert percentages to pixel offsets
    local xOffset, yOffset = ConvertPercentageToOffset(xPos, yPos)
    
    -- Position the first message using configurable values
    activeMessages[1]:ClearAllPoints()
    activeMessages[1]:SetPoint("CENTER", UIParent, "CENTER", xOffset, yOffset)

    -- Position subsequent messages relative to the previous one
    for i = 2, #activeMessages do
        activeMessages[i]:ClearAllPoints()
        activeMessages[i]:SetPoint("TOP", activeMessages[i - 1], "BOTTOM", 0, -MESSAGE_SPACING)
    end
end




-- Utility function to remove the oldest message and adjust the rest
local function RemoveOldestMessage()
    local oldestMessage = table.remove(activeMessages)  -- Remove the oldest message (first in the table)

    -- Hide the message frame and perform any necessary cleanup
    if oldestMessage then
        oldestMessage:Hide()

    end
end

Critmatic.MessageFrame = {}

function Critmatic.FormatPreviousRecord(oldAmount, newAmount)
    if not Critmatic.db.profile.generalSettings.showPreviousRecord then
        return ""
    end
    if not oldAmount or oldAmount <= 0 or not newAmount then
        return ""
    end
    return SafeAlertFormat(L["previous_record"], oldAmount, oldAmount)
end

function Critmatic.BuildRecordChatLine(label, spellName, value, prevText)
    local line = label .. spellName .. ": " .. value
    if prevText and prevText ~= "" then
        line = line .. " " .. prevText
    end
    return line
end

local ICON_SIZE = 24
local ICON_SPACING = 6

local function cloneTextStyle(f, justify)
    local fs = f:CreateFontString(nil, "ARTWORK")
    local fontPath, fontSize, fontFlags = f.text:GetFont()
    if fontPath then fs:SetFont(fontPath, fontSize, fontFlags) end
    fs:SetTextColor(f.text:GetTextColor())
    local sx, sy = f.text:GetShadowOffset()
    if sx then fs:SetShadowOffset(sx, sy) end
    fs:SetShadowColor(f.text:GetShadowColor())
    if fs.SetWordWrap then fs:SetWordWrap(false) end
    fs:SetJustifyH(justify or "LEFT")
    return fs
end

function Critmatic.MessageFrame:CreateMessage(text, r, g, b, spellIcon, amount, oldAmount, prevText)
    local MAX_MESSAGES = Critmatic.db.profile.alertNotificationFormat.global.maxMessages
    local delayInSeconds = 0.45
    local function delayedExecution()
        local f = Critmatic.CreateNewMessageFrame()

        f.text:SetTextColor(r, g, b)

        local hasPrev = type(prevText) == "string" and prevText ~= ""
        local _, lineH = f.text:GetFont()
        lineH = lineH or 20
        local gap = 2
        local y1 = hasPrev and (lineH + gap) / 2 or 0
        local y2 = -(lineH + gap) / 2

        local centerOffsetX = 0
        if spellIcon then
            if not f.icon then
                f.icon = f:CreateTexture(nil, "ARTWORK")
                f.icon:SetSize(ICON_SIZE, ICON_SIZE)
            end
            f.icon:SetTexture(spellIcon)
            f.icon:Show()
            local LMAShadow = LibStub("LibMatic-AlertShadow-1.0", true)
            if LMAShadow then
                LMAShadow:ApplyIconShadow(f.icon, Critmatic.db.profile.fontSettings.shadowStrength)
            end
            centerOffsetX = (ICON_SIZE + ICON_SPACING) / 2

            f.text:SetText(text)
            local textWidth = f.text:GetStringWidth() or 200
            local totalWidth = textWidth + ICON_SIZE + ICON_SPACING
            local iconOffsetX = -(totalWidth / 2) + (ICON_SIZE / 2)

            f.icon:ClearAllPoints()
            f.icon:SetPoint("CENTER", f, "CENTER", iconOffsetX, y1)

            if not f.iconBounce then
                f.iconBounce = f.icon:CreateAnimationGroup()
                local scaleUp = f.iconBounce:CreateAnimation("Scale")
                scaleUp:SetScale(1.5, 1.5)
                scaleUp:SetDuration(0.15)
                scaleUp:SetOrder(1)
                local pause = f.iconBounce:CreateAnimation("Pause")
                pause:SetDuration(0.12)
                pause:SetOrder(2)
                local scaleDown = f.iconBounce:CreateAnimation("Scale")
                scaleDown:SetScale(1 / 1.5, 1 / 1.5)
                scaleDown:SetDuration(0.15)
                scaleDown:SetOrder(3)
            end
            f.iconBounce:Play()
        elseif f.icon then
            f.icon:Hide()
        end

        f.text:ClearAllPoints()
        f.text:SetJustifyH("CENTER")
        f.text:SetPoint("CENTER", f, "CENTER", centerOffsetX, y1)
        f.text:SetText(text)

        if hasPrev then
            if not f.prevText then
                f.prevText = cloneTextStyle(f, "CENTER")
            end
            f.prevText:SetTextColor(r, g, b)
            f.prevText:ClearAllPoints()
            f.prevText:SetJustifyH("CENTER")
            f.prevText:SetPoint("CENTER", f, "CENTER", centerOffsetX, y2)
            f.prevText:SetText(prevText)
            f.prevText:Show()
        elseif f.prevText then
            f.prevText:Hide()
        end

        local mainH = f.text:GetStringHeight()
        if not mainH or mainH <= 0 then mainH = lineH end
        local topH = mainH
        if spellIcon and topH < ICON_SIZE then topH = ICON_SIZE end
        local contentH = topH
        if hasPrev then
            local prevH = f.prevText:GetStringHeight()
            if not prevH or prevH <= 0 then prevH = lineH end
            contentH = (lineH + gap) + (topH + prevH) / 2
            if contentH < topH then contentH = topH end
        end
        f:SetHeight(contentH)

        local cfg = Critmatic.db.profile.alertNotificationFormat.global
        local animated = false
        if cfg.countUpEnabled then
            local CountUp = LibStub("LibMatic-CountUp-1.0", true)
            if CountUp then
                f.numberCounter = CountUp:AttachLineCounter(f, f.text, {
                    lineText = text, amount = amount,
                    duration = cfg.countUpDuration, yOffset = y1, centerOffsetX = centerOffsetX,
                })
                animated = f.numberCounter ~= nil
                if hasPrev then
                    f.prevNumberCounter = CountUp:AttachLineCounter(f, f.prevText, {
                        lineText = prevText, amount = oldAmount,
                        duration = cfg.countUpDuration, yOffset = y2, centerOffsetX = centerOffsetX,
                    })
                end
            end
        end

        f.fadeOut = f:CreateAnimationGroup()
        local fade = f.fadeOut:CreateAnimation("Alpha")
        fade:SetFromAlpha(1)
        fade:SetToAlpha(0)
        fade:SetDuration(cfg.fadeTime)
        fade:SetStartDelay(animated and ((cfg.countUpDuration or 3) + (cfg.countUpHold or 3)) or cfg.startDelay)
        f.fadeOut:SetScript("OnFinished", function()
            f:Hide()
            if f.numberCounter then f.numberCounter:Stop() end
            if f.prevNumberCounter then f.prevNumberCounter:Stop() end
            -- Drop the faded frame from the local (non-shared) stack so later
            -- alerts don't anchor beneath an invisible ghost. No-op on the
            -- shared-stack path, where the frame isn't in activeMessages.
            for i = #activeMessages, 1, -1 do
                if activeMessages[i] == f then
                    table.remove(activeMessages, i)
                    break
                end
            end
            if #activeMessages > 0 then
                AdjustMessagePositions()
            end
        end)

        f.bounce:Play()
        f.fadeOut:Play()

        local stack = LibStub("LibMatic-AlertStack-1.0", true)
        local pos = Critmatic.db.profile.alertNotificationFormat.position
        if stack and pos.useSharedPosition ~= false then
            stack:Push("CritMatic", f, MAX_MESSAGES)
        else
            table.insert(activeMessages, 1, f)
            AdjustMessagePositions()

            if #activeMessages > MAX_MESSAGES then
                RemoveOldestMessage()
            end
        end

        return f
    end

    C_Timer.After(delayInSeconds, function()
        local LMGuard = LibStub("LibMatic-OverlayGuard-1.0", true)
        if LMGuard then
            LMGuard:RunWhenClear(delayedExecution)
        else
            delayedExecution()
        end
    end)
end
local message = ""
function Critmatic.ShowNewCritMessage(spellName, amount, spellIcon, oldAmount)
    message = SafeAlertFormat(Critmatic.db.profile.alertNotificationFormat.strings.critAlertNotificationFormat, spellName, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        message = string.upper(message)
    end

    local prevText = Critmatic.FormatPreviousRecord(oldAmount, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        prevText = string.upper(prevText)
    end

    local r, g, b = unpack(Critmatic.db.profile.fontSettings.fontColorCrit)
    Critmatic.MessageFrame:CreateMessage(message, r, g, b, spellIcon, amount, oldAmount, prevText)
end

function Critmatic.ShowNewNormalMessage(spellName, amount, spellIcon, oldAmount)
    message = SafeAlertFormat(Critmatic.db.profile.alertNotificationFormat.strings.hitAlertNotificationFormat, spellName, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        message = string.upper(message)
    end

    local prevText = Critmatic.FormatPreviousRecord(oldAmount, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        prevText = string.upper(prevText)
    end

    local r, g, b = unpack(Critmatic.db.profile.fontSettings.fontColor)
    Critmatic.MessageFrame:CreateMessage(message, r, g, b, spellIcon, amount, oldAmount, prevText)
end

function Critmatic.ShowNewHealCritMessage(spellName, amount, spellIcon, oldAmount)
    message = SafeAlertFormat(Critmatic.db.profile.alertNotificationFormat.strings.critHealAlertNotificationFormat, spellName, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        message = string.upper(message)
    end

    local prevText = Critmatic.FormatPreviousRecord(oldAmount, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        prevText = string.upper(prevText)
    end

    local r, g, b = unpack(Critmatic.db.profile.fontSettings.fontColorCritHeal)
    Critmatic.MessageFrame:CreateMessage(message, r, g, b, spellIcon, amount, oldAmount, prevText)
end

function Critmatic.ShowNewHealMessage(spellName, amount, spellIcon, oldAmount)
    -- Check if the spellName ends with 'Heal', 'heal', or 'HEAL'

    local formatString = Critmatic.db.profile.alertNotificationFormat.strings.healAlertNotificationFormat
    if string.sub(spellName, -4):lower() == "heal" then
        -- Remove the redundant 'Heal' from the format string
        formatString = string.gsub(formatString, "%%s Heal", "%%s")
        formatString = string.gsub(formatString, "%%s heal", "%%s")
        formatString = string.gsub(formatString, "%%s HEAL", "%%s")
    end
    message = SafeAlertFormat(formatString, spellName, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        message = string.upper(message)
    end

    local prevText = Critmatic.FormatPreviousRecord(oldAmount, amount)
    if Critmatic.db.profile.alertNotificationFormat.global.isUpper then
        prevText = string.upper(prevText)
    end

    local r, g, b = unpack(Critmatic.db.profile.fontSettings.fontColorHeal)
    Critmatic.MessageFrame:CreateMessage(message, r, g, b, spellIcon, amount, oldAmount, prevText)
end


