local ADDON_NAME, ns = ...
local WF = ns.WF
local Util = WF.Util

local Widgets = WF.UI.Widgets
local Theme = WF.UI.Theme
local Tracker = WF.Tracker

local UI = WF.UI.Main or {}
WF.UI.Main = UI

local COMPARE_TARGETS = { "Last Run", "Best (This Farm)", "Best (All)", "Average (Last 5)" }

local function NormalizeCompareTarget(v)
  if not v then return COMPARE_TARGETS[1] end
  v = tostring(v)
  for i = 1, #COMPARE_TARGETS do
    if string.lower(COMPARE_TARGETS[i]) == string.lower(v) then
      return COMPARE_TARGETS[i]
    end
  end
  return COMPARE_TARGETS[1]
end

local function SecondsToClock(sec)
  sec = tonumber(sec) or 0
  local h = math.floor(sec / 3600)
  local m = math.floor((sec % 3600) / 60)
  local s = math.floor(sec % 60)
  if h > 0 then return string.format("%dh %02dm %02ds", h, m, s) end
  return string.format("%dm %02ds", m, s)
end

local function SessionGPH(elapsed, totalValue)
  elapsed = tonumber(elapsed) or 0
  if elapsed <= 0 then return 0 end
  return (tonumber(totalValue) or 0) / elapsed * 3600
end

local function AddCommasToNumberText(v)
  local text = tostring(v or "")
  local function comma(num)
    local sign, int, frac = string.match(num, "^([+-]?)(%d+)(%.?%d*)$")
    if not int then return num end
    local rev = string.reverse(int)
    rev = string.gsub(rev, "(%d%d%d)", "%1,")
    rev = string.reverse(rev)
    rev = string.gsub(rev, "^,", "")
    return sign .. rev .. (frac or "")
  end
  return string.gsub(text, "[+-]?%d+%.?%d*", comma)
end

local function FormatMoneyClean(v)
  if not (Util and Util.FormatMoney) then
    return AddCommasToNumberText(math.floor((tonumber(v) or 0) + 0.5))
  end
  return AddCommasToNumberText(Util:FormatMoney(v))
end

local function FormatPct(v)
  if not v or v ~= v then return "—" end
  local pct = math.floor(v * 100 + 0.5)
  if pct > 0 then return "+" .. tostring(pct) .. "%" end
  if pct < 0 then return tostring(pct) .. "%" end
  return "0%"
end

local function SignedClock(dT)
  dT = tonumber(dT) or 0
  local abs = SecondsToClock(math.abs(dT))
  if dT > 1 then return abs .. " longer"
  elseif dT < -1 then return abs .. " shorter"
  else return "on pace" end
end

function UI:_GetHistorySessions()
  local db = WF.DB and WF.DB.Get and WF.DB:Get()
  if not db then return {} end
  db.history = db.history or {}
  db.history.sessions = db.history.sessions or {}
  return db.history.sessions
end

function UI:_FindBestAll()
  local sessions = self:_GetHistorySessions()
  local best, bestG = nil, 0
  for i = 1, #sessions do
    local s = sessions[i]
    if s then
      local g = SessionGPH(s.elapsed, s.totalValue)
      if g > bestG then bestG = g; best = s end
    end
  end
  return best
end

function UI:_FindBestThisFarm(farmId)
  if farmId == nil then return nil end
  local sessions = self:_GetHistorySessions()
  local best, bestG = nil, 0
  for i = 1, #sessions do
    local s = sessions[i]
    if s and s.farmId == farmId then
      local g = SessionGPH(s.elapsed, s.totalValue)
      if g > bestG then bestG = g; best = s end
    end
  end
  return best
end

