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"
if d.timerBarType == "square10" then d.timerBarType = "ring" end  -- 네모 바 제거로 인한 마이그레이션
d.timerTextSize  = d.timerTextSize  or 26
d.timerTextColor = d.timerTextColor or { r=1, g=0.6, b=0.6 }
d.ringColor      = d.ringColor      or { r=1, g=0.2, b=0.2 }
d.ringRadius     = d.ringRadius     or 55
d.dotColor       = d.dotColor       or { r=1, g=0.27, b=0.27 }
d.dotSize        = d.dotSize        or 11
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"
    if d2.timerBarType == "square10" then d2.timerBarType = "ring" end
    d2.timerTextSize  = d2.timerTextSize  or 26
    d2.timerTextColor = d2.timerTextColor or { r=1, g=0.6, b=0.6 }
    d2.ringColor      = d2.ringColor      or { r=1, g=0.2, b=0.2 }
    d2.ringRadius     = d2.ringRadius     or 55
    d2.dotColor       = d2.dotColor       or { r=1, g=0.27, b=0.27 }
    d2.dotSize        = d2.dotSize        or 11
    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_THICK   = 9
local SEG_N        = 48
local SEG_GAP      = 1.5

-- 현재 반지름 (DB 로드 전에는 기본값 사용)
local function GetRingOuter() return MLB_DB.ringRadius or 55 end
local function GetDisplaySize() return GetRingOuter() * 2 + 10 end

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

local timerFrame = display

local segments = {}

local function RebuildRingSegments()
    -- 기존 세그먼트 프레임 제거
    for i = 1, #segments do
        segments[i].frame:Hide()
        segments[i].frame:SetParent(nil)
    end
    segments = {}

    local RING_OUTER = GetRingOuter()
    local arcPerSeg = (2 * math.pi * RING_OUTER) / SEG_N
    local segLen    = math.max(1, arcPerSeg - SEG_GAP)
    local rc = MLB_DB.ringColor or { r=1, g=0.2, b=0.2 }

    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(rc.r, rc.g, rc.b, 1)

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

RebuildRingSegments()

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)
    local rc = (r and { r=r, g=g, b=b }) or MLB_DB.ringColor or { r=1, g=0.2, b=0.2 }
    for i = 1, SEG_N do
        segments[i].tex:SetColorTexture(rc.r, rc.g, rc.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 function GetDotColorCode()
    local c = MLB_DB.dotColor or { r=1, g=0.27, b=0.27 }
    return string.format("|cff%02x%02x%02x", math.floor(c.r*255), math.floor(c.g*255), math.floor(c.b*255))
end

local dotBar = display:CreateFontString(nil, "OVERLAY")
dotBar:SetFont(MLB_FONT, MLB_DB.dotSize or 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
    local cc = GetDotColorCode()
    local DOT_ON   = cc .. "●|r"
    local DOT_HALF = cc .. "◐|r"
    local DOT_OFF  = "|cff333333●|r"
    if fullDots >= TOTAL then
        currentChar = nil
        fullDots = TOTAL
    elseif remaining <= 0 then
        fullDots = 0
        currentChar = nil
    else
        currentChar = (frac > 0.5) and DOT_ON or DOT_HALF
    end

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

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

local function HideAllBars()
    HideRingBar()
    dotBar: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)
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)

    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)
        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)
            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
        local rc = MLB_DB.ringColor or { r=1, g=0.2, b=0.2 }
        SetRingColor(rc.r, rc.g, rc.b)
        UpdateRingBar(1.0)
    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)
        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 MakeDivider(parent, yOff)
    local d = parent:CreateTexture(nil, "ARTWORK")
    d:SetColorTexture(0.35, 0.35, 0.35, 0.6)
    d:SetPoint("TOPLEFT",  parent, "TOPLEFT",  0, yOff)
    d:SetPoint("TOPRIGHT", parent, "TOPRIGHT",  0, yOff)
    d:SetHeight(1)
end

