--[[--------------------------------------------------------------------
  PaladinHolyPower – ui/components/dropdown.lua
  Pixel-perfect dropdown styled to match the panel tabs.

  • Selected row = tab ACTIVE bg + left accent bar + warm text
  • Hover row    = base bg (no darkening) + faint white ink + glossy glass overlay
  • Closed control: readable over textures via subtle text shadow + dual arrow shadows (no boxes)
  • Texture rows: preview swatch sits above row bg/ink (not dimmed)
  • Font is always set before SetText (prevents "Font not set")
  • Scrollbar floats over content (no width lost)
----------------------------------------------------------------------]]

local PHP = _G.PaladinHolyPower or {}; _G.PaladinHolyPower = PHP

local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
local SM  = PHP.SharedMedia -- optional helper

local MEDIA_BASE   = (PHP and PHP.MEDIA) or "Interface\\AddOns\\PaladinHolyPower\\media\\"
local ARROW_DOWN   = MEDIA_BASE .. "icons\\assets\\chevron_down.png"
local ARROW_UP     = MEDIA_BASE .. "icons\\assets\\chevron_up.png"
local WHITE_TEX    = "Interface\\Buttons\\WHITE8x8"
local DEFAULT_FONT = "Fonts\\FRIZQT__.TTF"

-- ===== match panel.lua tab palette =====
local COLOR_TAB_N  = { 0.22, 0.20, 0.14, 1.00 } -- idle (and closed bg)
local COLOR_TAB_A  = { 0.13, 0.12, 0.10, 1.00 } -- active
local TEXT_IDLE    = { 1.00, 1.00, 1.00, 1.00 } -- closed/idle rows
local TEXT_ACTIVE  = { 1.00, 0.95, 0.75, 1.00 } -- selected row text
local BORDER_COLOR = { 0, 0, 0, 1 }

local CONST = {
  WIDTH            = 160,
  HEIGHT           = 22,
  ROW_HEIGHT       = 22,
  MAX_VISIBLE_ROWS = 10,
  BORDER_TEXTURE   = WHITE_TEX,
  BORDER_SIZE      = 1,
  MIN_THUMB_H      = 16,
}

-- Ink levels to match tabs/buttons feel
local INK_ACTIVE = 0.06  -- selected row ink
local INK_HOVER  = 0.04  -- hover ink (non-selected)

-- Read the live accent from panel.lua (fallback matches your panel default alpha)
local function GetAccentRGBA()
  local c = (PHP and PHP.ACCENT_COLOR) or { 0.95, 0.90, 0.55, 0.20 }
  return c[1] or 1, c[2] or 1, c[3] or 1, (c[4] or 0.20)
end

-- ---------- utils ----------
local function clamp(v, lo, hi)
  if v < lo then return lo end
  if v > hi then return hi end
  return v
end

-- ---------- font helpers ----------
local function ResolveFontPath(nameOrPath)
  if not nameOrPath or nameOrPath == "" then return DEFAULT_FONT end
  if type(nameOrPath) == "string" and nameOrPath:find("\\") then
    return nameOrPath
  end
  if LSM then
    local p = LSM:Fetch("font", nameOrPath, true)
    if p then return p end
  end
  if PHP and PHP.fonts then
    if #PHP.fonts > 0 then
      for _, f in ipairs(PHP.fonts) do
        if f.name == nameOrPath and f.path and f.path:find("\\") then
          return f.path
        end
      end
    else
      local p = PHP.fonts[nameOrPath]
      if type(p) == "string" and p:find("\\") then return p end
    end
  end
  return DEFAULT_FONT
end

local function SetFontString(fs, nameOrPath, size, r, g, b, a, flags)
  local path = ResolveFontPath(nameOrPath)
  fs:SetFont(path or DEFAULT_FONT, size or 14, flags or "")
  if r then fs:SetTextColor(r, g, b, a or 1) end
end

-- ---------- SharedMedia statusbar helpers ----------
local function CleanPackPrefix(name)
  return tostring(name or ""):gsub("^%s*%-+%s*", ""):gsub("%s+$","")
end

