local ADDON_NAME, ns = ...

-- unify ns across files (prevents split tables)
ns = _G[ADDON_NAME] or ns
_G[ADDON_NAME] = ns

ns.ThreatUI = ns.ThreatUI or {}
local UI = ns.ThreatUI

-- =============================================================
-- pom_threat_ui.lua
-- Threat SETTINGS UI ONLY.
--
-- IMPORTANT:
--  - This file must NOT own threat logic.
--  - Threat logic lives in pom_threat.lua (ns.threat:GetState).
--  - We keep a legacy UI:GetState(unit) wrapper ONLY to avoid old callers crashing.
-- =============================================================

-- =============================================================
-- DEFAULTS (must match pom.lua THREAT_DEFAULTS keys)
-- NOTE: UI uses these ONLY for fallback display and Reset button.
-- InitDB() in pom.lua is the source of truth for migrations.
-- =============================================================
local DEFAULTS = {
    enabled = true,

    colorBarByThreat = true,
    flashAggro       = true,
    flashDuration    = 0,     -- 0=until fixed

    showThreatPerc   = true,
    pctTextMin       = 1,

    gradientStart    = 60,
    gradientEnd      = 100,

    tankModeEnabled  = true,
    checkOffTank     = true,

    groupTintEnabled = false,

    colors = {
        high   = { r=1.0, g=0.0, b=0.0, a=1.0 },
        medium = { r=1.0, g=1.0, b=0.0, a=1.0 },
        low    = { r=0.5, g=0.5, b=0.5, a=1.0 },

        safe   = { r=0.2, g=0.8, b=0.2, a=1.0 },
        ot     = { r=0.6, g=0.2, b=1.0, a=1.0 },

        group  = { r=0.2, g=0.7, b=1.0, a=1.0 },
    },
}

-- -------------------------------------------------------------
-- READ-ONLY DB access
-- -------------------------------------------------------------
local function GetThreatDB()
    if not PlateOMaticDB then return nil end
    if type(PlateOMaticDB.threat) ~= "table" then return nil end
    return PlateOMaticDB.threat
end

local function GetColorsDB()
    local tdb = GetThreatDB()
    if not tdb then return nil end
    if type(tdb.colors) ~= "table" then return nil end
    return tdb.colors
end

-- write helper: create threat bucket ONLY when user clicks
local function EnsureWritableThreatDB()
    if not PlateOMaticDB then return nil end
    PlateOMaticDB.threat = PlateOMaticDB.threat or {}
    PlateOMaticDB.threat.colors = PlateOMaticDB.threat.colors or {}
    return PlateOMaticDB.threat
end

local function ApplyAll(applyAll)
    if applyAll then applyAll() end
end

local function GetCheckLabel(cb)
    if not cb then return nil end
    if cb.Text and cb.Text.SetText then return cb.Text end
    if cb.text and cb.text.SetText then return cb.text end
    local n = cb.GetName and cb:GetName()
    if n and _G[n.."Text"] and _G[n.."Text"].SetText then
        return _G[n.."Text"]
    end
    return nil
end

local function ReadBool(key)
    local tdb = GetThreatDB()
    local v = tdb and tdb[key]
    if v == nil then
        return DEFAULTS[key] and true or false
    end
    return v and true or false
end

local function ReadNumber(key)
    local tdb = GetThreatDB()
    local v = tdb and tdb[key]
    v = tonumber(v)
    if v == nil then
        return tonumber(DEFAULTS[key]) or 0
    end
    return v
end

local function ReadColor(key)
    local cdb = GetColorsDB()
    local c = cdb and cdb[key]
    local d = (DEFAULTS.colors and DEFAULTS.colors[key]) or { r=1, g=1, b=1, a=1 }
    if type(c) ~= "table" then
        return { r=d.r, g=d.g, b=d.b, a=d.a }
    end
    return {
        r = tonumber(c.r) or d.r,
        g = tonumber(c.g) or d.g,
        b = tonumber(c.b) or d.b,
        a = tonumber(c.a) or d.a,
    }
end

-- -------------------------------------------------------------
-- UI widgets
-- -------------------------------------------------------------
local function MakeCheck(parent, label, key, y, applyAll)
    local cb = CreateFrame("CheckButton", nil, parent, "ChatConfigCheckButtonTemplate")
    cb:SetPoint("TOPLEFT", 10, y)

    local t = GetCheckLabel(cb)
    if t then t:SetText(label) end

    local function refresh()
        cb:SetChecked(ReadBool(key))
    end
    refresh()

    cb:SetScript("OnClick", function(self)
        local tdb = EnsureWritableThreatDB()
        if not tdb then return end
        tdb[key] = self:GetChecked() and true or false
        ApplyAll(applyAll)
    end)

    cb._refresh = refresh
    return cb