local function MakeLbl(parent, x, y, text, r, g, b)
    local fs = parent:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    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(22, 22)
    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:SetSize(w, 22)
    btn:SetText("색상 선택")

    local swatch = btn:CreateTexture(nil, "OVERLAY")
    swatch:SetSize(12, 12)
    swatch:SetPoint("RIGHT", btn, "RIGHT", -6, 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

-- 우측 정렬 색상 버튼 (x=right_x, 오른쪽 끝에서 w만큼 왼쪽)
local function MakeColorPickerBtnRight(parent, rightX, y, w, getCol, setCol)
    local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btn:SetPoint("TOPRIGHT", parent, "TOPLEFT", rightX, y)
    btn:SetSize(w, 22)
    btn:SetText("색상 선택")

    local swatch = btn:CreateTexture(nil, "OVERLAY")
    swatch:SetSize(12, 12)
    swatch:SetPoint("RIGHT", btn, "RIGHT", -6, 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,
                hasOpacity = false,
                swatchFunc = function()
                    local r, g, b = ColorPickerFrame:GetColorRGB()
                    setCol(r, g, b); RefreshSwatch()
                end,
                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 function MakeSection(parent, px, py, pw, ph, title)
    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.22)
    f:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.7)

    if title then
        local lbl = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        lbl:SetPoint("TOPLEFT", f, "TOPLEFT", 8, -6)
        lbl:SetText("|cffaaaaaa" .. title .. "|r")
    end
    return f
end

-- ── 드롭다운 (재사용) ────────────────────────────────────────
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.5, 0.45, 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)

    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 SF_W, SF_H = 560, 620
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.45, 0.45, 0.45)
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.35, 0.35, 0.35, 0.5)
topDiv:SetPoint("TOPLEFT",  sf, "TOPLEFT",  16, -38)
topDiv:SetPoint("TOPRIGHT", sf, "TOPRIGHT", -16, -38)
topDiv:SetHeight(1)

-- ════════════════════════════════════════════════════════════
--  탭 시스템
-- ════════════════════════════════════════════════════════════
local TAB_LABELS = {
    "|cffff6633블러드|r 시전 알림",
    "|cff33ff99블러드|r 준비 알림",
    "|cffaaaaffCT|r 전투 타이머",
}
local TAB_Y   = -42
local TAB_H   = 24
local TAB_W   = (SF_W - 32) / 3
local BODY_Y  = TAB_Y - TAB_H - 4
local BODY_H  = SF_H - 42 - TAB_H - 6 - 60  -- 하단 버튼 영역 60px 확보

local tabButtons = {}
local tabPanels  = {}
local activeTab  = 1

local function ShowTab(idx)
    activeTab = idx
    for i, tb in ipairs(tabButtons) do
        if i == idx then
            tb:SetBackdropColor(0.18, 0.18, 0.24, 1)
            tb:SetBackdropBorderColor(0.7, 0.65, 0.3, 0.9)
            local lbl = tb:GetFontString()
            if lbl then lbl:SetTextColor(1, 0.85, 0.2) end
        else
            tb:SetBackdropColor(0.08, 0.08, 0.12, 0.9)
            tb:SetBackdropBorderColor(0.28, 0.28, 0.28, 0.8)
            local lbl = tb:GetFontString()
            if lbl then lbl:SetTextColor(0.65, 0.65, 0.65) end
        end
    end
    for i, panel in ipairs(tabPanels) do
        if i == idx then panel:Show() else panel:Hide() end
    end
end

for i, label in ipairs(TAB_LABELS) do
    local tb = CreateFrame("Button", nil, sf, "BackdropTemplate")
    tb:SetSize(TAB_W, TAB_H)
    tb:SetPoint("TOPLEFT", sf, "TOPLEFT", 16 + (i-1)*TAB_W, TAB_Y)
    tb:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        tile = false, edgeSize = 1,
    })
    tb:SetBackdropColor(0.08, 0.08, 0.12, 0.9)
    tb:SetBackdropBorderColor(0.28, 0.28, 0.28, 0.8)

    local tbLbl = tb:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    tbLbl:SetPoint("CENTER", tb, "CENTER", 0, 0)
    tbLbl:SetText(label)
    tbLbl:SetTextColor(0.65, 0.65, 0.65)

    tb:SetScript("OnClick", function() ShowTab(i) end)
    tabButtons[i] = tb

    -- 탭 패널
    local panel = CreateFrame("Frame", nil, sf, "BackdropTemplate")
    panel:SetPoint("TOPLEFT", sf, "TOPLEFT", 16, BODY_Y)
    panel:SetSize(SF_W - 32, BODY_H)
    panel:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        tile = false, edgeSize = 1,
    })
    panel:SetBackdropColor(0.05, 0.05, 0.08, 0.85)
    panel:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.7)
    panel:Hide()
    tabPanels[i] = panel
