local BFH = _G.BlightfallHelper
if not BFH then return end

local display, bg, iconFrame, icon, status, barBG, spark, label, timer, iconTimer, dragOverlay

local function Clamp(v, lo, hi)
    v = tonumber(v) or lo
    if v < lo then return lo end
    if v > hi then return hi end
    return v
end

function BFH:InitializeDisplay()
    display = CreateFrame("Frame", "BlightfallHelperDisplay", UIParent)
    display:SetFrameStrata("HIGH")
    display:SetClampedToScreen(true)
    display:SetMovable(true)
    display:Hide()
    self.display = display

    bg = CreateFrame("Frame", nil, display, "BackdropTemplate")
    bg:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })

    -- Keep the spell icon on its own higher frame level so background opacity
    -- never darkens or hides it, even at 100%.
    iconFrame = CreateFrame("Frame", nil, display)
    iconFrame:SetFrameLevel(display:GetFrameLevel() + 5)

    icon = iconFrame:CreateTexture(nil, "OVERLAY")
    icon:SetAllPoints(iconFrame)
    icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)

    status = CreateFrame("StatusBar", nil, display)
    status:SetFrameLevel(display:GetFrameLevel() + 2)
    status:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")

    barBG = status:CreateTexture(nil, "BACKGROUND")
    barBG:SetAllPoints(status)
    barBG:SetTexture("Interface\\Buttons\\WHITE8X8")

    -- Tiny marker only; this replaces the oversized glow/spark.
    spark = status:CreateTexture(nil, "OVERLAY")
    spark:SetTexture("Interface\\Buttons\\WHITE8X8")
    spark:SetVertexColor(1, 1, 1, 0.40)
    spark:SetWidth(2)

    label = status:CreateFontString(nil, "OVERLAY")
    label:SetJustifyH("LEFT")

    timer = status:CreateFontString(nil, "OVERLAY")
    timer:SetJustifyH("RIGHT")

    iconTimer = display:CreateFontString(nil, "OVERLAY")
    iconTimer:SetJustifyH("CENTER")

    dragOverlay = CreateFrame("Frame", nil, display, "BackdropTemplate")
    dragOverlay:SetAllPoints(display)
    dragOverlay:SetFrameLevel(display:GetFrameLevel() + 20)
    dragOverlay:SetBackdrop({edgeFile = "Interface\\Buttons\\WHITE8X8", edgeSize = 2})
    dragOverlay:SetBackdropBorderColor(0.62, 0.11, 1, 1)
    dragOverlay:EnableMouse(true)
    dragOverlay:RegisterForDrag("LeftButton")
    dragOverlay:SetMovable(true)
    dragOverlay:Hide()

    dragOverlay:SetScript("OnDragStart", function()
        if not BFH.db.locked then display:StartMoving() end
    end)

    dragOverlay:SetScript("OnDragStop", function()
        display:StopMovingOrSizing()
        local p, _, rp, x, y = display:GetPoint(1)
        BFH.db.point, BFH.db.relativePoint, BFH.db.x, BFH.db.y = p, rp, x, y
    end)

    display:SetScript("OnUpdate", function() BFH:OnDisplayUpdate() end)
    self:ApplyDisplaySettings()
end

function BFH:ApplyPosition()
    if not display then return end
    display:ClearAllPoints()
    display:SetPoint(
        self.db.point or "CENTER",
        UIParent,
        self.db.relativePoint or "CENTER",
        self.db.x or 0,
        self.db.y or 0
    )
end

