local _, ns = ...;

-- ============================================================
--  폰트 경로 결정
--  1순위: 애드온 내장 blood.ttf
--  2순위: 와우가 현재 실제로 사용 중인 폰트 (클라이언트 언어 무관)
-- ============================================================
local ADDON_FONT   = "Interface\\AddOns\\Moonlight_bloodlust\\font\\blood.ttf"
local FALLBACK_FONT = GameFontNormal:GetFont()

local MLB_FONT
do
    local testFS = UIParent:CreateFontString(nil, "ARTWORK")
    local ok = pcall(function() testFS:SetFont(ADDON_FONT, 12, "") end)
    if ok and testFS:GetFont() == ADDON_FONT then
        MLB_FONT = ADDON_FONT
    else
        MLB_FONT = FALLBACK_FONT
    end
    testFS:Hide()
end

-- ============================================================
--  Moonlight_bloodlust  (단일 파일 버전)
--  Combines:
--    [A] LustAlert       – icon + timer + sound when Bloodlust is CAST
--    [B] ReadyAlert      – text + voice when Bloodlust debuff EXPIRES
--    [Options]           – sound list, channels, settings panel
--
--  SavedVariables : MLB_DB  (global)
--  SavedVariablesPerCharacter : MLB_Positions
--  Slash command  : /mlb   (main settings UI)
--                   /mlb test  – preview lust-cast alert
--                   /mlb reset – reset icon position/scale
--                   /mlb ready – preview ready-alert text
-- ============================================================

-- ============================================================
--  [OPTIONS] 사운드/채널/설정 패널 정의
--  (구 Moonlight_bloodlust_Options.lua 내용)
-- ============================================================

-- 파일 확장자를 자동으로 결정하는 헬퍼
local SOUND_EXT_OVERRIDE = {}   -- name -> "full_name_with_ext"

local function ResolveSoundPath(name)
    local base = "Interface\\AddOns\\Moonlight_bloodlust\\Sounds\\"
    if name:match("%.ogg$") or name:match("%.mp3$") then
        return base .. name
    end
    if SOUND_EXT_OVERRIDE[name] then
        return base .. SOUND_EXT_OVERRIDE[name]
    end
    return base .. name .. ".ogg"
end

local function RegisterExtensions(files)
    for _, filename in ipairs(files) do
        if filename:match("%.mp3$") then
            local stem = filename:gsub("%.mp3$", "")
            SOUND_EXT_OVERRIDE[stem] = filename
        elseif filename:match("%.ogg$") then
            local stem = filename:gsub("%.ogg$", "")
            SOUND_EXT_OVERRIDE[stem] = filename
        end
    end
end

local SOUNDS = {}

if MOONLIGHT_BLOODLUST_SOUND_FILES then
    RegisterExtensions(MOONLIGHT_BLOODLUST_SOUND_FILES)
    for _, filename in ipairs(MOONLIGHT_BLOODLUST_SOUND_FILES) do
        local stem = filename:gsub("%.ogg$", ""):gsub("%.mp3$", "")
        table.insert(SOUNDS, {
            name     = stem,
            path     = ResolveSoundPath(stem),
            duration = 40.0,
        })
    end
end

ns.SOUNDS = SOUNDS;

local CHANNELS = {
    { name = "주음량",   key = "Master"   },
    { name = "효과",     key = "SFX"      },
    { name = "환경소리", key = "Ambience" },
    { name = "대화",     key = "Dialog"   },
}
ns.CHANNELS = CHANNELS;

local READY_MP3 = "Ready.mp3";
local READY_MSG = "블러드 사용 가능";
ns.READY_MP3 = READY_MP3;
ns.READY_MSG = READY_MSG;

local ReadyAlert_Defaults = {
    ClassOnly  = true,
    VoiceAlert = true,
    InRaid     = false,
    FontSize   = 30,
    ShowTime   = 2,
}
ns.readyOpts = CopyTable(ReadyAlert_Defaults);

function ns.SetupOptionPanels()
    if MLB_DB.readyOpts == nil then
        MLB_DB.readyOpts = CopyTable(ReadyAlert_Defaults);
    end
    for k, v in pairs(ReadyAlert_Defaults) do
        if MLB_DB.readyOpts[k] == nil then
            MLB_DB.readyOpts[k] = v;
        end
        ns.readyOpts[k] = MLB_DB.readyOpts[k];
    end

    local canvas = CreateFrame("Frame", nil, UIParent)

    local titleFS = canvas:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge");
    titleFS:SetPoint("TOPLEFT", canvas, "TOPLEFT", 10, -10);
    titleFS:SetText("|cffffcc00Moonlight|r_Bloodlust");

    local desc = canvas:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
    desc:SetPoint("TOPLEFT", titleFS, "BOTTOMLEFT", 0, -8);
    desc:SetText("모든 설정은 /mlb 커스텀 창에서 관리합니다.");
    desc:SetTextColor(0.8, 0.8, 0.8);

    local btn = CreateFrame("Button", nil, canvas, "UIPanelButtonTemplate");
    btn:SetSize(220, 36);
    btn:SetPoint("TOPLEFT", desc, "BOTTOMLEFT", 0, -16);
    btn:SetText("Moonlight_Bloodlust 설정 창 열기");
    btn:SetScript("OnClick", function()
        SettingsPanel:Hide();
        local sf = _G["MoonlightBloodlustSettings"];
        if sf then
            if sf:IsShown() then sf:Hide() else sf:Show() end
        end
    end);

    local hint = canvas:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall");
    hint:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -10);
    hint:SetText("슬래시 명령어:  /mlb");
    hint:SetTextColor(0.55, 0.55, 0.55);

    local layout = Settings.RegisterCanvasLayoutCategory(canvas, "!Moonlight_블러드");
    Settings.RegisterAddOnCategory(layout);
end

-- ============================================================
--  [MAIN] 핵심 로직
-- ============================================================

-- ────────────────────────────────────────────────────────────
--  SHARED DATA
-- ────────────────────────────────────────────────────────────
local LUST_BUFF_IDS = {
    [2825]    = "Bloodlust",
    [32182]   = "Heroism",
    [80353]   = "Time Warp",
    [264667]  = "Primal Rage",
    [390386]  = "Fury of the Aspects",
    [466904]  = "Harrier's Cry",
    [1243972] = "Void-touched Drums",
}

local SATED_IDS = {
    [57724]  = true,
    [57723]  = true,
    [80354]  = true,
    [390435] = true,
    [264689] = true,
}

local LUST_CLASSES = {
    ["MAGE"]    = true,
    ["SHAMAN"]  = true,
    ["HUNTER"]  = true,
    ["EVOKER"]  = true,
}

local FACTION_COLORS = {
    Horde    = { 1, 0.2, 0.2 },
    Alliance = { 0.3, 0.6, 1 },
}

-- ────────────────────────────────────────────────────────────
--  DB DEFAULTS
-- ────────────────────────────────────────────────────────────
MLB_DB = MLB_DB or {}
local d = MLB_DB
d.sound          = d.sound          or (MOONLIGHT_BLOODLUST_SOUND_FILES and MOONLIGHT_BLOODLUST_SOUND_FILES[1]) or "01"
d.channel        = d.channel        or "Master"
if d.enabled        == nil then d.enabled        = true  end
d.posX           = d.posX           or 0
d.posY           = d.posY           or 200
d.iconScale      = d.iconScale      or 1.0
d.posAnchor      = d.posAnchor      or nil
d.lustLabelText  = d.lustLabelText  or "블러드"
d.lustLabelSize  = d.lustLabelSize  or 13
d.lustLabelColor = d.lustLabelColor or { r=1, g=0.3, b=0.3 }
d.timerBarType   = d.timerBarType   or "ring"   -- "ring" | "dot10" | "square10"
d.timerTextSize  = d.timerTextSize  or 26
d.timerTextColor = d.timerTextColor or { r=1, g=0.6, b=0.6 }
d.alertAnimType  = d.alertAnimType  or "zoom"   -- "zoom" | "fade"
d.readyOpts      = d.readyOpts      or {}
local ro = d.readyOpts
if ro.ClassOnly  == nil then ro.ClassOnly  = true  end
if ro.VoiceAlert == nil then ro.VoiceAlert = true  end
if ro.InRaid     == nil then ro.InRaid     = false end
if ro.FontSize   == nil then ro.FontSize   = 30    end
if ro.ShowTime   == nil then ro.ShowTime   = 2     end
if ro.Color      == nil then ro.Color      = { r=1, g=1, b=1 } end
d.combatTimer    = d.combatTimer    or {}
local ct = d.combatTimer
if ct.enabled    == nil then ct.enabled    = true  end
if ct.alwaysShow == nil then ct.alwaysShow = false end
ct.fontSize      = ct.fontSize      or 28
ct.color         = ct.color         or { r=1, g=1, b=1 }
ct.posX          = ct.posX          or 0
ct.posY          = ct.posY          or 250
ct.posAnchor     = ct.posAnchor     or nil

local function InitDB()
    local d2 = MLB_DB
    d2.sound          = d2.sound          or (MOONLIGHT_BLOODLUST_SOUND_FILES and MOONLIGHT_BLOODLUST_SOUND_FILES[1]) or "01"
    d2.channel        = d2.channel        or "Master"
    if d2.enabled        == nil then d2.enabled        = true  end
    d2.posX           = d2.posX           or 0
    d2.posY           = d2.posY           or 200
    d2.iconScale      = d2.iconScale      or 1.0
    d2.posAnchor      = d2.posAnchor      or nil
    d2.lustLabelText  = d2.lustLabelText  or "블러드"
    d2.lustLabelSize  = d2.lustLabelSize  or 13
    d2.lustLabelColor = d2.lustLabelColor or { r=1, g=0.3, b=0.3 }
    d2.timerBarType   = d2.timerBarType   or "ring"
    d2.timerTextSize  = d2.timerTextSize  or 26
    d2.timerTextColor = d2.timerTextColor or { r=1, g=0.6, b=0.6 }
    d2.alertAnimType  = d2.alertAnimType  or "zoom"
    d2.readyOpts      = d2.readyOpts      or {}
    local ro2 = d2.readyOpts
    if ro2.ClassOnly  == nil then ro2.ClassOnly  = true  end
    if ro2.VoiceAlert == nil then ro2.VoiceAlert = true  end
    if ro2.InRaid     == nil then ro2.InRaid     = false end
    if ro2.FontSize   == nil then ro2.FontSize   = 30    end
    if ro2.ShowTime   == nil then ro2.ShowTime   = 2     end
    if ro2.Color      == nil then ro2.Color      = { r=1, g=1, b=1 } end
    d2.combatTimer    = d2.combatTimer    or {}
    local ct2 = d2.combatTimer
    if ct2.enabled    == nil then ct2.enabled    = true  end
    if ct2.alwaysShow == nil then ct2.alwaysShow = false end
    ct2.fontSize      = ct2.fontSize      or 28
    ct2.color         = ct2.color         or { r=1, g=1, b=1 }
    ct2.posX          = ct2.posX          or 0
    ct2.posY          = ct2.posY          or 250
    ct2.posAnchor     = ct2.posAnchor     or nil

    for k, v in pairs(d2.readyOpts) do ns.readyOpts[k] = v end

    if MLB_Positions == nil then MLB_Positions = {} end