end

-- ════════════════════════════════════════════════════════════
--  [탭 1] 시전 알림
-- ════════════════════════════════════════════════════════════
local P = tabPanels[1]
local PW = SF_W - 32   -- 패널 폭
local COL_L = 8        -- 왼쪽 여백
local COL_MID = PW / 2 -- 중앙 분리선

-- 활성화 체크
local enCheck = MakeCheck(P, COL_L, -8,
    "활성화",
    function() return MLB_DB.enabled end,
    function(v) MLB_DB.enabled = v end)

-- ─ 사운드 / 채널 (2열) ──────────────────────────────────────
MakeLbl(P, COL_L, -34, "사운드", 1, 0.82, 0)
local soundCountFS = P:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
soundCountFS:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L + 50, -34)
soundCountFS:SetText("|cff666666(" .. (#ns.SOUNDS - 1) .. "개)|r")

MakeLbl(P, COL_MID + 4, -34, "출력 채널", 1, 0.82, 0)

local HALF_W = COL_MID - COL_L - 4

local soundItems = {}
for _, s in ipairs(ns.SOUNDS) do
    table.insert(soundItems, { label = s.name, value = s.name })
end

local soundDropBtn
soundDropBtn = MakeCustomDropdown(
    P, COL_L, -50, HALF_W, 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
)

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(
    P, COL_MID + 4, -50, HALF_W, 22,
    channelItems,
    function() return MLB_DB.channel or "Master" end,
    function(v) MLB_DB.channel = v end,
    GetChannelName
)

-- ─ 구분선 ───────────────────────────────────────────────────
MakeDivider(P, -80)

-- ─ 라벨 / 애니메이션 (2열) ──────────────────────────────────
MakeLbl(P, COL_L, -88, "타이머 라벨", 1, 0.82, 0)
MakeLbl(P, COL_MID + 4, -88, "등장 애니메이션", 1, 0.82, 0)

local labelEdit = CreateFrame("EditBox", nil, P, "InputBoxTemplate")
labelEdit:SetSize(HALF_W, 20)
labelEdit:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L, -104)
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)

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
local animTypeDropBtn = MakeCustomDropdown(
    P, COL_MID + 4, -104, HALF_W, 22,
    animTypeItems,
    function() return MLB_DB.alertAnimType or "zoom" end,
    function(v) MLB_DB.alertAnimType = v end,
    GetAnimTypeLabel
)

-- ─ 구분선 ───────────────────────────────────────────────────
MakeDivider(P, -132)

-- ─ 타이머 바 스타일 / 글씨 크기 (2열) ──────────────────────
MakeLbl(P, COL_L, -140, "타이머 바 스타일", 1, 0.82, 0)
MakeLbl(P, COL_MID + 4, -140, "초시계 글씨 크기", 1, 0.82, 0)

local RefreshBarOptionPanels  -- 전방 선언 (패널 생성 후 정의됨)

local barTypeItems = {
    { label = "⊙ 원형 바",     value = "ring"  },
    { label = "● 동그라미 바", value = "dot10" },
}
local function GetBarTypeLabel(v)
    for _, it in ipairs(barTypeItems) do
        if it.value == v then return it.label end
    end
    return v
end

local barTypeDropBtn = MakeCustomDropdown(
    P, COL_L, -156, HALF_W, 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 rc = MLB_DB.ringColor or { r=1, g=0.2, b=0.2 }
                SetRingColor(rc.r, rc.g, rc.b)
                UpdateRingBar(remaining / LUST_DURATION)
            else
                UpdateDotBar(remaining)
            end
        elseif moveMode then
            ShowBarForMode()
            if v == "ring" then
                local rc = MLB_DB.ringColor or { r=1, g=0.2, b=0.2 }
                SetRingColor(rc.r, rc.g, rc.b)
                UpdateRingBar(0.75)
            else
                UpdateDotBar(LUST_DURATION * 0.75)
            end
        end
        -- 바 타입에 따라 조건부 옵션 패널 표시 (패널 생성 후 RefreshBarOptionPanels 호출)
        if RefreshBarOptionPanels then RefreshBarOptionPanels() end
    end,
    GetBarTypeLabel
)