function UI:_BuildAvgLastN(n)
  n = tonumber(n) or 5
  if n < 1 then n = 1 end
  local sessions = self:_GetHistorySessions()
  if #sessions == 0 then return nil end
  local take = math.min(n, #sessions)
  local sumE, sumV = 0, 0
  for i = #sessions, (#sessions - take + 1), -1 do
    local s = sessions[i]
    if s then
      sumE = sumE + (tonumber(s.elapsed) or 0)
      sumV = sumV + (tonumber(s.totalValue) or 0)
    end
  end
  return {
    farmName   = "Average (Last " .. tostring(take) .. ")",
    elapsed    = sumE / take,
    totalValue = sumV / take,
  }
end

function UI:_ResolveCompareTarget()
  local target = NormalizeCompareTarget(WF.DB and WF.DB.GetSetting and WF.DB:GetSetting("compareTarget"))
  local sessions = self:_GetHistorySessions()
  if target == "Last Run" then return sessions[#sessions] end
  if target == "Best (This Farm)" then
    local farmId = (Tracker and Tracker.current and Tracker.current.farmId)
      or (self._lastStoppedSession and self._lastStoppedSession.farmId)
    return self:_FindBestThisFarm(farmId)
  end
  if target == "Best (All)" then return self:_FindBestAll() end
  if target == "Average (Last 5)" then return self:_BuildAvgLastN(5) end
  return sessions[#sessions]
end

function UI:CycleCompareTarget()
  if not (WF.DB and WF.DB.GetSetting and WF.DB.SetSetting) then return end
  local cur = NormalizeCompareTarget(WF.DB:GetSetting("compareTarget"))
  local idx = 1
  for i = 1, #COMPARE_TARGETS do
    if COMPARE_TARGETS[i] == cur then idx = i; break end
  end
  idx = (idx % #COMPARE_TARGETS) + 1
  WF.DB:SetSetting("compareTarget", COMPARE_TARGETS[idx])
  self:RefreshComparePanel()
end

function UI:IsCompareOpen()
  return self.compareFrame and self.compareFrame:IsShown() or false
end

function UI:ToggleCompareWindow()
  if not self.compareFrame then
    if self.Create then self:Create() end
  end
  local f = self.compareFrame
  if not f then return end
  if f:IsShown() then
    f:Hide()
  else
    f:Show()
    self:RefreshComparePanel()
  end
end

function UI:InitCompareWindow(compareWin, compareBody)
  if not compareWin or not compareBody then return end

  if self.comparePanel and self._cmp then
    if compareWin.HookScript and not compareWin._wf_cmpHooked then
      local UIRef = self
      compareWin._wf_cmpHooked = true
      compareWin:HookScript("OnShow", function() UIRef:RefreshComparePanel() end)
    end
    return
  end

  local UIRef = self
  if compareWin.HookScript and not compareWin._wf_cmpHooked then
    compareWin._wf_cmpHooked = true
    compareWin:HookScript("OnShow", function() UIRef:RefreshComparePanel() end)
  end

  local accent = (Theme and Theme.accent) or { 0.35, 0.78, 1.00, 1.00 }
  local muted  = (Theme and Theme.muted)  or { 0.65, 0.65, 0.65, 1.00 }
  local text   = (Theme and Theme.text)   or { 0.92, 0.92, 0.92, 1.00 }
  local border = (Theme and Theme.border) or { 0.25, 0.25, 0.25, 0.50 }
  local posClr = accent
  local negClr = muted
  local neuClr = muted

  local panel = Widgets:Panel(compareBody)
  panel:SetPoint("TOPLEFT", 0, 0)
  panel:SetPoint("BOTTOMRIGHT", 0, 0)

  local function GetCurTarget()
    return NormalizeCompareTarget(WF.DB and WF.DB.GetSetting and WF.DB:GetSetting("compareTarget"))
  end

  -- Target selector
  local targetFrame = CreateFrame("Frame", nil, panel)
  targetFrame:SetPoint("TOPLEFT",  panel, "TOPLEFT",  12, -12)
  targetFrame:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -12, -12)
  targetFrame:SetHeight(22)

  local targetLabel = targetFrame:CreateFontString(nil, "OVERLAY", "WF_GameFontDisableSmall")
  targetLabel:SetPoint("LEFT", 0, 0)
  targetLabel:SetTextColor(muted[1], muted[2], muted[3], muted[4] or 1)
  targetLabel:SetText("Comparing against:")

  local targetValue = targetFrame:CreateFontString(nil, "OVERLAY", "WF_GameFontNormalSmall")
  targetValue:SetPoint("LEFT", targetLabel, "RIGHT", 6, 0)
  targetValue:SetTextColor(accent[1], accent[2], accent[3], 1)
  targetValue:SetText(GetCurTarget())

  local bCycleTarget = Widgets:Button(targetFrame, "Change", 68, 20)
  bCycleTarget:SetPoint("RIGHT", 0, 0)
  bCycleTarget:SetScript("OnClick", function() UIRef:CycleCompareTarget() end)
  bCycleTarget:SetScript("OnEnter", function()
    if not GameTooltip then return end
    if Widgets and Widgets.EnsureTooltipSkin then Widgets:EnsureTooltipSkin() end
    GameTooltip:SetOwner(bCycleTarget, "ANCHOR_TOP")
    GameTooltip:ClearLines()
    GameTooltip:AddLine("Compare target", accent[1], accent[2], accent[3], true)
    local cur = GetCurTarget()
    for _, t in ipairs(COMPARE_TARGETS) do
      local c = (t == cur) and accent or muted
      GameTooltip:AddLine((t == cur and "• " or "  ") .. t, c[1], c[2], c[3], true)
    end
    GameTooltip:Show()
  end)
  bCycleTarget:SetScript("OnLeave", function()
    if GameTooltip then GameTooltip:Hide() end
  end)

  local PAD       = 14
  local ROW_H     = 18
  local ROW_G     = 3
  local SEC_ABOVE = 10
  local SEC_BELOW = 4

  -- Returns a frame for chaining.
  -- Keep every section on the same left/right anchors instead of adding PAD each time.
  local function MakeSectionHead(prevFrame, labelText)
    local div = panel:CreateTexture(nil, "ARTWORK")
    div:SetHeight(1)
    div:SetPoint("TOPLEFT",  prevFrame, "BOTTOMLEFT",  0, -SEC_ABOVE)
    div:SetPoint("TOPRIGHT", prevFrame, "BOTTOMRIGHT", 0, -SEC_ABOVE)
    div:SetColorTexture(border[1], border[2], border[3], border[4] or 0.5)

    local f = CreateFrame("Frame", nil, panel)
    f:SetHeight(ROW_H)
    f:SetPoint("TOPLEFT",  div, "BOTTOMLEFT",  0, -SEC_BELOW)
    f:SetPoint("TOPRIGHT", div, "BOTTOMRIGHT", 0, -SEC_BELOW)

    local lbl = f:CreateFontString(nil, "OVERLAY", "WF_GameFontNormalSmall")
    lbl:SetPoint("LEFT", f, "LEFT", 0, 0)
    lbl:SetTextColor(accent[1], accent[2], accent[3], 0.9)
    lbl:SetText(labelText)
    return f
  end

  -- Returns (frame, valueFontString).
  local function MakeStatRow(prevFrame, labelText)
    local f = CreateFrame("Frame", nil, panel)
    f:SetHeight(ROW_H)
    f:SetPoint("TOPLEFT",  prevFrame, "BOTTOMLEFT",  0, -ROW_G)
    f:SetPoint("TOPRIGHT", prevFrame, "BOTTOMRIGHT", 0, -ROW_G)

    local lbl = f:CreateFontString(nil, "OVERLAY", "WF_GameFontDisableSmall")
    lbl:SetPoint("LEFT", f, "LEFT", PAD, 0)
    lbl:SetTextColor(muted[1], muted[2], muted[3], 0.9)
    lbl:SetText(labelText)

    local val = f:CreateFontString(nil, "OVERLAY", "WF_GameFontHighlightSmall")
    val:SetPoint("RIGHT", f, "RIGHT", -PAD, 0)
    val:SetJustifyH("RIGHT")
    val:SetTextColor(text[1], text[2], text[3], 1)
    val:SetText("—")

    return f, val
  end

  local curHead              = MakeSectionHead(targetFrame, "Current Run")
  local curTimeRow, curTime  = MakeStatRow(curHead,    "Elapsed")
  local curValRow,  curVal   = MakeStatRow(curTimeRow, "Value")
  local curGPHRow,  curGPH   = MakeStatRow(curValRow,  "GPH")

  local tarHead              = MakeSectionHead(curGPHRow, "Target Run")
  local tarNameRow, tarName  = MakeStatRow(tarHead,    "Run")
  local tarTimeRow, tarTime  = MakeStatRow(tarNameRow, "Elapsed")
  local tarValRow,  tarVal   = MakeStatRow(tarTimeRow, "Value")
  local tarGPHRow,  tarGPH   = MakeStatRow(tarValRow,  "GPH")

  local dHead                = MakeSectionHead(tarGPHRow, "Delta")
  local dTimeRow, dTime      = MakeStatRow(dHead,    "Δ Elapsed")
  local dValRow,  dVal       = MakeStatRow(dTimeRow, "Δ Value")
  local dGPHRow,  dGPH       = MakeStatRow(dValRow,  "Δ GPH")
  local dPctRow,  dPct       = MakeStatRow(dGPHRow,  "GPH vs target")

  local pBarFrame = CreateFrame("Frame", nil, panel)
  pBarFrame:SetHeight(8)
  pBarFrame:SetPoint("TOPLEFT",  dPctRow, "BOTTOMLEFT",  PAD,  -8)
  pBarFrame:SetPoint("TOPRIGHT", dPctRow, "BOTTOMRIGHT", -PAD, -8)

  local pBarBG = pBarFrame:CreateTexture(nil, "BACKGROUND")
  pBarBG:SetAllPoints()
  pBarBG:SetColorTexture(border[1], border[2], border[3], 0.35)

  local pBarFill = pBarFrame:CreateTexture(nil, "ARTWORK")
  pBarFill:SetPoint("TOPLEFT",    pBarFrame, "TOPLEFT",    0, 0)
  pBarFill:SetPoint("BOTTOMLEFT", pBarFrame, "BOTTOMLEFT", 0, 0)
  pBarFill:SetWidth(1)
  pBarFill:SetColorTexture(accent[1], accent[2], accent[3], 0.85)

  local pBarLabel = panel:CreateFontString(nil, "OVERLAY", "WF_GameFontDisableSmall")
  pBarLabel:SetPoint("TOPLEFT",  pBarFrame, "BOTTOMLEFT",  0, -3)
  pBarLabel:SetPoint("TOPRIGHT", pBarFrame, "BOTTOMRIGHT", 0, -3)
  pBarLabel:SetJustifyH("CENTER")
  pBarLabel:SetTextColor(muted[1], muted[2], muted[3], 0.6)
  pBarLabel:SetText("0% of target GPH")

  local hint = panel:CreateFontString(nil, "OVERLAY", "WF_GameFontDisableSmall")
  hint:SetPoint("BOTTOMLEFT",  panel, "BOTTOMLEFT",  12, 12)
  hint:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -12, 12)
  hint:SetJustifyH("CENTER")
  hint:SetTextColor(muted[1], muted[2], muted[3], 0.45)
  hint:SetText("Updates every 2 seconds while a session is active.")
  hint:SetWordWrap(true)

  self.comparePanel = panel
  self._cmp = {
    targetValue = targetValue,
    curTime = curTime, curVal = curVal, curGPH = curGPH,
    tarName = tarName, tarTime = tarTime, tarVal = tarVal, tarGPH = tarGPH,
    dTime = dTime, dVal = dVal, dGPH = dGPH, dPct = dPct,
    pBarFrame = pBarFrame, pBarFill = pBarFill, pBarLabel = pBarLabel,
    posClr = posClr, negClr = negClr, neuClr = neuClr,
  }

  -- 2-second live ticker
  do
    local ticker = 0
    compareWin:HookScript("OnUpdate", function(_, elapsed)
      ticker = ticker + (elapsed or 0)
      if ticker < 2.0 then return end
      ticker = 0
      if compareWin:IsShown() and UIRef.RefreshComparePanel then
        UIRef:RefreshComparePanel()
      end
    end)
  end
end

function UI:RefreshComparePanel()
  if not (self.comparePanel and self._cmp) then return end
  if not (self.comparePanel.IsShown and self.comparePanel:IsShown()) then return end

  local C = self._cmp
  if not (Tracker and Tracker.GetElapsed and Tracker.GetTotalValue) then return end

  local curElapsed = Tracker:GetElapsed() or 0
  local curTotal   = Tracker:GetTotalValue() or 0
  local curGPH     = SessionGPH(curElapsed, curTotal)

  C.curTime:SetText(SecondsToClock(curElapsed))
  C.curVal:SetText(FormatMoneyClean(curTotal))
  C.curGPH:SetText(FormatMoneyClean(Util:Round(curGPH)))

  if C.targetValue then
    C.targetValue:SetText(NormalizeCompareTarget(WF.DB and WF.DB.GetSetting and WF.DB:GetSetting("compareTarget")))
  end

  local function SetDeltaColor(fs, v)
    if not (fs and fs.SetTextColor) then return end
    v = tonumber(v) or 0
    if v > 0 then
      fs:SetTextColor(C.posClr[1], C.posClr[2], C.posClr[3], C.posClr[4] or 1)
    elseif v < 0 then
      fs:SetTextColor(C.negClr[1], C.negClr[2], C.negClr[3], C.negClr[4] or 1)
    else
      fs:SetTextColor(C.neuClr[1], C.neuClr[2], C.neuClr[3], C.neuClr[4] or 1)
    end
  end

  local function SignedMoney(v)
    v = tonumber(v) or 0
    local s = FormatMoneyClean(math.abs(v))
    if v > 0 then return "+" .. s end
    if v < 0 then return "-" .. s end
    return s
  end

  local function UpdateBar(pct)
    if not (C.pBarFrame and C.pBarFill) then return end
    local barW = C.pBarFrame:GetWidth() or 0
    if barW <= 1 then return end
    local fillW = math.max(1, math.min(barW, math.floor(barW * math.min(pct, 1.5))))
    C.pBarFill:SetWidth(fillW)
    local fillPct = math.floor(pct * 100 + 0.5)
    if C.pBarLabel then C.pBarLabel:SetText(tostring(fillPct) .. "% of target GPH") end
    if pct >= 1.0 then
      C.pBarFill:SetColorTexture(C.posClr[1], C.posClr[2], C.posClr[3], 0.9)
    elseif pct >= 0.8 then
      C.pBarFill:SetColorTexture(C.posClr[1], C.posClr[2], C.posClr[3], 0.65)
    else
      C.pBarFill:SetColorTexture(C.negClr[1], C.negClr[2], C.negClr[3], 0.75)
    end
  end

  local t = self:_ResolveCompareTarget()
  if not t then
    local dash = "—"
    C.tarName:SetText(dash); C.tarTime:SetText(dash)
    C.tarVal:SetText(dash);  C.tarGPH:SetText(dash)
    C.dTime:SetText(dash);   C.dVal:SetText(dash)
    C.dGPH:SetText(dash);    C.dPct:SetText(dash)
    SetDeltaColor(C.dTime, 0); SetDeltaColor(C.dVal, 0)
    SetDeltaColor(C.dGPH,  0); SetDeltaColor(C.dPct, 0)
    UpdateBar(0)
    if C.pBarLabel then C.pBarLabel:SetText("No target run found.") end
    return
  end

  local tName    = t.farmName or t.zone or t.name or "Run"
  local tElapsed = tonumber(t.elapsed)    or 0
  local tTotal   = tonumber(t.totalValue) or 0
  local tGPH     = SessionGPH(tElapsed, tTotal)

  C.tarName:SetText(tName)
  C.tarTime:SetText(SecondsToClock(tElapsed))
  C.tarVal:SetText(FormatMoneyClean(tTotal))
  C.tarGPH:SetText(FormatMoneyClean(Util:Round(tGPH)))

  local dT = curElapsed - tElapsed
  local dV = curTotal   - tTotal
  local dG = curGPH     - tGPH
  local pct = (tGPH > 0) and (curGPH / tGPH) or 0

  C.dTime:SetText(SignedClock(dT))
  C.dVal:SetText(SignedMoney(dV))
  C.dGPH:SetText(SignedMoney(Util:Round(dG)))
  C.dPct:SetText(FormatPct(pct - 1))

  SetDeltaColor(C.dTime, -dT)   -- less time = positive
  SetDeltaColor(C.dVal,   dV)
  SetDeltaColor(C.dGPH,   dG)
  SetDeltaColor(C.dPct,   pct - 1)

  UpdateBar(pct)
end