end

-- ────────────────────────────────────────────────────────────
--  HELPERS
-- ────────────────────────────────────────────────────────────
local function GetSoundPath(name)
    for _, s in ipairs(ns.SOUNDS) do
        if s.name == name then return s.path end
    end
    if ns.SOUNDS[1] then return ns.SOUNDS[1].path end
end

local function GetSoundDuration(name)
    for _, s in ipairs(ns.SOUNDS) do
        if s.name == name then return s.duration or 40.0 end
    end
    return 40.0
end

local function CheckSated()
    for id in pairs(SATED_IDS) do
        if C_UnitAuras.GetPlayerAuraBySpellID(id) then return true end
    end
    return false
end

-- ────────────────────────────────────────────────────────────
--  [C] COMBAT TIMER
-- ────────────────────────────────────────────────────────────
local combatStartTime  = nil
local combatTimerTicker = nil
local ctMoveMode       = false

local ct_frame = CreateFrame("Frame", "MoonlightCombatTimerFrame", UIParent)
ct_frame:SetSize(160, 40)
ct_frame:SetPoint("CENTER", UIParent, "CENTER", 0, 250)
ct_frame:Hide()

local ct_text = ct_frame:CreateFontString(nil, "OVERLAY")
ct_text:SetFont(MLB_FONT, 28, "OUTLINE")
ct_text:SetPoint("CENTER", ct_frame, "CENTER", 0, 0)
ct_text:SetText("[00:00]")
ct_text:SetTextColor(1, 1, 1)

local function FormatCombatTime(secs)
    local m = math.floor(secs / 60)
    local s = math.floor(secs % 60)
    return string.format("[%02d:%02d]", m, s)
end

local function CombatTimer_ApplyVisual()
    local c = MLB_DB.combatTimer
    if not c then return end
    ct_text:SetFont(MLB_FONT, c.fontSize or 28, "OUTLINE")
    local col = c.color or { r=1, g=1, b=1 }
    ct_text:SetTextColor(col.r, col.g, col.b)
end

local function CombatTimer_Start()
    combatStartTime = GetTime()
    ct_text:SetText(FormatCombatTime(0))
    CombatTimer_ApplyVisual()
    ct_frame:Show()
    if combatTimerTicker then combatTimerTicker:Cancel() end
    combatTimerTicker = C_Timer.NewTicker(0.5, function()
        if not combatStartTime then return end
        ct_text:SetText(FormatCombatTime(GetTime() - combatStartTime))
    end)
end

local function CombatTimer_Stop(lastTime)
    if combatTimerTicker then combatTimerTicker:Cancel(); combatTimerTicker = nil end
    combatStartTime = nil
    local c = MLB_DB.combatTimer
    if lastTime and lastTime > 0 then
        ct_text:SetText(FormatCombatTime(lastTime))
    end
    if c and c.alwaysShow and c.enabled then
        ct_frame:Show()
    elseif not ctMoveMode then
        ct_frame:Hide()
    end
end

local function CombatTimer_UpdatePosition()
    local c = MLB_DB.combatTimer
    if not c then return end
    ct_frame:ClearAllPoints()
    if c.posAnchor == "BOTTOMLEFT" then
        ct_frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", c.posX or 0, c.posY or 250)
    else
        ct_frame:SetPoint("CENTER", UIParent, "CENTER", c.posX or 0, c.posY or 250)
    end
end

local combatTimerReady = false  -- ADDON_LOADED 이후에만 전투 타이머를 동작시키는 가드

local combatEventFrame = CreateFrame("Frame", "MoonlightCombatEventFrame")
combatEventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
combatEventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
combatEventFrame:SetScript("OnEvent", function(self, event)
    -- [수정] ADDON_LOADED 이전에는 이벤트를 무시 (DB가 준비되지 않은 상태 방지)
    if not combatTimerReady then return end
    local c = MLB_DB.combatTimer
    if not c or not c.enabled then return end
    if event == "PLAYER_REGEN_DISABLED" then
        CombatTimer_Start()
    elseif event == "PLAYER_REGEN_ENABLED" then
        local elapsed = combatStartTime and (GetTime() - combatStartTime) or 0
        CombatTimer_Stop(elapsed)
    end
end)

-- ────────────────────────────────────────────────────────────
--  [A] LUSTALERT – 타이머 표시 (블러드 시전 시)
-- ────────────────────────────────────────────────────────────
local ANIM_DURATION = 0.35
local LUST_DURATION = 40
local isActive      = false
local hideTimer     = nil
local animTimer     = nil
local lustStartTime = nil
local previewHandle = nil
local previewingRow = nil
local moveMode      = false

-- ────────────────────────────────────────────────────────────
--  원형 진행 바
-- ────────────────────────────────────────────────────────────
local RING_OUTER   = 55
local RING_THICK   = 9
local SEG_N        = 48
local SEG_GAP      = 1.5
local DISPLAY_SIZE = RING_OUTER * 2 + 10

local display = CreateFrame("Frame", nil, UIParent)
display:SetSize(DISPLAY_SIZE, DISPLAY_SIZE)
display:SetPoint("CENTER", UIParent, "CENTER", 0, 200)
display:SetScale(1.0)
display:Hide()

local timerFrame = display

local arcPerSeg = (2 * math.pi * RING_OUTER) / SEG_N
local segLen    = math.max(1, arcPerSeg - SEG_GAP)

local segments = {}

for i = 1, SEG_N do
    local angle = -math.pi / 2 + (2 * math.pi * (i - 1) / SEG_N)
    local px = math.cos(angle) * RING_OUTER
    local py = math.sin(angle) * RING_OUTER

    local absAngle = math.abs(angle) % math.pi
    local segW, segH
    if absAngle < math.pi / 4 or absAngle > 3 * math.pi / 4 then
        segW = segLen
        segH = RING_THICK
    else
        segW = RING_THICK
        segH = segLen
    end

    local f = CreateFrame("Frame", nil, display)
    f:SetSize(segW, segH)
    f:SetPoint("CENTER", display, "CENTER", px, py)

    local tex = f:CreateTexture(nil, "ARTWORK")
    tex:SetAllPoints(f)
    tex:SetColorTexture(1, 0.2, 0.2, 1)

    segments[i] = { frame = f, tex = tex }
    f:Hide()
end

local function UpdateRingBar(progress)
    local lit = math.floor(progress * SEG_N + 0.5)
    for i = 1, SEG_N do
        if i <= lit then
            segments[i].frame:Show()
        else
            segments[i].frame:Hide()
        end
    end
end

local function SetRingColor(r, g, b)
    for i = 1, SEG_N do
        segments[i].tex:SetColorTexture(r, g, b, 1)
    end
end

local function HideRingBar()
    for i = 1, SEG_N do
        segments[i].frame:Hide()
    end
end

HideRingBar()

-- ────────────────────────────────────────────────────────────
--  동그라미 점 바 (dot10)
--  dot10: 점 10개, 1개당 4초.
--    4초 구간 시작: ● (가득)
--    2초 경과:      ◐ (반쪽)
-- ────────────────────────────────────────────────────────────
local DOT_CHAR_ON   = "|cffff4444●|r"
local DOT_CHAR_HALF = "|cffff4444◐|r"
local DOT_CHAR_OFF  = "|cff333333●|r"

local dotBar = display:CreateFontString(nil, "OVERLAY")
dotBar:SetFont(MLB_FONT, 11, "OUTLINE")
dotBar:SetText("")
dotBar:SetAlpha(0)
dotBar:Hide()

local function UpdateDotBar(remaining)
    local TOTAL    = 10
    local SEC_EACH = LUST_DURATION / TOTAL  -- 4초
    local dotsLeft = remaining / SEC_EACH
    local fullDots = math.floor(dotsLeft)
    local frac     = dotsLeft - fullDots
    local currentChar
    if fullDots >= TOTAL then
        currentChar = nil
        fullDots = TOTAL
    elseif remaining <= 0 then
        fullDots = 0
        currentChar = nil
    else
        currentChar = (frac > 0.5) and DOT_CHAR_ON or DOT_CHAR_HALF
    end

    local s = ""
    for i = 1, TOTAL do
        if i <= fullDots then
            s = s .. DOT_CHAR_ON
        elseif i == fullDots + 1 and currentChar then
            s = s .. currentChar
        else
            s = s .. DOT_CHAR_OFF
        end
    end
    dotBar:SetText(s)
end

-- ────────────────────────────────────────────────────────────
--  네모 바 (square10)
--  square10: 네모 10개, 1개당 4초.
--    4초 구간 시작: ■ (가득)
--    2초 경과:     ▦▒▨▥ (반쪽, 왼쪽 채워진 네모)
--    꺼진 상태:    □ (빈 네모)
-- ────────────────────────────────────────────────────────────
local SQ_CHAR_ON   = "|cffff4444■|r"
local SQ_CHAR_HALF = "|cffff4444▥|r"
local SQ_CHAR_OFF  = "|cff333333□|r"

local squareBar = display:CreateFontString(nil, "OVERLAY")
squareBar:SetFont(MLB_FONT, 11, "OUTLINE")
squareBar:SetText("")
squareBar:SetAlpha(0)
squareBar:Hide()