-- 글씨 크기 스테퍼
local timerSizeDisplay = P:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
timerSizeDisplay:SetPoint("TOPLEFT", P, "TOPLEFT", COL_MID + 4 + HALF_W/2 - 12, -156)
timerSizeDisplay:SetText(tostring(MLB_DB.timerTextSize or 26))

local timerSizeMinusBtn = CreateFrame("Button", nil, P, "UIPanelButtonTemplate")
timerSizeMinusBtn:SetSize(28, 22)
timerSizeMinusBtn:SetPoint("TOPLEFT", P, "TOPLEFT", COL_MID + 4, -156)
timerSizeMinusBtn:SetText("-")

local timerSizePlusBtn = CreateFrame("Button", nil, P, "UIPanelButtonTemplate")
timerSizePlusBtn:SetSize(28, 22)
timerSizePlusBtn:SetPoint("TOPRIGHT", P, "TOPLEFT", COL_MID + 4 + HALF_W, -156)
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)

-- ─ 바 타입별 조건부 옵션 패널 ──────────────────────────────
-- 원형 바: 색상 + 반지름
local ringOptionsPanel = CreateFrame("Frame", nil, P)
ringOptionsPanel:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L, -186)
ringOptionsPanel:SetSize(PW - 16, 48)

MakeLbl(ringOptionsPanel, 0, 0, "원형 색상", 0.7, 0.9, 1)
MakeLbl(ringOptionsPanel, COL_MID - COL_L + 4, 0, "원형 반지름", 0.7, 0.9, 1)

local ringColorBtn = MakeColorPickerBtn(ringOptionsPanel, 0, -14, HALF_W,
    function()
        return MLB_DB.ringColor or { r=1, g=0.2, b=0.2 }
    end,
    function(r, g, b)
        MLB_DB.ringColor = { r=r, g=g, b=b }
        -- 현재 세그먼트 색상 즉시 반영
        for i = 1, SEG_N do
            segments[i].tex:SetColorTexture(r, g, b, 1)
        end
    end
)

-- 반지름 스테퍼
local ringRadDisplay = ringOptionsPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
ringRadDisplay:SetPoint("TOPLEFT", ringOptionsPanel, "TOPLEFT", COL_MID - COL_L + 4 + HALF_W/2 - 12, -14)
ringRadDisplay:SetText(tostring(MLB_DB.ringRadius or 55))

local ringRadMinusBtn = CreateFrame("Button", nil, ringOptionsPanel, "UIPanelButtonTemplate")
ringRadMinusBtn:SetSize(28, 22)
ringRadMinusBtn:SetPoint("TOPLEFT", ringOptionsPanel, "TOPLEFT", COL_MID - COL_L + 4, -14)
ringRadMinusBtn:SetText("-")

local ringRadPlusBtn = CreateFrame("Button", nil, ringOptionsPanel, "UIPanelButtonTemplate")
ringRadPlusBtn:SetSize(28, 22)
ringRadPlusBtn:SetPoint("TOPRIGHT", ringOptionsPanel, "TOPLEFT", COL_MID - COL_L + 4 + HALF_W, -14)
ringRadPlusBtn:SetText("+")

local function RefreshRingRadius()
    local r = MLB_DB.ringRadius or 55
    ringRadDisplay:SetText(tostring(r))
    local newSize = r * 2 + 10
    display:SetSize(newSize, newSize)
    RebuildRingSegments()
    if MLB_DB.timerBarType == "ring" then
        if lustStartTime then
            local remaining = math.max(LUST_DURATION - (GetTime() - lustStartTime), 0)
            UpdateRingBar(remaining / LUST_DURATION)
        elseif moveMode then
            UpdateRingBar(0.75)
        end
    end
end

ringRadMinusBtn:SetScript("OnClick", function()
    local r = math.max(20, (MLB_DB.ringRadius or 55) - 2)
    MLB_DB.ringRadius = r; RefreshRingRadius()
end)
ringRadPlusBtn:SetScript("OnClick", function()
    local r = math.min(120, (MLB_DB.ringRadius or 55) + 2)
    MLB_DB.ringRadius = r; RefreshRingRadius()
end)

-- 동그라미 바: 색상 + 크기
local dotOptionsPanel = CreateFrame("Frame", nil, P)
dotOptionsPanel:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L, -186)
dotOptionsPanel:SetSize(PW - 16, 48)