end

local function MakeSlider(parent, label, key, y, minV, maxV, step, applyAll)
    local row = CreateFrame("Frame", nil, parent)
    row:SetSize(360, 42)
    row:SetPoint("TOPLEFT", 10, y)

    local txt = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    txt:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
    txt:SetText(label)

    local s = CreateFrame("Slider", nil, row, "OptionsSliderTemplate")
    s:SetPoint("TOPLEFT", row, "TOPLEFT", 0, -16)
    s:SetPoint("TOPRIGHT", row, "TOPRIGHT", 0, -16)
    s:SetMinMaxValues(minV, maxV)
    s:SetValueStep(step)
    s:SetObeyStepOnDrag(true)

    local low  = s.Low  or s:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    local high = s.High or s:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    local text = s.Text or s:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")

    if not s.Low then low:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, -2); s.Low = low end
    if not s.High then high:SetPoint("TOPRIGHT", s, "BOTTOMRIGHT", 0, -2); s.High = high end
    if not s.Text then text:SetPoint("TOP", s, "BOTTOM", 0, -2); s.Text = text end

    low:SetText(tostring(minV))
    high:SetText(tostring(maxV))

    local function refresh()
        local v = ReadNumber(key)
        if v < minV then v = minV end
        if v > maxV then v = maxV end
        s:SetValue(v)
        text:SetText(string.format("%.2f", v))
    end
    refresh()

    s:SetScript("OnValueChanged", function(self, value)
        local tdb = EnsureWritableThreatDB()
        if not tdb then return end
        tdb[key] = value
        self.Text:SetText(string.format("%.2f", value))
        ApplyAll(applyAll)
    end)

    row._refresh = refresh
    return row
end

local function MakeColorRow(parent, label, key, y, applyAll)
    local row = CreateFrame("Frame", nil, parent)
    row:SetSize(360, 22)
    row:SetPoint("TOPLEFT", 10, y)

    local txt = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    txt:SetPoint("LEFT", row, "LEFT", 0, 0)
    txt:SetText(label)

    local btn = CreateFrame("Button", nil, row)
    btn:SetSize(18, 18)
    btn:SetPoint("RIGHT", -10, 0)

    local sw = btn:CreateTexture(nil, "BACKGROUND")
    sw:SetAllPoints(btn)

    local function refresh()
        local c = ReadColor(key)
        sw:SetColorTexture(c.r, c.g, c.b, c.a)
    end
    refresh()

    btn:SetScript("OnClick", function()
        local c0 = ReadColor(key)

        ColorPickerFrame:SetupColorPickerAndShow({
            r = c0.r, g = c0.g, b = c0.b,
            opacity = 1 - (c0.a or 1),
            hasOpacity = true,

            swatchFunc = function()
                local r,g,b = ColorPickerFrame:GetColorRGB()
                local a = 1 - (ColorPickerFrame:GetColorAlpha() or 0)

                local tdb = EnsureWritableThreatDB()
                if not tdb then return end
                tdb.colors[key] = tdb.colors[key] or {}
                tdb.colors[key].r, tdb.colors[key].g, tdb.colors[key].b, tdb.colors[key].a = r,g,b,a

                refresh()
                ApplyAll(applyAll)
            end,

            opacityFunc = function()
                local r,g,b = ColorPickerFrame:GetColorRGB()
                local a = 1 - (ColorPickerFrame:GetColorAlpha() or 0)

                local tdb = EnsureWritableThreatDB()
                if not tdb then return end
                tdb.colors[key] = tdb.colors[key] or {}
                tdb.colors[key].r, tdb.colors[key].g, tdb.colors[key].b, tdb.colors[key].a = r,g,b,a

                refresh()
                ApplyAll(applyAll)
            end,

            cancelFunc = function(prev)
                if prev then
                    local a = 1 - (prev.opacity or 0)

                    local tdb = EnsureWritableThreatDB()
                    if not tdb then return end
                    tdb.colors[key] = tdb.colors[key] or {}
                    tdb.colors[key].r, tdb.colors[key].g, tdb.colors[key].b, tdb.colors[key].a = prev.r, prev.g, prev.b, a
                end

                refresh()
                ApplyAll(applyAll)
            end,
        })
    end)

    row._refresh = refresh
    return row
end