local function UpdateSquareBar(remaining)
    local TOTAL    = 10
    local SEC_EACH = LUST_DURATION / TOTAL  -- 4초
    local dotsLeft = remaining / SEC_EACH
    local fullDots = math.floor(dotsLeft)
    local frac     = dotsLeft - fullDots
    local currentChar
    if fullDots >= TOTAL then
        currentChar = nil
        fullDots = TOTAL
    elseif remaining <= 0 then
        fullDots = 0
        currentChar = nil
    else
        currentChar = (frac > 0.5) and SQ_CHAR_ON or SQ_CHAR_HALF
    end

    local s = ""
    for i = 1, TOTAL do
        if i <= fullDots then
            s = s .. SQ_CHAR_ON
        elseif i == fullDots + 1 and currentChar then
            s = s .. currentChar
        else
            s = s .. SQ_CHAR_OFF
        end
    end
    squareBar:SetText(s)
end

-- ── 바 모드 전환 헬퍼 ────────────────────────────────────────
local function ShowBarForMode()
    local t = MLB_DB.timerBarType or "ring"
    if t == "ring" then
        dotBar:Hide()
        squareBar:Hide()
    elseif t == "square10" then
        HideRingBar()
        dotBar:Hide()
        squareBar:Show()
    else  -- dot10
        HideRingBar()
        squareBar:Hide()
        dotBar:Show()
    end
end

local function HideAllBars()
    HideRingBar()
    dotBar:Hide()
    squareBar:Hide()
end

-- ── 라벨 / 타이머 텍스트 ──────────────────────────────────────
local lustLabel = display:CreateFontString(nil, "OVERLAY")
lustLabel:SetFont(MLB_FONT, 13, "OUTLINE")
lustLabel:SetText(MLB_DB.lustLabelText or "")
lustLabel:SetTextColor(1, 0.3, 0.3)

local timerText = display:CreateFontString(nil, "OVERLAY")
timerText:SetFont(MLB_FONT, MLB_DB.timerTextSize or 26, "OUTLINE")
local ttc = MLB_DB.timerTextColor or { r=1, g=0.6, b=0.6 }
timerText:SetTextColor(ttc.r, ttc.g, ttc.b)

local function RepositionTimerTexts()
    local lblSz   = MLB_DB.lustLabelSize or 13
    local timerSz = MLB_DB.timerTextSize or 26
    local gap     = 4
    local lblBottom  =  lblSz * 0.5 + gap * 0.5
    local timerTop   = -(timerSz * 0.5 + gap * 0.5)
    local dotBarH    = 14
    local dotBarY    = timerTop - timerSz * 0.5 - gap - dotBarH * 0.5

    lustLabel:ClearAllPoints()
    lustLabel:SetPoint("CENTER", display, "CENTER", 0, lblBottom)
    timerText:ClearAllPoints()
    timerText:SetPoint("CENTER", display, "CENTER", 0, timerTop)
    dotBar:ClearAllPoints()
    dotBar:SetPoint("CENTER", display, "CENTER", 0, dotBarY)
    squareBar:ClearAllPoints()
    squareBar:SetPoint("CENTER", display, "CENTER", 0, dotBarY)
end
RepositionTimerTexts()

local function HideAlert()
    if hideTimer then hideTimer:Cancel() hideTimer = nil end
    if animTimer then animTimer:Cancel() animTimer = nil end
    lustStartTime = nil
    HideAllBars()
    if not moveMode then
        display:Hide()
    end
end

local function StartZoomAnim()
    display:Show()
    local targetScale = MLB_DB.iconScale
    local anim = MLB_DB.alertAnimType or "zoom"

    timerText:SetAlpha(0)
    lustLabel:SetAlpha(0)
    dotBar:SetAlpha(0)
    squareBar:SetAlpha(0)

    if anim == "zoom" then
        display:SetScale(targetScale * 0.1)
    else
        display:SetScale(targetScale)
    end

    local start = GetTime()
    if animTimer then animTimer:Cancel() end
    animTimer = C_Timer.NewTicker(0.016, function(t)
        local prog = math.min((GetTime() - start) / ANIM_DURATION, 1)
        local ease = 1 - (1 - prog) * (1 - prog)
        timerText:SetAlpha(ease)
        lustLabel:SetAlpha(ease)
        dotBar:SetAlpha(ease)
        squareBar:SetAlpha(ease)
        if anim == "zoom" then
            display:SetScale(targetScale * (0.1 + ease * 0.9))
        end
        if prog >= 1 then
            display:SetScale(targetScale)
            timerText:SetAlpha(1)
            lustLabel:SetAlpha(1)
            dotBar:SetAlpha(1)
            squareBar:SetAlpha(1)
            t:Cancel()
        end
    end)
end

local function ShowBloodlustAlert(playSound)
    lustStartTime = GetTime()

    local col
    if MLB_DB.lustLabelColor then
        col = { MLB_DB.lustLabelColor.r, MLB_DB.lustLabelColor.g, MLB_DB.lustLabelColor.b }
    else
        local faction = UnitFactionGroup("player")
        col = FACTION_COLORS[faction] or FACTION_COLORS["Horde"]
    end
    lustLabel:SetTextColor(col[1], col[2], col[3])
    local ttc2 = MLB_DB.timerTextColor or { r=1, g=0.6, b=0.6 }
    timerText:SetTextColor(ttc2.r, ttc2.g, ttc2.b)
    timerText:SetText(string.format("%.1f", LUST_DURATION))

    if hideTimer then hideTimer:Cancel() end
    ShowBarForMode()
    local barType = MLB_DB.timerBarType or "ring"
    if barType == "ring" then
        SetRingColor(col[1], col[2], col[3])
        UpdateRingBar(1.0)
    elseif barType == "square10" then
        UpdateSquareBar(LUST_DURATION)
    else
        UpdateDotBar(LUST_DURATION)
    end
    StartZoomAnim()

    if playSound ~= false then
        local path = GetSoundPath(MLB_DB.sound)
        if path then PlaySoundFile(path, MLB_DB.channel) end
    end

    hideTimer = C_Timer.NewTimer(LUST_DURATION, function()
        HideAlert()
    end)
end

-- LustAlert polling (OnUpdate)
local CHECK_INTERVAL = 0.2
local elapsed        = 0
local justZoned      = false
local stableCount    = 0
local STABLE_NEEDED  = 10

local pollFrame = CreateFrame("Frame", "MoonlightLustPollFrame")
pollFrame:SetScript("OnUpdate", function(self, delta)
    elapsed = elapsed + delta
    if elapsed < CHECK_INTERVAL then return end
    elapsed = 0

    local active = CheckSated()

    if justZoned then
        isActive    = active
        stableCount = stableCount + 1
        if stableCount >= STABLE_NEEDED then justZoned = false end
        return
    end

    if active and not isActive then
        if MLB_DB.enabled then ShowBloodlustAlert() end
    end
    isActive = active

    if lustStartTime then
        local passed    = GetTime() - lustStartTime
        local remaining = math.max(LUST_DURATION - passed, 0)
        timerText:SetText(string.format("%.1f", remaining))
        local barType = MLB_DB.timerBarType or "ring"
        if barType == "ring" then
            UpdateRingBar(remaining / LUST_DURATION)
        elseif barType == "square10" then
            UpdateSquareBar(remaining)
        else
            UpdateDotBar(remaining)
        end
        if remaining <= 0 then HideAlert() end
    end
end)

-- ────────────────────────────────────────────────────────────
--  [B] READY-ALERT
-- ────────────────────────────────────────────────────────────
local READY_REFRESH = 0.5
local ready_frame   = CreateFrame("FRAME", nil, UIParent)
local readyAfter    = nil
local readyBlink    = false
local readyBfirst   = true

ns.readyText = ready_frame:CreateFontString(nil, "OVERLAY")
ns.readyText:SetFont(MLB_FONT, 30, "THICKOUTLINE")
ns.readyText:SetPoint("CENTER", ready_frame, "CENTER", 0, 0)
ns.readyText:SetText(ns.READY_MSG)
ns.readyText:Hide()

ready_frame:SetPoint("CENTER", UIParent, "CENTER", 0, 100)
ready_frame:SetSize(100, 50)
ready_frame:Show()

local function hideReadyMsg()   ns.readyText:Hide() end
local function showReadyMsg()
    local c = (MLB_DB.readyOpts and MLB_DB.readyOpts.Color) or { r=1, g=1, b=1 }
    ns.readyText:SetTextColor(c.r, c.g, c.b)
    ns.readyText:Show()
    C_Timer.After(ns.readyOpts.ShowTime, hideReadyMsg)
end

local function fireReadyAlert()
    if ns.readyOpts.VoiceAlert then
        PlaySoundFile("Interface\\AddOns\\Moonlight_bloodlust\\" .. ns.READY_MP3, "MASTER")
    end
    showReadyMsg()
end

local function updateReadyAuras()
    if not IsInInstance() then return end
    if readyBfirst then return end

    if ns.readyText:IsShown() then
        local c = (MLB_DB.readyOpts and MLB_DB.readyOpts.Color) or { r=1, g=1, b=1 }
        if readyBlink then
            readyBlink = false
            ns.readyText:SetTextColor(c.r, c.g, c.b)
        else
            readyBlink = true
            ns.readyText:SetTextColor(c.r * 0.4, c.g * 0.4, c.b * 0.4)
        end
    end

    for id in pairs(SATED_IDS) do
        local aura = C_UnitAuras.GetPlayerAuraBySpellID(id)
        if aura then
            local remaining = aura.expirationTime - GetTime()
            if remaining < 1 and readyAfter == nil then
                C_Timer.After(remaining, fireReadyAlert)
                readyAfter = remaining
            end
            return
        end
    end
    readyAfter = nil
end

local function isReadyNeeded()
    local _, englishClass = UnitClass("player")
    if ns.readyOpts.ClassOnly then
        if not LUST_CLASSES[englishClass] then return false end
    end
    if IsInGroup() then
        if IsInRaid() then
            return ns.readyOpts.InRaid == true
        end
        return true
    end
    return false
end

local readyTicker = nil

ns.checkReadyStatus = function()
    if readyTicker then readyTicker:Cancel() end
    if isReadyNeeded() then
        readyTicker = C_Timer.NewTicker(READY_REFRESH, updateReadyAuras)
    end