MakeLbl(dotOptionsPanel, 0, 0, "동그라미 색상", 0.7, 0.9, 1)
MakeLbl(dotOptionsPanel, COL_MID - COL_L + 4, 0, "동그라미 크기", 0.7, 0.9, 1)

local dotColorBtn = MakeColorPickerBtn(dotOptionsPanel, 0, -14, HALF_W,
    function()
        return MLB_DB.dotColor or { r=1, g=0.27, b=0.27 }
    end,
    function(r, g, b)
        MLB_DB.dotColor = { r=r, g=g, b=b }
        -- 현재 dotBar 즉시 갱신
        if lustStartTime then
            local remaining = math.max(LUST_DURATION - (GetTime() - lustStartTime), 0)
            UpdateDotBar(remaining)
        end
    end
)

-- 점 크기 스테퍼
local dotSizeDisplay = dotOptionsPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
dotSizeDisplay:SetPoint("TOPLEFT", dotOptionsPanel, "TOPLEFT", COL_MID - COL_L + 4 + HALF_W/2 - 12, -14)
dotSizeDisplay:SetText(tostring(MLB_DB.dotSize or 11))

local dotSizeMinusBtn = CreateFrame("Button", nil, dotOptionsPanel, "UIPanelButtonTemplate")
dotSizeMinusBtn:SetSize(28, 22)
dotSizeMinusBtn:SetPoint("TOPLEFT", dotOptionsPanel, "TOPLEFT", COL_MID - COL_L + 4, -14)
dotSizeMinusBtn:SetText("-")

local dotSizePlusBtn = CreateFrame("Button", nil, dotOptionsPanel, "UIPanelButtonTemplate")
dotSizePlusBtn:SetSize(28, 22)
dotSizePlusBtn:SetPoint("TOPRIGHT", dotOptionsPanel, "TOPLEFT", COL_MID - COL_L + 4 + HALF_W, -14)
dotSizePlusBtn:SetText("+")

local function RefreshDotSize()
    local sz = MLB_DB.dotSize or 11
    dotSizeDisplay:SetText(tostring(sz))
    dotBar:SetFont(MLB_FONT, sz, "OUTLINE")
    RepositionTimerTexts()
end

dotSizeMinusBtn:SetScript("OnClick", function()
    local sz = math.max(6, (MLB_DB.dotSize or 11) - 1)
    MLB_DB.dotSize = sz; RefreshDotSize()
end)
dotSizePlusBtn:SetScript("OnClick", function()
    local sz = math.min(30, (MLB_DB.dotSize or 11) + 1)
    MLB_DB.dotSize = sz; RefreshDotSize()
end)

-- 패널 생성 완료 후 전환 함수 정의 (barTypeDropBtn 콜백에서 호출됨)
RefreshBarOptionPanels = function()
    local bt = MLB_DB.timerBarType or "ring"
    if bt == "ring" then
        ringOptionsPanel:Show()
        dotOptionsPanel:Hide()
    else
        ringOptionsPanel:Hide()
        dotOptionsPanel:Show()
    end
end

-- 초기 상태 적용
RefreshBarOptionPanels()

-- ─ 구분선 (바 옵션 패널 아래) ──────────────────────────────

MakeDivider(P, -186)

-- ─ 라벨 색상 / 초시계 색상 (2열) ───────────────────────────
MakeLbl(P, COL_L, -242, "라벨 색상", 1, 0.82, 0)
MakeLbl(P, COL_MID + 4, -242, "초시계 색상", 1, 0.82, 0)

-- 라벨 글씨 크기 (+색상 버튼 왼쪽에)
local lblSizeDisplay = P:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
lblSizeDisplay:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L, -258)

local lblMinusBtn = CreateFrame("Button", nil, P, "UIPanelButtonTemplate")
lblMinusBtn:SetSize(22, 22)
lblMinusBtn:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L, -258)
lblMinusBtn:SetText("-")

local lblPlusBtn = CreateFrame("Button", nil, P, "UIPanelButtonTemplate")
lblPlusBtn:SetSize(22, 22)
lblPlusBtn:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L + 24, -258)
lblPlusBtn:SetText("+")

lblSizeDisplay:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L + 50, -258)
lblSizeDisplay:SetText(tostring(MLB_DB.lustLabelSize or 13))