local function FetchStatusbarPath(name)
  if not name or name == "" or name == "Solid Color" then return nil end
  local tex
  if SM and SM.Fetch then tex = SM:Fetch("statusbar", name) end
  if (not tex) and LSM and LSM.Fetch then tex = LSM:Fetch("statusbar", name) end
  if tex then return tex end
  local clean = CleanPackPrefix(name)
  if clean ~= name then
    if SM and SM.Fetch then tex = SM:Fetch("statusbar", clean) end
    if (not tex) and LSM and LSM.Fetch then tex = LSM:Fetch("statusbar", clean) end
  end
  return tex
end

-- ---------- main ----------
function PHP.CreateDropdown(name, label, items,
                            parent, x, y, selected, setter, width)
  width       = width or CONST.WIDTH
  local value = selected or (items and items[1]) or ""

  local lowerLabel = tostring(label or ""):lower()
  local lowerName  = tostring(name or ""):lower()

  local isTextureDropdown =
    (lowerLabel == "texture") or (lowerName:find("bartexture", 1, true) ~= nil)

  local isFontDropdown =
    (lowerLabel == "font") or (lowerName:find("font", 1, true) ~= nil)

  -- Container (label + button frame)
  local dropdown = CreateFrame("Frame", name .. "Container", parent, "BackdropTemplate")
  dropdown:SetSize(width, CONST.HEIGHT)
  dropdown:SetPoint("TOPLEFT", parent, "TOPLEFT", x or 0, y or 0)
  dropdown:SetBackdrop({
    bgFile   = WHITE_TEX,
    edgeFile = CONST.BORDER_TEXTURE,
    edgeSize = CONST.BORDER_SIZE,
  })
  dropdown:SetBackdropColor(unpack(COLOR_TAB_N))
  dropdown:SetBackdropBorderColor(unpack(BORDER_COLOR))

  -- Label (white)
  dropdown.label = dropdown:CreateFontString(nil, "OVERLAY", "GameFontNormal")
  dropdown.label:SetPoint("BOTTOMLEFT", dropdown, "TOPLEFT", 0, 2)
  dropdown.label:SetText(label or "")
  dropdown.label:SetTextColor(1, 1, 1, 1)

  -- Button (closed state)
  local button = CreateFrame("Button", nil, dropdown, "BackdropTemplate")
  button:SetPoint("TOPLEFT", dropdown, "TOPLEFT", 0, 0)
  button:SetPoint("BOTTOMRIGHT", dropdown, "BOTTOMRIGHT", 0, 0)
  button:SetBackdrop({
    bgFile   = WHITE_TEX,
    edgeFile = CONST.BORDER_TEXTURE,
    edgeSize = CONST.BORDER_SIZE,
  })
  button:SetBackdropColor(unpack(COLOR_TAB_N))
  button:SetBackdropBorderColor(unpack(BORDER_COLOR))

  -- Closed-state text: same look as open rows (white + subtle shadow, no outline)
  button.text = button:CreateFontString(nil, "OVERLAY")
  button.text:SetPoint("LEFT", button, "LEFT", 8, 0)
  button.text:SetShadowOffset(1,-1)
  button.text:SetShadowColor(0,0,0,0.9)

  local function ApplyClosedTextFont(fontNameOrPath)
    local path = ResolveFontPath(fontNameOrPath)
    button.text:SetFont(path or DEFAULT_FONT, 14, "")
    button.text:SetTextColor(TEXT_IDLE[1], TEXT_IDLE[2], TEXT_IDLE[3], TEXT_IDLE[4])
  end

  -- Arrow (white) + dual tiny shadows for texture dropdowns (soft pseudo-outline)
  local arrowShadowNW, arrowShadowSE
  button.arrow = button:CreateTexture(nil, "OVERLAY")
  button.arrow:SetSize(12, 12)
  button.arrow:SetPoint("RIGHT", button, "RIGHT", -8, 0)

  if isTextureDropdown then
    arrowShadowNW = button:CreateTexture(nil, "ARTWORK")
    arrowShadowNW:SetSize(12, 12)
    arrowShadowNW:SetPoint("CENTER", button.arrow, "CENTER", -1, 1)
    arrowShadowNW:SetVertexColor(0, 0, 0, 0.45)

    arrowShadowSE = button:CreateTexture(nil, "ARTWORK")
    arrowShadowSE:SetSize(12, 12)
    arrowShadowSE:SetPoint("CENTER", button.arrow, "CENTER", 1, -1)
    arrowShadowSE:SetVertexColor(0, 0, 0, 0.30)
  end

  local function UpdateArrow(open)
    local tex = open and ARROW_UP or ARROW_DOWN
    button.arrow:SetTexture(tex)
    button.arrow:SetVertexColor(1,1,1,1)
    if arrowShadowNW then arrowShadowNW:SetTexture(tex) end
    if arrowShadowSE then arrowShadowSE:SetTexture(tex) end
  end
  UpdateArrow(false)

  -- Selected texture preview (Texture dropdowns only)
  if isTextureDropdown then
    if not button._PHP_SelectedSwatch then
      local sw = button:CreateTexture(nil, "ARTWORK", nil, 0)
      button._PHP_SelectedSwatch = sw
      sw:SetPoint("TOPLEFT",  button, "TOPLEFT",  1, -1)
      sw:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -1, 1)
    end
  end

  local function UpdateSelectedSwatch()
    if not isTextureDropdown then return end
    local sw = button._PHP_SelectedSwatch
    if not sw then return end
    local path = FetchStatusbarPath(value)
    if path then sw:SetTexture(path); sw:SetVertexColor(1,1,1,1) else sw:SetTexture(nil) end
  end

  local function UpdateButtonText()
    -- Set font FIRST, then text
    if isFontDropdown then
      ApplyClosedTextFont(value)          -- show chosen font with same shadow style
    else
      ApplyClosedTextFont(DEFAULT_FONT)   -- standard readable text
    end
    button.text:SetText(tostring(value or ""))
    UpdateSelectedSwatch()
  end
  UpdateButtonText()

  -- Popup menu
  local menu = CreateFrame("Frame", nil, dropdown, "BackdropTemplate")
  menu:SetBackdrop({
    bgFile   = WHITE_TEX,
    edgeFile = CONST.BORDER_TEXTURE,
    edgeSize = CONST.BORDER_SIZE,
  })
  menu:SetBackdropColor(unpack(COLOR_TAB_N))
  menu:SetBackdropBorderColor(unpack(BORDER_COLOR))
  menu:SetPoint("TOPLEFT", dropdown, "BOTTOMLEFT", 0, -2)
  menu:SetWidth(width)
  menu:SetFrameStrata("TOOLTIP")
  menu:Hide()

  -- Scroll frame (full-size; scrollbar floats)
  local scrollFrame = CreateFrame("ScrollFrame", nil, menu)
  scrollFrame:SetPoint("TOPLEFT", menu, "TOPLEFT", 0, 0)
  scrollFrame:SetPoint("BOTTOMRIGHT", menu, "BOTTOMRIGHT", 0, 0)
  scrollFrame:EnableMouseWheel(true)

  local content = CreateFrame("Frame", nil, scrollFrame)
  scrollFrame:SetScrollChild(content)

  local rows = {}

  local function BuildRows()
    for i = 1, #rows do rows[i]:Hide() end
    wipe(rows)

    local count = items and #items or 0
    content:SetSize(width, count * CONST.ROW_HEIGHT)

    for i, itemName in ipairs(items or {}) do
      local row = CreateFrame("Button", nil, content)
      row:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -((i - 1) * CONST.ROW_HEIGHT))
      row:SetSize(width, CONST.ROW_HEIGHT)
      row.fontName = itemName

      -- solid bg (idle by default)
      row.bg = row:CreateTexture(nil, "BACKGROUND")
      row.bg:SetTexture(WHITE_TEX)
      row.bg:SetAllPoints()
      row.bg:SetVertexColor(unpack(COLOR_TAB_N))

      -- ink highlight (white; above bg, below swatch)
      row.ink = row:CreateTexture(nil, "ARTWORK", nil, 0)
      row.ink:SetTexture(WHITE_TEX)
      row.ink:SetAllPoints(row)
      row.ink:SetVertexColor(1, 1, 1, 1)
      row.ink:SetAlpha(0)                 -- idle

      -- glossy hover overlay (ADD blend so it brightens)
      row.gloss = row:CreateTexture(nil, "OVERLAY", nil, 1)
      row.gloss:SetAllPoints(row)
      row.gloss:SetTexture(WHITE_TEX)
      row.gloss:SetBlendMode("ADD")
      if row.gloss.SetGradient then
        row.gloss:SetGradient("VERTICAL",
          CreateColor(1, 1, 1, 0.08),  -- top sheen
          CreateColor(1, 1, 1, 0.00)) -- fade
      else
        row.gloss:SetVertexColor(1, 1, 1, 0.05)
      end
      row.gloss:Hide()

      -- statusbar swatch (on top of bg+ink; gloss is additive so still vivid)
      if isTextureDropdown then
        row.swatch = row:CreateTexture(nil, "ARTWORK", nil, 2)
        row.swatch:SetPoint("TOPLEFT", row, "TOPLEFT", 2, -2)
        row.swatch:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -2, 2)
        local path = FetchStatusbarPath(itemName)
        if path then row.swatch:SetTexture(path); row.swatch:SetVertexColor(1,1,1,1)
        else row.swatch:SetTexture(nil) end
      end

      -- active selector stripe (front/top) using panel accent
      row.sel = row:CreateTexture(nil, "OVERLAY", nil, 7)
      do
        local ar, ag, ab = GetAccentRGBA()
        row.sel:SetColorTexture(ar, ag, ab, 1) -- opaque stripe like panel tabs
      end
      row.sel:SetPoint("LEFT", row, "LEFT", 0, 0)
      row.sel:SetWidth(2)
      row.sel:SetHeight(CONST.ROW_HEIGHT)
      row.sel:Hide()

      -- text
      row.text = row:CreateFontString(nil, "OVERLAY")
      row.text:SetPoint("LEFT", row, "LEFT", 10, 0)
      if isFontDropdown then
        SetFontString(row.text, itemName, 14, unpack(TEXT_IDLE))
      else
        row.text:SetFont(DEFAULT_FONT, 14, "")
        row.text:SetTextColor(unpack(TEXT_IDLE))
      end
      row.text:SetText(tostring(itemName or ""))
      row.text:SetShadowOffset(1,-1)
      row.text:SetShadowColor(0,0,0,0.9)

      function row:UpdateBg()
        if self._active then
          self.bg:SetVertexColor(unpack(COLOR_TAB_A))
        else
          self.bg:SetVertexColor(unpack(COLOR_TAB_N))
        end
      end

      function row:SetActive(a)
        self._active = not not a
        if self._active then
          self.text:SetTextColor(unpack(TEXT_ACTIVE))
          self.sel:Show()
          self.ink:SetAlpha(INK_ACTIVE)  -- softer active ink
          self.gloss:Hide()              -- active baseline = matte
        else
          self.text:SetTextColor(unpack(TEXT_IDLE))
          self.sel:Hide()
          self.ink:SetAlpha(0)           -- idle
          self.gloss:Hide()
        end
        self:UpdateBg()
      end

      -- White-glass hover (no darkening)
      row:SetScript("OnEnter", function(self)
        self:UpdateBg()                     -- keep base color, no darker bg
        if not self._active then
          self.ink:SetAlpha(INK_HOVER)      -- very light white lift
        end
        self.gloss:Show()                    -- sheen on hover only
      end)

      row:SetScript("OnLeave", function(self)
        self:UpdateBg()
        if not self._active then
          self.ink:SetAlpha(0)
        end
        self.gloss:Hide()
      end)

      row:SetScript("OnMouseDown", function(self)
        if self._active then
          self.ink:SetAlpha(0.10)            -- brief press pop
        else
          self.ink:SetAlpha(0.12)
        end
      end)

      row:SetScript("OnMouseUp", function(self)
        if self._active then
          self.ink:SetAlpha(INK_ACTIVE)
        else
          if MouseIsOver(self) then
            self.ink:SetAlpha(INK_HOVER)    -- stay subtle on hover
          else
            self.ink:SetAlpha(0)
          end
        end
        if MouseIsOver(self) then self.gloss:Show() else self.gloss:Hide() end
      end)

      row:SetScript("OnClick", function()
        value = itemName
        UpdateButtonText()
        menu:Hide()
        UpdateArrow(false)
        if setter then setter(itemName) end
      end)

      row:SetActive(false)
      table.insert(rows, row)
    end
  end

  local function RefreshActive()
    for _, r in ipairs(rows) do
      r:SetActive(r.fontName == value)
    end
  end

  BuildRows()
  RefreshActive()

  ----------------------------------------------------------------------
  -- Floating scrollbar thumb (child of menu; always visible & aligned)
  ----------------------------------------------------------------------
  local function EnsureThumb()
    if menu._thumb then return end
    local thumb = CreateFrame("Frame", nil, menu, "BackdropTemplate")
    thumb:SetBackdrop({ bgFile = WHITE_TEX })
    thumb:SetBackdropColor(1, 0.95, 0.75, 1) -- warm accent tint
    thumb:SetWidth(8)
    thumb:SetPoint("TOPRIGHT", menu, "TOPRIGHT", -2, -2) -- x fixed; y updated
    thumb:Hide()
    thumb:SetFrameStrata(menu:GetFrameStrata())
    thumb:SetFrameLevel(menu:GetFrameLevel() + 50)
    menu._thumb = thumb
  end

  local function UpdateThumbPosition()
    if not menu._thumb or not menu:IsShown() then return end
    local viewH    = menu._viewH or 0
    local contentH = menu._contentH or 0
    local range    = menu._range or 0
    if range <= 0 or viewH <= 0 or contentH <= 0 then
      menu._thumb:Hide()
      return
    end

    -- proportional height, clamped
    local idealH = (viewH * viewH) / contentH
    local thumbH = clamp(idealH, CONST.MIN_THUMB_H, viewH - 4)
    menu._thumb:SetHeight(thumbH)

    local cur   = scrollFrame:GetVerticalScroll() or 0
    local frac  = (range == 0) and 0 or (cur / range)
    local travel = (viewH - thumbH - 4) -- inside 2px padding
    local yOff   = -2 - (frac * travel)
    menu._thumb:ClearAllPoints()
    menu._thumb:SetPoint("TOPRIGHT", menu, "TOPRIGHT", -2, yOff)
    menu._thumb:Show()
  end

  local function RecalcScroll()
    local count    = items and #items or 0
    local viewRows = math.min(CONST.MAX_VISIBLE_ROWS, count)
    local viewH    = viewRows * CONST.ROW_HEIGHT
    local contentH = count * CONST.ROW_HEIGHT

    scrollFrame:SetHeight(viewH)
    menu:SetHeight(viewH + 2)          -- +2 for border
    content:SetSize(width, contentH)

    menu._viewH    = viewH
    menu._contentH = contentH
    menu._range    = math.max(0, contentH - viewH)

    EnsureThumb()
    scrollFrame:SetVerticalScroll(0)

    if menu._range > 0 then
      menu._thumb:Show()
    else
      menu._thumb:Hide()
    end
    UpdateThumbPosition()
  end

  -- Unified mousewheel handler on both menu and scrollFrame
  local function OnWheel(_, delta)
    local range = menu._range or 0
    if range <= 0 then return end
    local cur  = scrollFrame:GetVerticalScroll() or 0
    local step = CONST.ROW_HEIGHT
    local new  = clamp(cur - delta * step, 0, range)
    if new ~= cur then
      scrollFrame:SetVerticalScroll(new)
      UpdateThumbPosition()
    end
  end
  menu:EnableMouseWheel(true)
  menu:SetScript("OnMouseWheel", OnWheel)
  scrollFrame:SetScript("OnMouseWheel", OnWheel)

  -- Also respond to programmatic vertical scrolls
  scrollFrame:SetScript("OnVerticalScroll", function(_, offset)
    scrollFrame:SetVerticalScroll(offset)
    UpdateThumbPosition()
  end)

  -- Toggle popup
  button:SetScript("OnClick", function()
    if menu:IsShown() then
      menu:Hide()
      UpdateArrow(false)
      return
    end
    RecalcScroll()
    RefreshActive()

    local btnLeft, btnBottom = button:GetLeft(), button:GetBottom()
    if btnLeft and btnBottom then
      menu:ClearAllPoints()
      menu:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", btnLeft, btnBottom - 2)
    end
    UpdateArrow(true)
    menu:Show()
    UpdateThumbPosition()
  end)

  -- click-outside to close
  local function GlobalMouseDown()
    if not MouseIsOver(menu) and not MouseIsOver(button) then
      if menu:IsShown() then
        menu:Hide()
        UpdateArrow(false)
      end
    end
  end
  menu:SetScript("OnShow", function()
    menu:RegisterEvent("GLOBAL_MOUSE_DOWN")
    UpdateThumbPosition()
  end)
  menu:SetScript("OnHide", function()
    menu:UnregisterEvent("GLOBAL_MOUSE_DOWN")
    UpdateArrow(false)
    if menu._thumb then menu._thumb:Hide() end
  end)
  menu:SetScript("OnEvent", GlobalMouseDown)

  dropdown.value = button.text
  dropdown.list  = menu
  return dropdown
end