function BFH:ApplyDisplaySettings()
    if not display then return end
    local d = self.db

    display:SetScale(Clamp(d.scale, 0.5, 2.0))
    self:ApplyPosition()

    bg:ClearAllPoints()
    iconFrame:ClearAllPoints()
    status:ClearAllPoints()
    label:ClearAllPoints()
    timer:ClearAllPoints()
    iconTimer:ClearAllPoints()

    local alpha = Clamp(d.backgroundAlpha, 0, 1)
    bg:SetBackdropColor(0.018, 0.018, 0.022, alpha)
    bg:SetBackdropBorderColor(0, 0, 0, d.borderEnabled and 1 or 0)
    barBG:SetVertexColor(0.018, 0.018, 0.022, alpha)

    local font = d.font or "Fonts\\FRIZQT__.TTF"
    if not label:SetFont(font, Clamp(d.labelFontSize, 8, 32), "OUTLINE") then
        font = "Fonts\\FRIZQT__.TTF"
        d.font = font
        d.fontName = "Friz Quadrata"
        label:SetFont(font, Clamp(d.labelFontSize, 8, 32), "OUTLINE")
    end
    timer:SetFont(font, Clamp(d.timerFontSize, 8, 32), "OUTLINE")
    iconTimer:SetFont(font, Clamp(d.timerFontSize, 8, 32), "OUTLINE")

    if d.displayMode == "ICON" then
        local s = Clamp(d.iconOnlySize, 32, 128)
        display:SetSize(s, s + 30)

        bg:SetPoint("TOP", display, "TOP")
        bg:SetSize(s, s)

        iconFrame:SetPoint("TOP", display, "TOP")
        iconFrame:SetSize(s, s)
        iconFrame:Show()
        icon:Show()

        status:Hide()
        label:Hide()
        timer:Hide()

        iconTimer:SetPoint("TOP", iconFrame, "BOTTOM", 0, -4)
        iconTimer:Show()
    else
        local bw = Clamp(d.barWidth, 100, 500)
        local bh = Clamp(d.barHeight, 10, 60)
        local iw = d.showIcon and Clamp(d.iconSize, 10, 80) or 0

        display:SetSize(bw + iw, math.max(bh, iw))
        bg:SetAllPoints(display)

        status:SetSize(bw, bh)
        status:Show()

        if d.showIcon then
            iconFrame:SetSize(iw, iw)
            iconFrame:Show()
            icon:Show()

            if d.iconPosition == "RIGHT" then
                status:SetPoint("LEFT", display, "LEFT", 0, 0)
                iconFrame:SetPoint("LEFT", status, "RIGHT", 0, 0)
            else
                iconFrame:SetPoint("LEFT", display, "LEFT", 0, 0)
                status:SetPoint("LEFT", iconFrame, "RIGHT", 0, 0)
            end
        else
            iconFrame:Hide()
            status:SetPoint("LEFT", display, "LEFT", 0, 0)
        end

        label:SetPoint("LEFT", status, "LEFT", 6, 0)
        timer:SetPoint("RIGHT", status, "RIGHT", -6, 0)
        label:Show()
        timer:Show()
        iconTimer:Hide()
    end

    spark:SetShown(d.showSpark and d.displayMode == "BAR")
    dragOverlay:SetShown(not d.locked)
end

local function SetBarColor(stage, remaining)
    if not status then return end
    local d = BFH.db
    local c

    -- User requested the spell colour from 4 seconds upward,
    -- then #FF0010 once the timer is under 4 seconds.
    if remaining < 4 then
        c = d.dangerColor
    elseif stage == "BLIGHT" then
        c = d.blightColor
    else
        c = d.soulColor
    end

    status:SetStatusBarColor(c[1], c[2], c[3], c[4] or 1)
end

function BFH:StartStage(stage, duration, preview)
    if not self.db.enabled and not preview then return end

    self.stage = stage
    self.preview = preview and true or false
    self.stageDuration = Clamp(duration or 7, 0.5, 20)
    self.startTime = GetTime()
    self.endTime = self.startTime + self.stageDuration
    self.lastSpoken = nil

    local spellID, text
    if stage == "BLIGHT" then
        spellID, text = self.SPELL.BLIGHTFALL, "Blightfall"
    else
        spellID, text = self.SPELL.SOUL_REAPER, "Soul Reaper"
    end

    icon:SetTexture(self:GetSpellTexture(spellID))
    label:SetText(text)
    status:SetMinMaxValues(0, self.stageDuration)
    status:SetValue(self.stageDuration)

    self:ApplyDisplaySettings()
    SetBarColor(stage, self.stageDuration)
    display:Show()
end

function BFH:StopStage()
    self:StopTTS()
    self.stage = nil
    self.preview = nil
    self.lastSpoken = nil
    if display then display:Hide() end
end

function BFH:ShowPreview(stage)
    local duration = stage == "BLIGHT" and self.db.blightDelay or self.db.soulDelay
    self:StartStage(stage or "SOUL", duration, true)
end

function BFH:FormatRemaining(v)
    v = math.max(v, 0)
    local p = tonumber(self.db.precision) or 1
    if p <= 0 then return tostring(math.ceil(v)) end
    if p == 1 then return string.format("%.1f", v) end
    return string.format("%.2f", v)
end

function BFH:OnDisplayUpdate()
    if not self.stage or not self.endTime then return end

    local remaining = self.endTime - GetTime()
    if remaining <= 0 then
        self:StopStage()
        return
    end

    status:SetValue(remaining)

    local text = self:FormatRemaining(remaining)
    timer:SetText(text)
    iconTimer:SetText(text)
    SetBarColor(self.stage, remaining)

    if self.db.showSpark and self.db.displayMode == "BAR" and status:IsShown() then
        local fraction = math.max(0, math.min(1, remaining / self.stageDuration))
        spark:ClearAllPoints()
        spark:SetPoint("CENTER", status, "LEFT", status:GetWidth() * fraction, 0)
        spark:SetHeight(math.max(4, status:GetHeight() - 6))
    end

    local maxCount = math.min(10, math.floor(self.db.countdownStart or 4))
    local spoken

    for n = 1, maxCount do
        if remaining <= n then
            spoken = n
            break
        end
    end

    if spoken and self.lastSpoken ~= spoken then
        self.lastSpoken = spoken
        self:PlayCountdown(spoken)
    end
end