local function RefreshLabelSize()
    local sz = MLB_DB.lustLabelSize or 13
    lblSizeDisplay:SetText(tostring(sz))
    lustLabel:SetFont(MLB_FONT, sz, "OUTLINE")
    local ds = GetDisplaySize()
    display:SetSize(ds, ds)
    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)

local lblColorBtn = MakeColorPickerBtn(P, COL_L + 82, -258, HALF_W - 86,
    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
)

local timerColorBtn = MakeColorPickerBtn(P, COL_MID + 4, -258, HALF_W,
    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(P, -288)

-- ─ 미리보기 + 위치 이동 통합 버튼 ──────────────────────────
-- lustBtn_state: 0=기본, 1=미리보기+이동 중
local lustBtn_state = 0

local previewLustBtn = CreateFrame("Button", nil, P, "UIPanelButtonTemplate")
previewLustBtn:SetSize(PW - 16, 28)
previewLustBtn:SetPoint("TOPLEFT", P, "TOPLEFT", COL_L, -300)
previewLustBtn:SetText("▶  시전 알림 미리보기")
previewLustBtn:GetFontString():SetTextColor(0.3, 1, 0.7)

local lustPreviewing = false

local function LustBtn_SetNormal()
    lustBtn_state = 0
    previewLustBtn:SetText("▶  시전 알림 미리보기")
    previewLustBtn:GetFontString():SetTextColor(0.3, 1, 0.7)
end

local function LustBtn_StopMove()
    moveMode = false
    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

local function StopLustPreview()
    lustPreviewing = false
    if previewHandle then StopSound(previewHandle) previewHandle = nil end
    previewingRow = nil
    HideAlert()
    LustBtn_SetNormal()
end

previewLustBtn:SetScript("OnClick", function()
    if lustBtn_state == 0 then
        -- 기본 → 미리보기 + 위치 이동 모드 동시 시작
        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
        moveMode = true
        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)
        lustBtn_state = 1
        previewLustBtn:SetText("|cffff9999■  이동 중… (클릭해서 완료)|r")
        previewLustBtn:GetFontString():SetTextColor(1, 1, 1)
    else
        -- 이동 중 → 완료, 기본으로 돌아감
        LustBtn_StopMove()
        lustPreviewing = false
        if previewHandle then StopSound(previewHandle) previewHandle = nil end
        previewingRow = nil
        HideAlert()
        LustBtn_SetNormal()
    end
end)

-- UpdateRows 전방 선언 (드롭다운 동기화)
local UpdateRows
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
    if ringRadDisplay  then ringRadDisplay:SetText(tostring(MLB_DB.ringRadius or 55)) end
    if dotSizeDisplay  then dotSizeDisplay:SetText(tostring(MLB_DB.dotSize or 11)) end
    if RefreshBarOptionPanels then RefreshBarOptionPanels() end
end

-- ════════════════════════════════════════════════════════════
--  [탭 2] 준비 알림
-- ════════════════════════════════════════════════════════════
local P2 = tabPanels[2]

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

local coCheck = MakeCheck(P2, COL_L + COL_MID, -8,
    "블 직업만 작동",
    function() return ns.readyOpts.ClassOnly end,
    function(v) ns.readyOpts.ClassOnly = v; MLB_DB.readyOpts.ClassOnly = v; ns.checkReadyStatus() end)

local clsDesc = P2:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
clsDesc:SetPoint("TOPLEFT", P2, "TOPLEFT", COL_L + COL_MID + 24, -28)
clsDesc:SetText("|cff666666마법사·주술사·사냥꾼·기원사|r")

-- ─ 구분선 ───────────────────────────────────────────────────
MakeDivider(P2, -42)

-- ─ 알림 문구 ────────────────────────────────────────────────
MakeLbl(P2, COL_L, -50, "알림 문구", 1, 0.82, 0)

local readyMsgEdit = CreateFrame("EditBox", nil, P2, "InputBoxTemplate")
readyMsgEdit:SetSize(PW - 16, 20)
readyMsgEdit:SetPoint("TOPLEFT", P2, "TOPLEFT", COL_L, -66)
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(P2, -96)

-- ─ 글씨 크기 / 색상 (2열) ───────────────────────────────────
MakeLbl(P2, COL_L, -104, "알림 글씨 크기", 1, 0.82, 0)
MakeLbl(P2, COL_MID + 4, -104, "알림 색상", 1, 0.82, 0)