end

-- ────────────────────────────────────────────────────────────
--  SETTINGS UI  (/mlb)
-- ────────────────────────────────────────────────────────────

local rows   = {}
local chRows = {}

local function MakeBox(parent, px, py, pw, ph)
    local f = CreateFrame("Frame", nil, parent, "BackdropTemplate")
    f:SetPoint("TOPLEFT", parent, "TOPLEFT", px, py)
    f:SetSize(pw, ph)
    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        tile = false, edgeSize = 1,
    })
    f:SetBackdropColor(0, 0, 0, 0.28)
    f:SetBackdropBorderColor(0.38, 0.38, 0.38, 0.9)
    return f
end

local function MakeBoxHeader(box, labelText)
    local lbl = box:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lbl:SetPoint("TOPLEFT", box, "TOPLEFT", 8, -8)
    lbl:SetText("|cffffcc00" .. labelText .. "|r")
    local div = box:CreateTexture(nil, "ARTWORK")
    div:SetColorTexture(0.7, 0.65, 0.3, 0.8)
    div:SetPoint("TOPLEFT",  box, "TOPLEFT",  1, -24)
    div:SetPoint("TOPRIGHT", box, "TOPRIGHT", -1, -24)
    div:SetHeight(1)
    return lbl
end

local function MakeDivider(parent, yOff)
    local d = parent:CreateTexture(nil, "ARTWORK")
    d:SetColorTexture(0.7, 0.65, 0.3, 0.8)
    d:SetPoint("TOPLEFT",  parent, "TOPLEFT",  8, yOff)
    d:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -8, yOff)
    d:SetHeight(1)
end

local function MakeLbl(parent, x, y, text, r, g, b)
    local fs = parent:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    fs:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    fs:SetText(text)
    if r then fs:SetTextColor(r, g, b) end
    return fs
end

local function MakeCheck(parent, x, y, labelText, getVal, setVal)
    local cb = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    cb:SetSize(24, 24)
    cb:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
    cb:SetChecked(getVal())
    local lbl = parent:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    lbl:SetPoint("LEFT", cb, "RIGHT", 2, 0)
    lbl:SetText(labelText)
    cb:SetScript("OnClick", function(self)
        setVal(self:GetChecked() and true or false)
    end)
    return cb
end

local function MakeColorPickerBtn(parent, x, y, w, getCol, setCol)
    local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btn:SetPoint("TOPLEFT",  parent, "TOPLEFT",  x,  y)
    btn:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -8, y)
    btn:SetHeight(20)
    btn:SetText("색상 선택")

    local swatch = btn:CreateTexture(nil, "OVERLAY")
    swatch:SetSize(14, 14)
    swatch:SetPoint("RIGHT", btn, "RIGHT", -8, 0)
    local function RefreshSwatch()
        local c = getCol()
        swatch:SetColorTexture(c.r, c.g, c.b, 1)
    end
    RefreshSwatch()
    btn.RefreshSwatch = RefreshSwatch

    btn:SetScript("OnClick", function()
        local c = getCol()
        if ColorPickerFrame.SetupColorPickerAndShow then
            ColorPickerFrame:SetupColorPickerAndShow({
                r = c.r, g = c.g, b = c.b,
                opacity = nil,
                hasOpacity = false,
                swatchFunc = function()
                    local r, g, b = ColorPickerFrame:GetColorRGB()
                    setCol(r, g, b)
                    RefreshSwatch()
                end,
                opacityFunc = nil,
                cancelFunc = function(prev)
                    setCol(prev.r, prev.g, prev.b)
                    RefreshSwatch()
                end,
                previousValues = { r=c.r, g=c.g, b=c.b },
            })
        else
            ColorPickerFrame:Hide()
            ColorPickerFrame.func = function()
                local r, g, b = ColorPickerFrame:GetColorRGB()
                setCol(r, g, b)
                RefreshSwatch()
            end
            ColorPickerFrame.cancelFunc = function()
                setCol(c.r, c.g, c.b)
                RefreshSwatch()
            end
            ColorPickerFrame:SetColorRGB(c.r, c.g, c.b)
            ColorPickerFrame:Show()
        end
    end)
    return btn
end

-- ── 메인 프레임 ─────────────────────────────────────────────
local SF_W, SF_H = 820, 730
local sf = CreateFrame("Frame", "MoonlightBloodlustSettings", UIParent, "BackdropTemplate")
sf:SetSize(SF_W, SF_H)
sf:SetPoint("CENTER")
sf:SetFrameStrata("DIALOG")
sf:SetFrameLevel(20)
sf:SetMovable(true)
sf:EnableMouse(true)
sf:RegisterForDrag("LeftButton")
sf:SetClampedToScreen(true)
sf:SetScript("OnDragStart", sf.StartMoving)
sf:SetScript("OnDragStop",  sf.StopMovingOrSizing)
sf:SetBackdrop({
    bgFile   = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
    edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
    tile = true, tileSize = 32, edgeSize = 32,
    insets = { left=8, right=8, top=8, bottom=8 },
})
sf:Hide()
table.insert(UISpecialFrames, "MoonlightBloodlustSettings")

local titleFS = sf:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
titleFS:SetPoint("TOPLEFT", 20, -14)
titleFS:SetText("|cffffcc00Moonlight|r_Bloodlust")

local subFS = sf:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
subFS:SetPoint("LEFT", titleFS, "RIGHT", 8, -1)
subFS:SetTextColor(0.5, 0.5, 0.5)
subFS:SetText("/mlb")

local closeBtn = CreateFrame("Button", nil, sf, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", -4, -4)
closeBtn:SetScript("OnClick", function()
    if previewHandle then StopSound(previewHandle) previewHandle = nil previewingRow = nil end
    sf:Hide()
end)

local topDiv = sf:CreateTexture(nil, "ARTWORK")
topDiv:SetColorTexture(0.4, 0.4, 0.4, 0.5)
topDiv:SetPoint("TOPLEFT",  sf, "TOPLEFT",  16, -40)
topDiv:SetPoint("TOPRIGHT", sf, "TOPRIGHT", -16, -40)
topDiv:SetHeight(1)

local COL_TOP = -46
local COL_H   = 575
local COL1_X  = 16
local COL1_W  = 248
local COL2_X  = 280
local COL2_W  = 248
local COL3_X  = 544
local COL3_W  = 248

-- ════════════════════════════════════════════════════════════
--  좌측 컬럼: 시전 알림
-- ════════════════════════════════════════════════════════════
local box1 = MakeBox(sf, COL1_X, COL_TOP, COL1_W, COL_H)
MakeBoxHeader(box1, "블러드 시전 알림")

local enCheck = MakeCheck(box1, 4, -28,
    "활성화",
    function() return MLB_DB.enabled end,
    function(v) MLB_DB.enabled = v end)

MakeDivider(box1, -52)

MakeLbl(box1, 8, -58, "사운드 선택", 1, 0.82, 0)
local soundCountFS = box1:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
soundCountFS:SetPoint("TOPRIGHT", box1, "TOPRIGHT", -8, -58)
local soundFileCount = #ns.SOUNDS - 1
soundCountFS:SetText("|cff888888" .. soundFileCount .. "개 파일|r")

local function MakeCustomDropdown(parent, x, y, w, h, items, getVal, setVal, displayFunc)
    local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btn:SetSize(w, h)
    btn:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)

    local btnTxt = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    btnTxt:SetPoint("LEFT", btn, "LEFT", 8, 0)
    btnTxt:SetPoint("RIGHT", btn, "RIGHT", -18, 0)
    btnTxt:SetJustifyH("LEFT")
    btnTxt:SetText(displayFunc and displayFunc(getVal()) or getVal())
    btn.label = btnTxt

    local arrow = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    arrow:SetPoint("RIGHT", btn, "RIGHT", -6, 0)
    arrow:SetText("v")

    local popup = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
    popup:SetSize(w, #items * h)
    popup:SetFrameStrata("TOOLTIP")
    popup:SetFrameLevel(200)
    popup:SetBackdrop({
        bgFile   = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        tile = false, edgeSize = 1,
    })
    popup:SetBackdropBorderColor(0.6, 0.5, 0.1, 1)
    popup:Hide()

    local itemBtns = {}
    for i, item in ipairs(items) do
        local ib = CreateFrame("Button", nil, popup)
        ib:SetSize(w, h)
        ib:SetPoint("TOPLEFT", popup, "TOPLEFT", 0, -(i-1)*h)

        local ibBg = ib:CreateTexture(nil, "BACKGROUND")
        ibBg:SetAllPoints()
        ibBg:SetColorTexture(1, 1, 1, 0)
        ib.bg = ibBg

        local ibTxt = ib:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        ibTxt:SetPoint("LEFT", ib, "LEFT", 8, 0)
        ibTxt:SetJustifyH("LEFT")
        ibTxt:SetText(item.label or item.value)
        ib.lbl = ibTxt

        ib:SetScript("OnEnter", function() ibBg:SetColorTexture(1, 0.82, 0, 0.2) end)
        ib:SetScript("OnLeave", function()
            if getVal() == item.value then
                ibBg:SetColorTexture(1, 0.82, 0, 0.12)
            else
                ibBg:SetColorTexture(1, 1, 1, 0)
            end
        end)
        ib:SetScript("OnClick", function()
            setVal(item.value)
            btnTxt:SetText(displayFunc and displayFunc(item.value) or item.value)
            for j, _ in ipairs(items) do
                if getVal() == items[j].value then
                    itemBtns[j].bg:SetColorTexture(1, 0.82, 0, 0.12)
                else
                    itemBtns[j].bg:SetColorTexture(1, 1, 1, 0)
                end
            end
            popup:Hide()
        end)
        itemBtns[i] = ib
    end

    btn:SetScript("OnClick", function()
        if popup:IsShown() then
            popup:Hide()
        else
            for i, item in ipairs(items) do
                if getVal() == item.value then
                    itemBtns[i].bg:SetColorTexture(1, 0.82, 0, 0.12)
                else
                    itemBtns[i].bg:SetColorTexture(1, 1, 1, 0)
                end
            end
            popup:ClearAllPoints()
            popup:SetPoint("TOPLEFT", btn, "BOTTOMLEFT", 0, -2)
            popup:Show()
            popup:SetSize(w, #items * h)
        end
    end)

    popup:SetScript("OnHide", function() end)
    btn.popup  = popup
    btn.Refresh = function()
        local disp = displayFunc and displayFunc(getVal()) or getVal()
        btnTxt:SetText(disp)
        for i, item in ipairs(items) do
            if getVal() == item.value then
                itemBtns[i].bg:SetColorTexture(1, 0.82, 0, 0.12)
            else
                itemBtns[i].bg:SetColorTexture(1, 1, 1, 0)
            end
        end
    end
    return btn
end

-- ── 사운드 드롭다운 ──────────────────────────────────────────
local soundItems = {}
for _, s in ipairs(ns.SOUNDS) do
    table.insert(soundItems, { label = s.name, value = s.name })
end

local soundDropBtn
soundDropBtn = MakeCustomDropdown(
    box1, 8, -74, COL1_W - 16, 22,
    soundItems,
    function() return MLB_DB.sound or (MOONLIGHT_BLOODLUST_SOUND_FILES and MOONLIGHT_BLOODLUST_SOUND_FILES[1]) or "01" end,
    function(v) MLB_DB.sound = v end
)
-- [수정] 사운드 선택 옆 재생/중지 버튼 제거

MakeDivider(box1, -100)
MakeLbl(box1, 8, -106, "출력 채널", 1, 0.82, 0)

local function GetChannelName(key)
    for _, ch in ipairs(ns.CHANNELS) do
        if ch.key == key then return ch.name end
    end
    return key
end

local channelItems = {}
for _, ch in ipairs(ns.CHANNELS) do
    table.insert(channelItems, { label = ch.name, value = ch.key })
end

local channelDropBtn = MakeCustomDropdown(
    box1, 8, -122, COL1_W - 16, 22,
    channelItems,
    function() return MLB_DB.channel or "Master" end,
    function(v) MLB_DB.channel = v end,
    GetChannelName
)

-- UpdateRows: 드롭다운 텍스트 동기화 (전방 선언, 아래에서 재정의)
local UpdateRows
local animTypeDropBtn
local barTypeDropBtn
local lblSizeDisplay
local timerSizeDisplay

UpdateRows = function()
    if soundDropBtn     then soundDropBtn.Refresh()     end
    if channelDropBtn   then channelDropBtn.Refresh()   end
    if animTypeDropBtn  then animTypeDropBtn.Refresh()  end
    if barTypeDropBtn   then barTypeDropBtn.Refresh()   end
    if lblSizeDisplay   then lblSizeDisplay:SetText(tostring(MLB_DB.lustLabelSize or 13)) end
    if timerSizeDisplay then timerSizeDisplay:SetText(tostring(MLB_DB.timerTextSize or 26)) end
end

MakeDivider(box1, -148)

-- ── 타이머 라벨 텍스트 ───────────────────────────────────────
MakeLbl(box1, 8, -154, "타이머 라벨", 1, 0.82, 0)

local labelEdit = CreateFrame("EditBox", nil, box1, "InputBoxTemplate")
labelEdit:SetSize(COL1_W - 16, 20)
labelEdit:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -170)
labelEdit:SetAutoFocus(false)
labelEdit:SetMaxLetters(32)
labelEdit:SetText(MLB_DB.lustLabelText or "")
labelEdit:SetScript("OnEnterPressed", function(self)
    local txt = self:GetText()
    MLB_DB.lustLabelText = txt; lustLabel:SetText(txt); self:ClearFocus()
end)
labelEdit:SetScript("OnEscapePressed", function(self)
    self:SetText(MLB_DB.lustLabelText or ""); self:ClearFocus()
end)
labelEdit:SetScript("OnTextChanged", function(self)
    local txt = self:GetText()
    MLB_DB.lustLabelText = txt; lustLabel:SetText(txt)
end)

MakeLbl(box1, 8, -194, "라벨 글씨 크기", 1, 0.82, 0)

lblSizeDisplay = box1:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
lblSizeDisplay:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -212)
lblSizeDisplay:SetWidth(COL1_W - 16)
lblSizeDisplay:SetJustifyH("CENTER")

local lblMinusBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
lblMinusBtn:SetSize(28, 20)
lblMinusBtn:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -210)
lblMinusBtn:SetText("-")

local lblPlusBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
lblPlusBtn:SetSize(28, 20)
lblPlusBtn:SetPoint("TOPRIGHT", box1, "TOPRIGHT", -8, -210)
lblPlusBtn:SetText("+")

local function RefreshLabelSize()
    local sz = MLB_DB.lustLabelSize or 13
    lblSizeDisplay:SetText(tostring(sz))
    lustLabel:SetFont(MLB_FONT, sz, "OUTLINE")
    display:SetSize(DISPLAY_SIZE, DISPLAY_SIZE)
    RepositionTimerTexts()
end

lblMinusBtn:SetScript("OnClick", function()
    local sz = math.max(8, (MLB_DB.lustLabelSize or 13) - 1)
    MLB_DB.lustLabelSize = sz; RefreshLabelSize()
end)
lblPlusBtn:SetScript("OnClick", function()
    local sz = math.min(40, (MLB_DB.lustLabelSize or 13) + 1)
    MLB_DB.lustLabelSize = sz; RefreshLabelSize()
end)

MakeLbl(box1, 8, -236, "라벨 색상", 1, 0.82, 0)
local lblColorBtn = MakeColorPickerBtn(box1, 8, -252, COL1_W - 32,
    function()
        return MLB_DB.lustLabelColor or { r=1, g=0.3, b=0.3 }
    end,
    function(r, g, b)
        MLB_DB.lustLabelColor = { r=r, g=g, b=b }
        lustLabel:SetTextColor(r, g, b)
    end
)

MakeDivider(box1, -298)

-- ── 등장 애니메이션 ──────────────────────────────────────────
MakeLbl(box1, 8, -304, "등장 애니메이션", 1, 0.82, 0)

local animTypeItems = {
    { label = "↗ 줌인",    value = "zoom" },
    { label = "◎ 페이드인", value = "fade" },
}
local function GetAnimTypeLabel(v)
    for _, it in ipairs(animTypeItems) do
        if it.value == v then return it.label end
    end
    return v
end
animTypeDropBtn = MakeCustomDropdown(
    box1, 8, -320, COL1_W - 16, 22,
    animTypeItems,
    function() return MLB_DB.alertAnimType or "zoom" end,
    function(v) MLB_DB.alertAnimType = v end,
    GetAnimTypeLabel
)

MakeDivider(box1, -348)

-- ── 타이머 바 스타일 ─────────────────────────────────────────
MakeLbl(box1, 8, -354, "타이머 바 스타일", 1, 0.82, 0)

local barTypeItems = {
    { label = "⊙ 원형 바",     value = "ring"     },
    { label = "● 동그라미 바", value = "dot10"    },
    { label = "■ 네모 바",     value = "square10" },
}

local function GetBarTypeLabel(v)
    for _, it in ipairs(barTypeItems) do
        if it.value == v then return it.label end
    end
    return v
end

barTypeDropBtn = MakeCustomDropdown(
    box1, 8, -370, COL1_W - 16, 22,
    barTypeItems,
    function() return MLB_DB.timerBarType or "ring" end,
    function(v)
        MLB_DB.timerBarType = v
        if lustStartTime then
            local remaining = math.max(LUST_DURATION - (GetTime() - lustStartTime), 0)
            ShowBarForMode()
            if v == "ring" then
                local llc = MLB_DB.lustLabelColor or { r=1, g=0.3, b=0.3 }
                SetRingColor(llc.r, llc.g, llc.b)
                UpdateRingBar(remaining / LUST_DURATION)
            elseif v == "square10" then
                UpdateSquareBar(remaining)
            else
                UpdateDotBar(remaining)
            end
        elseif moveMode then
            ShowBarForMode()
            if v == "ring" then
                SetRingColor(1, 0.3, 0.3); UpdateRingBar(0.75)
            elseif v == "square10" then
                UpdateSquareBar(LUST_DURATION * 0.75)
            else
                UpdateDotBar(LUST_DURATION * 0.75)
            end
        end
    end,
    GetBarTypeLabel
)

MakeDivider(box1, -398)

-- ── 초시계 글씨 크기 ─────────────────────────────────────────
MakeLbl(box1, 8, -404, "초시계 글씨 크기", 1, 0.82, 0)

timerSizeDisplay = box1:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
timerSizeDisplay:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -422)
timerSizeDisplay:SetWidth(COL1_W - 16)
timerSizeDisplay:SetJustifyH("CENTER")
timerSizeDisplay:SetText(tostring(MLB_DB.timerTextSize or 26))

local timerSizeMinusBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
timerSizeMinusBtn:SetSize(28, 20)
timerSizeMinusBtn:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -420)
timerSizeMinusBtn:SetText("-")

local timerSizePlusBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
timerSizePlusBtn:SetSize(28, 20)
timerSizePlusBtn:SetPoint("TOPRIGHT", box1, "TOPRIGHT", -8, -420)
timerSizePlusBtn:SetText("+")

local function RefreshTimerTextSize()
    local sz = MLB_DB.timerTextSize or 26
    timerSizeDisplay:SetText(tostring(sz))
    timerText:SetFont(MLB_FONT, sz, "OUTLINE")
    RepositionTimerTexts()
end

timerSizeMinusBtn:SetScript("OnClick", function()
    local sz = math.max(8, (MLB_DB.timerTextSize or 26) - 1)
    MLB_DB.timerTextSize = sz; RefreshTimerTextSize()
end)
timerSizePlusBtn:SetScript("OnClick", function()
    local sz = math.min(60, (MLB_DB.timerTextSize or 26) + 1)
    MLB_DB.timerTextSize = sz; RefreshTimerTextSize()
end)

MakeLbl(box1, 8, -444, "초시계 색상", 1, 0.82, 0)
local timerColorBtn = MakeColorPickerBtn(box1, 8, -460, COL1_W - 32,
    function()
        return MLB_DB.timerTextColor or { r=1, g=0.6, b=0.6 }
    end,
    function(r, g, b)
        MLB_DB.timerTextColor = { r=r, g=g, b=b }
        timerText:SetTextColor(r, g, b)
    end
)