-- -------------------------------------------------------------
-- Build embedded panel
-- -------------------------------------------------------------
function UI:Build(parent, applyAll)
    local f = CreateFrame("Frame", nil, parent)
    f:SetAllPoints()

    local title = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    title:SetPoint("TOPLEFT", 10, -20)
    title:SetText("Threat & Aggro")

    f._refreshers = {}

    local y = -40
    local function add(w)
        f._refreshers[#f._refreshers+1] = w
    end

    add(MakeCheck(f, "Enable Threat System", "enabled", y, applyAll)); y = y - 24
    add(MakeCheck(f, "Color Bar by Threat", "colorBarByThreat", y, applyAll)); y = y - 24
    add(MakeCheck(f, "Flash on Aggro", "flashAggro", y, applyAll)); y = y - 24
    add(MakeCheck(f, "Show Threat %", "showThreatPerc", y, applyAll)); y = y - 24
    add(MakeCheck(f, "Group Tint (DPS/Heal)", "groupTintEnabled", y, applyAll)); y = y - 24
    add(MakeCheck(f, "Tank Mode Enabled", "tankModeEnabled", y, applyAll)); y = y - 24
    add(MakeCheck(f, "Check Off-Tank Aggro", "checkOffTank", y, applyAll)); y = y - 30

    add(MakeSlider(f, "Flash Duration (0=until fixed)", "flashDuration", y, 0, 5, 0.1, applyAll)); y = y - 46
    add(MakeSlider(f, "Gradient Start %", "gradientStart", y, 1, 100, 1, applyAll)); y = y - 46
    add(MakeSlider(f, "Gradient End %", "gradientEnd", y, 1, 100, 1, applyAll)); y = y - 46
    add(MakeSlider(f, "Threat Text Min %", "pctTextMin", y, 0, 100, 1, applyAll)); y = y - 50

    local t2 = f:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    t2:SetPoint("TOPLEFT", 10, y)
    t2:SetText("Threat Colors")
    y = y - 26

    local rows = {
        { "High Threat", "high" },
        { "Medium Threat", "medium" },
        { "Low", "low" },
        { "Tank Safe (Have Aggro)", "safe" },
        { "Off-Tank Has Aggro", "ot" },
        { "Group Tint", "group" },
    }

    for _, r in ipairs(rows) do
        add(MakeColorRow(f, r[1], r[2], y, applyAll))
        y = y - 24
    end

    local reset = CreateFrame("Button", nil, f, "UIPanelButtonTemplate")
    reset:SetSize(190, 22)
    reset:SetPoint("TOPLEFT", 10, y - 8)
    reset:SetText("Reset Threat Settings")
    reset:SetScript("OnClick", function()
        local tdb = EnsureWritableThreatDB()
        if not tdb then return end

        -- Apply defaults (do NOT delete bucket)
        for k, v in pairs(DEFAULTS) do
            if k ~= "colors" then
                tdb[k] = (type(v) == "table") and CopyTable(v) or v
            end
        end

        tdb.colors = tdb.colors or {}
        for ck, cv in pairs(DEFAULTS.colors) do
            tdb.colors[ck] = tdb.colors[ck] or {}
            tdb.colors[ck].r, tdb.colors[ck].g, tdb.colors[ck].b, tdb.colors[ck].a = cv.r, cv.g, cv.b, cv.a
        end

        -- refresh UI widgets
        if f._refreshers then
            for _, w in ipairs(f._refreshers) do
                if w and w._refresh then w._refresh() end
                if type(w) == "function" then w() end
            end
        end

        ApplyAll(applyAll)
    end)
end

-- -------------------------------------------------------------
-- Standalone window
-- -------------------------------------------------------------
function UI:ToggleFrame(applyAll)
    if self._frame and self._frame:IsShown() then
        self._frame:Hide()
        return
    end

    if not self._frame then
        local f = CreateFrame("Frame", "POM_ThreatFrame", UIParent, "BasicFrameTemplateWithInset")
        f:SetSize(460, 620)
        f:SetFrameStrata("DIALOG")
        f:SetPoint("CENTER")
        f:SetMovable(true)
        f:EnableMouse(true)
        f:RegisterForDrag("LeftButton")
        f:SetScript("OnDragStart", f.StartMoving)
        f:SetScript("OnDragStop", f.StopMovingOrSizing)
        if f.TitleText then f.TitleText:SetText("POM - Threat") end

        self:Build(f, applyAll)
        self._frame = f
    end

    self._frame:Show()
end

-- -------------------------------------------------------------
-- Legacy compatibility:
-- If ANY old code calls ns.ThreatUI:GetState(unit), delegate to ns.threat:GetState(unit).
-- NO compares, NO math here.
-- -------------------------------------------------------------
function UI:GetState(unit)
    if ns and ns.threat and ns.threat.GetState then
        return ns.threat:GetState(unit)
    end
    return nil, nil, nil, false, 0, 0, "noThreatModule"
end