local readySizeDisplay = P2:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
readySizeDisplay:SetPoint("TOPLEFT", P2, "TOPLEFT", COL_L + 50, -120)
readySizeDisplay:SetText(tostring(ns.readyOpts.FontSize or 30))

local readySizeMinusBtn = CreateFrame("Button", nil, P2, "UIPanelButtonTemplate")
readySizeMinusBtn:SetSize(22, 22)
readySizeMinusBtn:SetPoint("TOPLEFT", P2, "TOPLEFT", COL_L, -120)
readySizeMinusBtn:SetText("-")

local readySizePlusBtn = CreateFrame("Button", nil, P2, "UIPanelButtonTemplate")
readySizePlusBtn:SetSize(22, 22)
readySizePlusBtn:SetPoint("TOPLEFT", P2, "TOPLEFT", COL_L + 24, -120)
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)

local readyColorBtn = MakeColorPickerBtn(P2, COL_MID + 4, -120, HALF_W,
    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(P2, -150)

-- ─ 미리보기 + 위치 이동 통합 버튼 ──────────────────────────
-- readyBtn_state: 0=기본, 1=미리보기+이동 중
local readyBtn_state = 0
local readyMoveMode = false

local previewReadyBtn = CreateFrame("Button", nil, P2, "UIPanelButtonTemplate")
previewReadyBtn:SetSize(PW - 16, 28)
previewReadyBtn:SetPoint("TOPLEFT", P2, "TOPLEFT", COL_L, -162)
previewReadyBtn:SetText("▶  준비 알림 미리보기")
previewReadyBtn:GetFontString():SetTextColor(0.3, 1, 0.7)

local readyPreviewing = false
local readyPreviewHandle = nil

local function ReadyBtn_SetNormal()
    readyBtn_state = 0
    previewReadyBtn:SetText("▶  준비 알림 미리보기")
    previewReadyBtn:GetFontString():SetTextColor(0.3, 1, 0.7)
end

local function ReadyBtn_StopMove()
    readyMoveMode = false
    ready_frame:SetMovable(false); ready_frame:EnableMouse(false)
    ready_frame:SetScript("OnDragStart", nil); ready_frame:SetScript("OnDragStop", nil)
    ns.readyText:Hide()
end

local function StopReadyPreview()
    readyPreviewing = false
    if readyPreviewHandle then StopSound(readyPreviewHandle) readyPreviewHandle = nil end
    ReadyBtn_SetNormal()
end

previewReadyBtn:SetScript("OnClick", function()
    if readyBtn_state == 0 then
        -- 기본 → 미리보기 + 위치 이동 모드 동시 시작
        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()
        readyPreviewing = true
        readyMoveMode = true
        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)
        readyBtn_state = 1
        previewReadyBtn:SetText("|cffff9999■  이동 중… (클릭해서 완료)|r")
        previewReadyBtn:GetFontString():SetTextColor(1, 1, 1)
    else
        -- 이동 중 → 완료, 기본으로 돌아감
        ReadyBtn_StopMove()
        readyPreviewing = false
        if readyPreviewHandle then StopSound(readyPreviewHandle) readyPreviewHandle = nil end
        ReadyBtn_SetNormal()
    end
end)

-- ════════════════════════════════════════════════════════════
--  [탭 3] 전투 타이머
-- ════════════════════════════════════════════════════════════
local P3 = tabPanels[3]