MakeDivider(box1, -484)

MakeLbl(box1, 8, -490, "타이머 위치", 1, 0.82, 0)

local moveBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
moveBtn:SetSize(86, 22)
moveBtn:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -506)
moveBtn:SetText("위치 이동")

local resetPosBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
resetPosBtn:SetSize(90, 22)
resetPosBtn:SetPoint("LEFT", moveBtn, "RIGHT", 4, 0)
resetPosBtn:SetText("위치 초기화")

local function UpdateMoveBtn()
    moveBtn:SetText(moveMode and "|cffff9999이동 중…|r" or "위치 이동")
end

moveBtn:SetScript("OnClick", function()
    moveMode = not moveMode
    if moveMode then
        if not lustStartTime then
            timerText:SetText("40.0"); lustLabel:SetTextColor(1, 0.3, 0.3)
            timerText:SetTextColor(1, 0.6, 0.6); timerText:SetAlpha(1); lustLabel:SetAlpha(1)
            display:SetScale(MLB_DB.iconScale); display:Show()
            ShowBarForMode()
            if (MLB_DB.timerBarType or "ring") == "ring" then
                SetRingColor(1, 0.3, 0.3)
                UpdateRingBar(0.75)
            elseif MLB_DB.timerBarType == "square10" then
                UpdateSquareBar(LUST_DURATION * 0.75)
            else
                UpdateDotBar(LUST_DURATION * 0.75)
            end
        end
        display:SetMovable(true); display:EnableMouse(true)
        display:RegisterForDrag("LeftButton")
        display:SetScript("OnDragStart", display.StartMoving)
        display:SetScript("OnDragStop", function(self)
            self:StopMovingOrSizing()
            MLB_DB.posX = self:GetLeft(); MLB_DB.posY = self:GetBottom(); MLB_DB.posAnchor = "BOTTOMLEFT"
        end)
        display:EnableMouseWheel(true)
        display:SetScript("OnMouseWheel", function(self, delta)
            local sc = math.max(0.3, math.min(3.0, self:GetScale() + delta * 0.1))
            self:SetScale(sc); MLB_DB.iconScale = sc
        end)
    else
        display:SetMovable(false); display:EnableMouse(false)
        display:SetScript("OnDragStart", nil); display:SetScript("OnDragStop", nil)
        display:EnableMouseWheel(false); display:SetScript("OnMouseWheel", nil)
        if not lustStartTime then display:Hide() end
    end
    UpdateMoveBtn()
end)

resetPosBtn:SetScript("OnClick", function()
    MLB_DB.posX = 0; MLB_DB.posY = 200; MLB_DB.posAnchor = nil; MLB_DB.iconScale = 1.0
    display:ClearAllPoints(); display:SetPoint("CENTER", UIParent, "CENTER", 0, 200); display:SetScale(1.0)
end)

UpdateMoveBtn()

-- [수정] 시전 알림 미리보기를 블러드 시전 알림 컬럼으로 이동
MakeDivider(box1, -534)

MakeLbl(box1, 8, -540, "테스트", 1, 0.82, 0)

local previewLustBtn = CreateFrame("Button", nil, box1, "UIPanelButtonTemplate")
previewLustBtn:SetSize(COL1_W - 16, 24)
previewLustBtn:SetPoint("TOPLEFT", box1, "TOPLEFT", 8, -556)
previewLustBtn:SetText("▶  시전 알림 미리보기")
-- [수정] 시전 알림 미리보기 버튼 색상: 주황-빨강 계열 (블러드러스트 테마)
previewLustBtn:GetFontString():SetTextColor(1, 0.45, 0.2)

local lustPreviewing = false

local function StopLustPreview()
    lustPreviewing = false
    if previewHandle then StopSound(previewHandle) previewHandle = nil end
    previewingRow = nil
    HideAlert()
    UpdateRows()
    previewLustBtn:SetText("▶  시전 알림 미리보기")
    previewLustBtn:GetFontString():SetTextColor(1, 0.45, 0.2)
end

previewLustBtn:SetScript("OnClick", function()
    if lustPreviewing then
        StopLustPreview()
    else
        if previewHandle then StopSound(previewHandle) previewHandle = nil end
        previewingRow = nil
        local path = GetSoundPath(MLB_DB.sound)
        if path then
            local _, handle = PlaySoundFile(path, MLB_DB.channel)
            previewHandle = handle
        end
        ShowBloodlustAlert(false)
        lustPreviewing = true
        previewLustBtn:SetText("■  재생 중지")
        previewLustBtn:GetFontString():SetTextColor(1, 0.45, 0.2)
        UpdateRows()
    end
end)
--  중간 컬럼: 준비 알림 (ReadyAlert)
-- ════════════════════════════════════════════════════════════
local box2 = MakeBox(sf, COL2_X, COL_TOP, COL2_W, COL_H)
MakeBoxHeader(box2, "블러드 준비 알림")

local infoFS = box2:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
infoFS:SetPoint("TOPLEFT", box2, "TOPLEFT", 8, -30)
infoFS:SetText("소진 디버프 만료 시 알림")
infoFS:SetTextColor(0.6, 0.6, 0.6)

MakeDivider(box2, -44)

local vaCheck = MakeCheck(box2, 4, -50,
    "블 가능시 음성알림",
    function() return ns.readyOpts.VoiceAlert end,
    function(v) ns.readyOpts.VoiceAlert = v; MLB_DB.readyOpts.VoiceAlert = v end)

local coCheck = MakeCheck(box2, 4, -78,
    "블 가능 직업만 작동",
    function() return ns.readyOpts.ClassOnly end,
    function(v) ns.readyOpts.ClassOnly = v; MLB_DB.readyOpts.ClassOnly = v; ns.checkReadyStatus() end)

local clsDesc = box2:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
clsDesc:SetPoint("TOPLEFT", box2, "TOPLEFT", 30, -98)
clsDesc:SetText("|cff888888마법사·주술사·사냥꾼·기원사|r")

MakeDivider(box2, -136)

MakeLbl(box2, 8, -142, "알림 문구", 1, 0.82, 0)

local readyMsgEdit = CreateFrame("EditBox", nil, box2, "InputBoxTemplate")
readyMsgEdit:SetSize(COL2_W - 16, 20)
readyMsgEdit:SetPoint("TOPLEFT", box2, "TOPLEFT", 8, -158)
readyMsgEdit:SetAutoFocus(false)
readyMsgEdit:SetMaxLetters(64)
readyMsgEdit:SetText(ns.READY_MSG)
local function ApplyReadyMsg(self)
    local txt = self:GetText()
    if txt == "" then txt = "블러드 사용 가능" end
    ns.READY_MSG = txt
    MLB_DB.readyMsg = txt
    ns.readyText:SetText(txt)
    self:SetText(txt)
    self:ClearFocus()
end
readyMsgEdit:SetScript("OnEnterPressed", ApplyReadyMsg)
readyMsgEdit:SetScript("OnEscapePressed", function(self)
    self:SetText(ns.READY_MSG); self:ClearFocus()
end)
readyMsgEdit:SetScript("OnTextChanged", function(self)
    local txt = self:GetText()
    if txt == "" then txt = "블러드 사용 가능" end
    ns.READY_MSG = txt
    MLB_DB.readyMsg = txt
    ns.readyText:SetText(txt)
end)

MakeDivider(box2, -182)

MakeLbl(box2, 8, -194, "알림 글씨 크기", 1, 0.82, 0)

local readySizeDisplay = box2:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
readySizeDisplay:SetPoint("TOPLEFT", box2, "TOPLEFT", 8, -212)
readySizeDisplay:SetWidth(COL2_W - 16)
readySizeDisplay:SetJustifyH("CENTER")
readySizeDisplay:SetText(tostring(ns.readyOpts.FontSize or 30))

local readySizeMinusBtn = CreateFrame("Button", nil, box2, "UIPanelButtonTemplate")
readySizeMinusBtn:SetSize(28, 20)
readySizeMinusBtn:SetPoint("TOPLEFT", box2, "TOPLEFT", 8, -210)
readySizeMinusBtn:SetText("-")

local readySizePlusBtn = CreateFrame("Button", nil, box2, "UIPanelButtonTemplate")
readySizePlusBtn:SetSize(28, 20)
readySizePlusBtn:SetPoint("TOPRIGHT", box2, "TOPRIGHT", -8, -210)
readySizePlusBtn:SetText("+")

local function RefreshReadyFontSize()
    local sz = ns.readyOpts.FontSize or 30
    readySizeDisplay:SetText(tostring(sz))
    ns.readyText:SetFont(MLB_FONT, sz, "THICKOUTLINE")
    MLB_DB.readyOpts.FontSize = sz
end

readySizeMinusBtn:SetScript("OnClick", function()
    local sz = math.max(8, (ns.readyOpts.FontSize or 30) - 1)
    ns.readyOpts.FontSize = sz; RefreshReadyFontSize()
end)
readySizePlusBtn:SetScript("OnClick", function()
    local sz = math.min(72, (ns.readyOpts.FontSize or 30) + 1)
    ns.readyOpts.FontSize = sz; RefreshReadyFontSize()
end)

MakeDivider(box2, -236)

MakeLbl(box2, 8, -242, "알림 색상", 1, 0.82, 0)
local readyColorBtn = MakeColorPickerBtn(box2, 8, -258, COL2_W - 32,
    function()
        return (MLB_DB.readyOpts and MLB_DB.readyOpts.Color) or { r=1, g=1, b=1 }
    end,
    function(r, g, b)
        if not MLB_DB.readyOpts then MLB_DB.readyOpts = {} end
        MLB_DB.readyOpts.Color = { r=r, g=g, b=b }
        ns.readyOpts.Color = MLB_DB.readyOpts.Color
        ns.readyText:SetTextColor(r, g, b)
    end
)

MakeDivider(box2, -284)
MakeLbl(box2, 8, -290, "텍스트 위치", 1, 0.82, 0)

local readyMoveMode = false
local rmBtn = CreateFrame("Button", nil, box2, "UIPanelButtonTemplate")
rmBtn:SetSize(86, 22)
rmBtn:SetPoint("TOPLEFT", box2, "TOPLEFT", 8, -306)
rmBtn:SetText("위치 이동")