local ctEnCheck = MakeCheck(P3, COL_L, -8,
    "활성화",
    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(P3, COL_L + COL_MID, -8,
    "항상 표시",
    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 = P3:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
ctAlwaysDesc:SetPoint("TOPLEFT", P3, "TOPLEFT", COL_L + COL_MID + 24, -28)
ctAlwaysDesc:SetText("|cff666666전투 외에도 표시|r")

-- ─ 구분선 ───────────────────────────────────────────────────
MakeDivider(P3, -42)

-- ─ 글씨 크기 / 색상 (2열) ───────────────────────────────────
MakeLbl(P3, COL_L, -50, "글씨 크기", 1, 0.82, 0)
MakeLbl(P3, COL_MID + 4, -50, "글씨 색상", 1, 0.82, 0)

local ctSizeDisplay = P3:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
ctSizeDisplay:SetPoint("TOPLEFT", P3, "TOPLEFT", COL_L + 50, -66)
ctSizeDisplay:SetText(tostring(MLB_DB.combatTimer and MLB_DB.combatTimer.fontSize or 28))

local ctMinusBtn = CreateFrame("Button", nil, P3, "UIPanelButtonTemplate")
ctMinusBtn:SetSize(22, 22)
ctMinusBtn:SetPoint("TOPLEFT", P3, "TOPLEFT", COL_L, -66)
ctMinusBtn:SetText("-")

local ctPlusBtn = CreateFrame("Button", nil, P3, "UIPanelButtonTemplate")
ctPlusBtn:SetSize(22, 22)
ctPlusBtn:SetPoint("TOPLEFT", P3, "TOPLEFT", COL_L + 24, -66)
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)

local ctColorBtn = MakeColorPickerBtn(P3, COL_MID + 4, -66, HALF_W,
    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(P3, -96)

-- ─ 미리보기 + 위치 이동 통합 버튼 ──────────────────────────
-- ctBtn_state: 0=기본, 1=미리보기+이동 중
local ctBtn_state = 0

local ctPreviewBtn = CreateFrame("Button", nil, P3, "UIPanelButtonTemplate")
ctPreviewBtn:SetSize(PW - 16, 28)
ctPreviewBtn:SetPoint("TOPLEFT", P3, "TOPLEFT", COL_L, -108)
ctPreviewBtn:SetText("▶  타이머 미리보기")
ctPreviewBtn:GetFontString():SetTextColor(0.3, 1, 0.7)

local ctPreviewing = false

local function CTBtn_SetNormal()
    ctBtn_state = 0
    ctPreviewBtn:SetText("▶  타이머 미리보기")
    ctPreviewBtn:GetFontString():SetTextColor(0.3, 1, 0.7)
end

local function CTBtn_StopMove()
    ctMoveMode = false
    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

ctPreviewBtn:SetScript("OnClick", function()
    if ctBtn_state == 0 then
        -- 기본 → 미리보기 + 위치 이동 모드 동시 시작
        ctPreviewing = true
        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)
        ctMoveMode = true
        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)
        ctBtn_state = 1
        ctPreviewBtn:SetText("|cffff9999■  이동 중… (클릭해서 완료)|r")
        ctPreviewBtn:GetFontString():SetTextColor(1, 1, 1)
    else
        -- 이동 중 → 완료, 기본으로 돌아감
        CTBtn_StopMove()
        ctPreviewing = false
        if combatTimerTicker then combatTimerTicker:Cancel(); combatTimerTicker = nil end
        CombatTimer_Stop(0)
        CTBtn_SetNormal()
    end
end)



-- ════════════════════════════════════════════════════════════
--  하단 버튼 영역
-- ════════════════════════════════════════════════════════════
local botDiv = sf:CreateTexture(nil, "ARTWORK")
botDiv:SetColorTexture(0.35, 0.35, 0.35, 0.5)
botDiv:SetPoint("BOTTOMLEFT",  sf, "BOTTOMLEFT",  16, 56)
botDiv:SetPoint("BOTTOMRIGHT", sf, "BOTTOMRIGHT", -16, 56)
botDiv:SetHeight(1)

local resetAllBtn = CreateFrame("Button", nil, sf, "UIPanelButtonTemplate")
resetAllBtn:SetSize(160, 26)
resetAllBtn:SetPoint("BOTTOMLEFT", sf, "BOTTOMLEFT", 16, 24)
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"
            MLB_DB.ringColor  = { r=1, g=0.2, b=0.2 }
            MLB_DB.ringRadius = 55
            MLB_DB.dotColor   = { r=1, g=0.27, b=0.27 }
            MLB_DB.dotSize    = 11
            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, 30)
verFS:SetTextColor(0.45, 0.45, 0.45)
verFS:SetText("/mlb test · /mlb ready · /mlb reset")

-- 탭 1이 기본으로 열린 상태
ShowTab(1)

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)

        -- 반지름 및 dotSize 적용
        dotBar:SetFont(MLB_FONT, MLB_DB.dotSize or 11, "OUTLINE")
        local ds = GetDisplaySize()
        display:SetSize(ds, ds)
        RebuildRingSegments()

        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
        if ringColorBtn  and ringColorBtn.RefreshSwatch  then ringColorBtn.RefreshSwatch()  end
        if dotColorBtn   and dotColorBtn.RefreshSwatch   then dotColorBtn.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