local rmResetBtn = CreateFrame("Button", nil, box2, "UIPanelButtonTemplate")
rmResetBtn:SetSize(90, 22)
rmResetBtn:SetPoint("LEFT", rmBtn, "RIGHT", 4, 0)
rmResetBtn:SetText("위치 초기화")
rmResetBtn:SetScript("OnClick", function()
    if MLB_Positions then
        MLB_Positions.readyX = nil
        MLB_Positions.readyY = nil
    end
    ready_frame:ClearAllPoints()
    ready_frame:SetPoint("CENTER", UIParent, "CENTER", 0, 100)
end)

local function UpdateRmBtn()
    rmBtn:SetText(readyMoveMode and "|cffff9999이동 중…|r" or "위치 이동")
end

rmBtn:SetScript("OnClick", function()
    readyMoveMode = not readyMoveMode
    if readyMoveMode then
        local c = (MLB_DB.readyOpts and MLB_DB.readyOpts.Color) or { r=1, g=1, b=1 }
        ns.readyText:SetText(ns.READY_MSG); ns.readyText:SetTextColor(c.r, c.g, c.b); ns.readyText:Show()
        ready_frame:SetMovable(true); ready_frame:EnableMouse(true)
        ready_frame:RegisterForDrag("LeftButton")
        ready_frame:SetScript("OnDragStart", ready_frame.StartMoving)
        ready_frame:SetScript("OnDragStop", function(self)
            self:StopMovingOrSizing()
            if MLB_Positions then
                MLB_Positions.readyX = self:GetLeft()
                MLB_Positions.readyY = self:GetBottom()
            end
        end)
    else
        ready_frame:SetMovable(false); ready_frame:EnableMouse(false)
        ready_frame:SetScript("OnDragStart", nil); ready_frame:SetScript("OnDragStop", nil)
        ns.readyText:Hide()
    end
    UpdateRmBtn()
end)

MakeDivider(box2, -334)

MakeLbl(box2, 8, -360, "테스트", 1, 0.82, 0)

-- [수정] 시전 알림 미리보기는 블러드 시전 알림 컬럼(box1)으로 이동됨

local previewReadyBtn = CreateFrame("Button", nil, box2, "UIPanelButtonTemplate")
previewReadyBtn:SetSize(COL2_W - 16, 24)
previewReadyBtn:SetPoint("TOPLEFT", box2, "TOPLEFT", 8, -376)
previewReadyBtn:SetText("▶  준비 알림 미리보기")
-- [수정] 준비 알림 미리보기 버튼 색상: 민트 계열
previewReadyBtn:GetFontString():SetTextColor(0.3, 1, 0.7)

local readyPreviewing = false
local readyPreviewHandle = nil

local function StopReadyPreview()
    readyPreviewing = false
    if readyPreviewHandle then StopSound(readyPreviewHandle) readyPreviewHandle = nil end
    previewReadyBtn:SetText("▶  준비 알림 미리보기")
    previewReadyBtn:GetFontString():SetTextColor(0.3, 1, 0.7)
end

previewReadyBtn:SetScript("OnClick", function()
    if readyPreviewing then
        StopReadyPreview()
        ns.readyText:Hide()
    else
        local mp3path = "Interface\\AddOns\\Moonlight_bloodlust\\" .. ns.READY_MP3
        local _, handle = PlaySoundFile(mp3path, "MASTER")
        readyPreviewHandle = handle
        local c = (MLB_DB.readyOpts and MLB_DB.readyOpts.Color) or { r=1, g=1, b=1 }
        ns.readyText:SetText(ns.READY_MSG)
        ns.readyText:SetTextColor(c.r, c.g, c.b)
        ns.readyText:Show()
        C_Timer.After(ns.readyOpts.ShowTime or 2, function()
            ns.readyText:Hide()
        end)
        readyPreviewing = true
        previewReadyBtn:SetText("■  재생 중지")
        previewReadyBtn:GetFontString():SetTextColor(0.3, 1, 0.7)
    end
end)

-- ════════════════════════════════════════════════════════════
--  우측 컬럼: 전투 타이머
-- ════════════════════════════════════════════════════════════
local box3 = MakeBox(sf, COL3_X, COL_TOP, COL3_W, COL_H)
MakeBoxHeader(box3, "전투 타이머")

local ctInfoFS = box3:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
ctInfoFS:SetPoint("TOPLEFT", box3, "TOPLEFT", 8, -30)
ctInfoFS:SetText("전투 경과 시간 표시")
ctInfoFS:SetTextColor(0.6, 0.6, 0.6)

MakeDivider(box3, -44)

local ctEnCheck = MakeCheck(box3, 4, -50,
    "활성화",
    function() return MLB_DB.combatTimer and MLB_DB.combatTimer.enabled end,
    function(v)
        MLB_DB.combatTimer.enabled = v
        if not v then
            local elapsed2 = combatStartTime and (GetTime() - combatStartTime) or 0
            CombatTimer_Stop(elapsed2)
        end
    end)

local ctAlwaysCheck = MakeCheck(box3, 4, -78,
    "항상 표시",
    function() return MLB_DB.combatTimer and MLB_DB.combatTimer.alwaysShow end,
    function(v)
        MLB_DB.combatTimer.alwaysShow = v
        local c = MLB_DB.combatTimer
        if v and c.enabled and not combatStartTime then
            ct_text:SetText("[00:00]")
            CombatTimer_ApplyVisual()
            ct_frame:Show()
        elseif not v and not combatStartTime then
            if not ctMoveMode then ct_frame:Hide() end
        end
    end)

local ctAlwaysDesc = box3:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
ctAlwaysDesc:SetPoint("TOPLEFT", box3, "TOPLEFT", 30, -98)
ctAlwaysDesc:SetText("|cff888888전투 외에도 표시|r")

MakeDivider(box3, -112)

MakeLbl(box3, 8, -118, "글씨 크기", 1, 0.82, 0)

local ctSizeDisplay = box3:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
ctSizeDisplay:SetPoint("TOPLEFT", box3, "TOPLEFT", 8, -136)
ctSizeDisplay:SetWidth(COL3_W - 16)
ctSizeDisplay:SetJustifyH("CENTER")
ctSizeDisplay:SetText(tostring(MLB_DB.combatTimer and MLB_DB.combatTimer.fontSize or 28))

local ctMinusBtn = CreateFrame("Button", nil, box3, "UIPanelButtonTemplate")
ctMinusBtn:SetSize(28, 20)
ctMinusBtn:SetPoint("TOPLEFT", box3, "TOPLEFT", 8, -134)
ctMinusBtn:SetText("-")

local ctPlusBtn = CreateFrame("Button", nil, box3, "UIPanelButtonTemplate")
ctPlusBtn:SetSize(28, 20)
ctPlusBtn:SetPoint("TOPRIGHT", box3, "TOPRIGHT", -8, -134)
ctPlusBtn:SetText("+")

local function RefreshCTFontSize()
    local sz = MLB_DB.combatTimer.fontSize or 28
    ctSizeDisplay:SetText(tostring(sz))
    ct_text:SetFont(MLB_FONT, sz, "OUTLINE")
    ct_frame:SetSize(math.max(160, sz * 5), sz + 12)
end

ctMinusBtn:SetScript("OnClick", function()
    local sz = math.max(10, (MLB_DB.combatTimer.fontSize or 28) - 1)
    MLB_DB.combatTimer.fontSize = sz; RefreshCTFontSize()
end)
ctPlusBtn:SetScript("OnClick", function()
    local sz = math.min(72, (MLB_DB.combatTimer.fontSize or 28) + 1)
    MLB_DB.combatTimer.fontSize = sz; RefreshCTFontSize()
end)

MakeDivider(box3, -158)

MakeLbl(box3, 8, -164, "글씨 색상", 1, 0.82, 0)
local ctColorBtn = MakeColorPickerBtn(box3, 8, -180, COL3_W - 32,
    function()
        return (MLB_DB.combatTimer and MLB_DB.combatTimer.color) or { r=1, g=1, b=1 }
    end,
    function(r, g, b)
        MLB_DB.combatTimer.color = { r=r, g=g, b=b }
        ct_text:SetTextColor(r, g, b)
    end
)

MakeDivider(box3, -204)

MakeLbl(box3, 8, -210, "타이머 위치", 1, 0.82, 0)

local ctMoveBtn = CreateFrame("Button", nil, box3, "UIPanelButtonTemplate")
ctMoveBtn:SetSize(86, 22)
ctMoveBtn:SetPoint("TOPLEFT", box3, "TOPLEFT", 8, -226)
ctMoveBtn:SetText("위치 이동")

local ctResetBtn = CreateFrame("Button", nil, box3, "UIPanelButtonTemplate")
ctResetBtn:SetSize(76, 22)
ctResetBtn:SetPoint("LEFT", ctMoveBtn, "RIGHT", 4, 0)
ctResetBtn:SetText("초기화")

local function UpdateCTMoveBtn()
    ctMoveBtn:SetText(ctMoveMode and "|cffff9999이동 중…|r" or "위치 이동")
end

ctMoveBtn:SetScript("OnClick", function()
    ctMoveMode = not ctMoveMode
    if ctMoveMode then
        ct_text:SetText(FormatCombatTime(combatStartTime and (GetTime()-combatStartTime) or 0))
        CombatTimer_ApplyVisual()
        ct_frame:Show()
        ct_frame:SetMovable(true); ct_frame:EnableMouse(true)
        ct_frame:RegisterForDrag("LeftButton")
        ct_frame:SetScript("OnDragStart", ct_frame.StartMoving)
        ct_frame:SetScript("OnDragStop", function(self)
            self:StopMovingOrSizing()
            MLB_DB.combatTimer.posX = self:GetLeft()
            MLB_DB.combatTimer.posY = self:GetBottom()
            MLB_DB.combatTimer.posAnchor = "BOTTOMLEFT"
        end)
    else
        ct_frame:SetMovable(false); ct_frame:EnableMouse(false)
        ct_frame:SetScript("OnDragStart", nil); ct_frame:SetScript("OnDragStop", nil)
        local c = MLB_DB.combatTimer
        if not combatStartTime and not (c.alwaysShow and c.enabled) then
            ct_frame:Hide()
        end
    end
    UpdateCTMoveBtn()
end)

ctResetBtn:SetScript("OnClick", function()
    MLB_DB.combatTimer.posX = 0
    MLB_DB.combatTimer.posY = 250
    MLB_DB.combatTimer.posAnchor = nil
    ct_frame:ClearAllPoints()
    ct_frame:SetPoint("CENTER", UIParent, "CENTER", 0, 250)
end)

UpdateCTMoveBtn()

MakeDivider(box3, -254)

MakeLbl(box3, 8, -260, "테스트", 1, 0.82, 0)

local ctPreviewBtn = CreateFrame("Button", nil, box3, "UIPanelButtonTemplate")
ctPreviewBtn:SetSize(COL3_W - 16, 24)
ctPreviewBtn:SetPoint("TOPLEFT", box3, "TOPLEFT", 8, -276)
ctPreviewBtn:SetText("▶  타이머 미리보기")

local ctPreviewing = false
ctPreviewBtn:SetScript("OnClick", function()
    ctPreviewing = not ctPreviewing
    if ctPreviewing then
        CombatTimer_ApplyVisual()
        combatStartTime = GetTime()
        ct_frame:Show()
        if combatTimerTicker then combatTimerTicker:Cancel() end
        combatTimerTicker = C_Timer.NewTicker(0.5, function()
            if combatStartTime then
                ct_text:SetText(FormatCombatTime(GetTime() - combatStartTime))
            end
        end)
        ctPreviewBtn:SetText("■  미리보기 중지")
    else
        CombatTimer_Stop(0)
        ctPreviewBtn:SetText("▶  타이머 미리보기")
    end
end)

-- ── 하단 구분선 + 버전 표시 ─────────────────────────────────
local botDiv = sf:CreateTexture(nil, "ARTWORK")
botDiv:SetColorTexture(0.4, 0.4, 0.4, 0.5)
botDiv:SetPoint("BOTTOMLEFT",  sf, "BOTTOMLEFT",  16, 76)
botDiv:SetPoint("BOTTOMRIGHT", sf, "BOTTOMRIGHT", -16, 76)
botDiv:SetHeight(1)

local resetAllBtn = CreateFrame("Button", nil, sf, "UIPanelButtonTemplate")
resetAllBtn:SetSize(200, 28)
resetAllBtn:SetPoint("BOTTOMLEFT", sf, "BOTTOMLEFT", 16, 42)
resetAllBtn:SetText("|cffff6666전체 설정 초기화|r")
resetAllBtn:SetScript("OnClick", function()
    StaticPopupDialogs["MLB_RESET_ALL_CONFIRM"] = {
        text = "|cffffcc00Moonlight_Bloodlust|r" ..
               "\n\n모든 설정을 기본값으로 초기화합니다." ..
               "\n현재 설정이 모두 사라집니다." ..
               "\n\n계속하시겠습니까?",
        button1 = "초기화",
        button2 = "취소",
        OnAccept = function()
            if MLB_DB then wipe(MLB_DB) end
            if MLB_Positions then wipe(MLB_Positions) end
            MLB_DB.lustLabelText  = "!블러드!"
            MLB_DB.lustLabelColor = { r=1, g=0.3, b=0.3 }
            MLB_DB.timerTextColor = { r=1, g=0.6, b=0.6 }
            MLB_DB.timerBarType   = "dot10"
            MLB_DB.timerTextSize  = 26
            MLB_DB.lustLabelSize  = 13
            MLB_DB.readyMsg       = "소진 끝,블러드 ON"
            MLB_DB.readyOpts      = {
                ClassOnly  = true,
                VoiceAlert = true,
                InRaid     = false,
                FontSize   = 30,
                ShowTime   = 2,
                Color      = { r=1, g=1, b=1 },
            }
            MLB_DB.sound     = nil
            MLB_DB.channel   = "Master"
            MLB_DB.enabled   = true
            MLB_DB.posX      = 0
            MLB_DB.posY      = 200
            MLB_DB.iconScale = 1.0
            MLB_DB.posAnchor = nil
            MLB_DB.alertAnimType = "zoom"
            ReloadUI()
        end,
        timeout = 0,
        whileDead = true,
        hideOnEscape = true,
        showAlert = true,
    }
    StaticPopup_Show("MLB_RESET_ALL_CONFIRM")
end)

local verFS = sf:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
verFS:SetPoint("BOTTOMRIGHT", sf, "BOTTOMRIGHT", -16, 50)
verFS:SetTextColor(0.5, 0.5, 0.5)
verFS:SetText("/mlb test · /mlb ready · /mlb reset")

UpdateRows()

-- ────────────────────────────────────────────────────────────
--  EVENT FRAME (ADDON_LOADED / PLAYER_ENTERING_WORLD)
-- ────────────────────────────────────────────────────────────
local eventFrame = CreateFrame("Frame", "MoonlightBloodlustEventFrame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
eventFrame:RegisterEvent("GROUP_ROSTER_UPDATE")

eventFrame:SetScript("OnEvent", function(self, event, arg1)
    if event == "ADDON_LOADED" and arg1 == "Moonlight_bloodlust" then
        InitDB()

        display:ClearAllPoints()
        if MLB_DB.posAnchor == "BOTTOMLEFT" then
            display:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", MLB_DB.posX, MLB_DB.posY)
        else
            display:SetPoint("CENTER", UIParent, "CENTER", MLB_DB.posX, MLB_DB.posY)
        end
        display:SetScale(MLB_DB.iconScale)

        lustLabel:SetText(MLB_DB.lustLabelText or "")
        lustLabel:SetFont(MLB_FONT, MLB_DB.lustLabelSize or 13, "OUTLINE")
        local llc = MLB_DB.lustLabelColor or { r=1, g=0.3, b=0.3 }
        lustLabel:SetTextColor(llc.r, llc.g, llc.b)
        local ttc2 = MLB_DB.timerTextColor or { r=1, g=0.6, b=0.6 }
        timerText:SetFont(MLB_FONT, MLB_DB.timerTextSize or 26, "OUTLINE")
        timerText:SetTextColor(ttc2.r, ttc2.g, ttc2.b)

        if MLB_Positions and MLB_Positions.readyX then
            ready_frame:ClearAllPoints()
            ready_frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", MLB_Positions.readyX, MLB_Positions.readyY)
        end

        if MLB_DB.readyMsg and MLB_DB.readyMsg ~= "" then
            ns.READY_MSG = MLB_DB.readyMsg
            ns.readyText:SetText(ns.READY_MSG)
            if readyMsgEdit then readyMsgEdit:SetText(ns.READY_MSG) end
        end

        ns.SetupOptionPanels()

        ns.readyText:SetFont(MLB_FONT, ns.readyOpts.FontSize, "THICKOUTLINE")
        local rc = (MLB_DB.readyOpts and MLB_DB.readyOpts.Color) or { r=1, g=1, b=1 }
        ns.readyOpts.Color = rc

        CombatTimer_UpdatePosition()
        CombatTimer_ApplyVisual()
        local c = MLB_DB.combatTimer
        if c and c.enabled and c.alwaysShow then
            ct_text:SetText("[00:00]")
            ct_frame:Show()
        end

        -- [수정] DB 준비 완료 → 이제부터 전투 이벤트 처리 허용
        combatTimerReady = true

        -- [수정] /reload 등으로 전투 중에 로드된 경우: PLAYER_REGEN_DISABLED를 이미 놓쳤으므로
        --        InCombatLockdown()으로 현재 전투 상태를 확인해 타이머를 즉시 시작
        if c and c.enabled and InCombatLockdown() then
            CombatTimer_Start()
        end

        if lblColorBtn  and lblColorBtn.RefreshSwatch  then lblColorBtn.RefreshSwatch()  end
        if timerColorBtn and timerColorBtn.RefreshSwatch then timerColorBtn.RefreshSwatch() end
        if readyColorBtn and readyColorBtn.RefreshSwatch then readyColorBtn.RefreshSwatch() end
        if ctColorBtn   and ctColorBtn.RefreshSwatch   then ctColorBtn.RefreshSwatch()   end

        UpdateRows()

        local ver = C_AddOns.GetAddOnMetadata("Moonlight_bloodlust", "Version")
        print("|cff88ccffMoonlight|r_|cffff3399Bloodlust|r  v" .. (ver or "?") .. "  command: /mlb")
        return
    end

    if event == "PLAYER_ENTERING_WORLD" then
        justZoned   = true
        stableCount = 0
        if readyBfirst then
            readyBfirst = false
        end
        ns.checkReadyStatus()
        -- [수정] 존 이동 후에도 전투 중이면 타이머 재시작 (이전 combatStartTime은 무효)
        if combatTimerReady then
            local c = MLB_DB.combatTimer
            if c and c.enabled and InCombatLockdown() then
                CombatTimer_Start()
            elseif not InCombatLockdown() and combatStartTime then
                -- 전투가 끝났는데 타이머가 남아있는 경우 정리
                CombatTimer_Stop(0)
            end
        end
        return
    end

    if event == "GROUP_ROSTER_UPDATE" then
        ns.checkReadyStatus()
    end
end)

-- ────────────────────────────────────────────────────────────
--  SLASH COMMANDS
-- ────────────────────────────────────────────────────────────
local function ResetDisplayPosition()
    MLB_DB.posX = 0; MLB_DB.posY = 200; MLB_DB.posAnchor = nil; MLB_DB.iconScale = 1.0
    display:ClearAllPoints()
    display:SetPoint("CENTER", UIParent, "CENTER", 0, 200)
    display:SetScale(1.0)
    print("|cff88ccffMoonlight|r_|cffff3399Bloodlust|r  아이콘 위치/크기 초기화 완료")
end

SLASH_MOONLIGHTBLOODLUST1 = "/mlb"
SlashCmdList["MOONLIGHTBLOODLUST"] = function(msg)
    msg = msg and msg:lower() or ""
    if msg == "test" then
        ShowBloodlustAlert()
    elseif msg == "reset" then
        ResetDisplayPosition()
    elseif msg == "ready" then
        fireReadyAlert()
    else
        if sf:IsShown() then
            sf:Hide()
        else
            UpdateRows()
            sf:Show()
        end
    end
end
