-- Portal Pro :: UI/Tab_Ledger.lua
-- Every copper in and out. Refunds are shown as their own row type and never edit the
-- payment they reverse, so the history stays honest.

local ADDON_NAME, PP = ...
local UI, T, U = PP.UI, PP.Theme, PP.U
local L = PP.L

local ROW_HEIGHT = 30

local KIND_STYLE = {
	payment = { color = "positive", label = L["Payment"], sign = "+" },
	manual  = { color = "positive", label = L["Manual"],  sign = "+" },
	refund  = { color = "negative", label = L["Refund"],  sign = "-" },
	reagent = { color = "textMuted", label = L["Reagent"], sign = "-" },
}

--------------------------------------------------------------------------------
-- Export popup
--------------------------------------------------------------------------------

local exportFrame

local function showExport(text)
	if not exportFrame then
		exportFrame = UI:Panel(UIParent, "overlay")
		exportFrame:SetSize(520, 320)
		exportFrame:SetPoint("CENTER")
		exportFrame:SetFrameStrata("DIALOG")
		UI:Border(exportFrame, "borderStrong")
		UI:Shadow(exportFrame, 6)
		UI:MakeDraggable(exportFrame)

		local title = UI:Text(exportFrame, "Export (CSV)", T.fontSize.md, "", "textPrimary")
		title:SetPoint("TOPLEFT", T.space.md, -T.space.md)

		local hint = UI:Text(exportFrame, "Ctrl+A then Ctrl+C to copy.", T.fontSize.sm, "", "textMuted")
		hint:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -2)

		local close = UI:Button(exportFrame, "X", function() exportFrame:Hide() end, "ghost", 26, 26)
		close:SetPoint("TOPRIGHT", -T.space.sm, -T.space.sm)

		local area = UI:TextArea(exportFrame, "", nil, 220)
		area:SetPoint("TOPLEFT", hint, "BOTTOMLEFT", 0, -T.space.sm)
		area:SetPoint("BOTTOMRIGHT", -T.space.md, T.space.md)
		exportFrame.area = area
	end

	exportFrame.area:SetValue(text)
	exportFrame:Show()
	exportFrame.area.edit:SetFocus()
	exportFrame.area.edit:HighlightText()
end

--------------------------------------------------------------------------------
-- Rows
--------------------------------------------------------------------------------

local function createRow(parent)
	local row = UI:Panel(parent, "raised")
	row:SetColorToken("base")

	row.time = UI:Text(row, "", T.fontSize.sm, "", "textMuted")
	row.time:SetPoint("LEFT", T.space.md, 0)
	row.time:SetWidth(78)

	row.customer = UI:Text(row, "", T.fontSize.base, "", "textPrimary")
	row.customer:SetPoint("LEFT", row.time, "RIGHT", T.space.sm, 0)
	row.customer:SetWidth(110)

	row.dest = UI:Text(row, "", T.fontSize.sm, "", "textSecondary")
	row.dest:SetPoint("LEFT", row.customer, "RIGHT", T.space.sm, 0)
	row.dest:SetWidth(100)

	row.kind = UI:Text(row, "", T.fontSize.xs, "", "textMuted")
	row.kind:SetPoint("LEFT", row.dest, "RIGHT", T.space.sm, 0)
	row.kind:SetWidth(64)

	row.amount = UI:Text(row, "", T.fontSize.base, "", "textPrimary", "RIGHT")
	row.amount:SetPoint("RIGHT", -104, 0)
	row.amount:SetWidth(110)

	-- Delete is arm-then-confirm rather than instant: the ledger is append-only precisely
	-- so history can't be quietly rewritten, and a stray click removing a payment would
	-- undermine that. No modal needed — the button just asks.
	row.deleteBtn = UI:Button(row, "X", function(self)
		if not row.entry then return end
		if row.armed then
			PP.Ledger:Delete(row.entry.id)
			row.armed = false
			return
		end
		row.armed = true
		self:SetTextValue("Sure?")
		C_Timer.After(3, function()
			if row.armed then
				row.armed = false
				self:SetTextValue("X")
			end
		end)
	end, "ghost", 44, 20)
	row.deleteBtn:SetPoint("RIGHT", -T.space.md, 0)
	UI:Tooltip(row.deleteBtn, "Delete entry", {
		"Removes this row completely. Click twice to confirm.",
		" ",
		"Use this for a portal that wasn't a sale - a dungeon group, or a",
		"favour for a guildmate. To stop those being logged in the first",
		"place, see Settings -> Tracking.",
	})

	row.refundBtn = UI:Button(row, L["Refund"], function()
		if not row.entry then return end
		PP.Ledger:RefundEntry(row.entry.id)
	end, "ghost", 56, 20)
	row.refundBtn:SetPoint("RIGHT", row.deleteBtn, "LEFT", -2, 0)

	UI:Hoverable(row, "base", "raised")
	return row
end

local function updateRow(row, entry)
	row.entry = entry
	if not entry then return end

	local style = KIND_STYLE[entry.kind] or KIND_STYLE.manual

	row.time:SetText(date("%d %b %H:%M", entry.ts))
	row.customer:SetText(entry.customer or "-")

	local dest = entry.dest and PP.Dest:ByKey(entry.dest)
	row.dest:SetText(dest and dest.name or "-")

	row.kind:SetText(style.label:upper())
	row.kind:SetTextColor(T:C(style.color))

	local sign = (entry.kind == "refund" or entry.kind == "reagent") and -1 or 1
	row.amount:SetText(U.MoneyFull(entry.copper * sign, true))
	row.amount:SetTextColor(T:C(style.color))

	-- Only payments can be refunded, and only up to what hasn't already been returned.
	local refundable = entry.kind == "payment"
		and PP.Ledger:RefundedAmount(entry.id) < entry.copper
	row.refundBtn:SetShown(refundable)

	-- Rows are recycled, so reset any half-armed delete from the previous occupant.
	row.armed = false
	row.deleteBtn:SetTextValue("X")

	-- Always reassign, including to nil: rows are recycled, and a stale note from the
	-- previous occupant would otherwise follow the row down the list.
	UI:Tooltip(row, entry.note and (entry.customer or "") or nil,
		entry.note and { entry.note } or nil)
end

--------------------------------------------------------------------------------
-- Tab
--------------------------------------------------------------------------------

local function build(parent)
	local frame = CreateFrame("Frame", nil, parent)
	frame.filter = "all"
	frame.search = ""

	local bar = CreateFrame("Frame", nil, frame)
	bar:SetPoint("TOPLEFT", T.space.lg, -T.space.lg)
	bar:SetPoint("TOPRIGHT", -T.space.lg, -T.space.lg)
	bar:SetHeight(T.size.buttonHeight)

	local heading = UI:Text(bar, L["Ledger"], T.fontSize.lg, "", "textPrimary")
	heading:SetPoint("LEFT")

	local segmented = UI:Segmented(bar, {
		{ value = "all",     label = "All" },
		{ value = "payment", label = L["Payment"] },
		{ value = "refund",  label = L["Refund"] },
		{ value = "reagent", label = L["Reagent"] },
	}, "all", function(value)
		frame.filter = value
		frame.__def.refresh(frame)
	end)
	segmented:SetPoint("LEFT", heading, "RIGHT", T.space.xl, 0)
	segmented:SetWidth(280)

	local export = UI:Button(bar, "Export", function()
		showExport(PP.Ledger:ExportCSV())
	end, "ghost", 64, 24)
	export:SetPoint("RIGHT")

	local search = UI:Input(bar, "Search customer...", "", function(value)
		frame.search = (value or ""):lower()
		frame.__def.refresh(frame)
	end)
	search.liveUpdate = true
	search:SetPoint("RIGHT", export, "LEFT", -T.space.sm, 0)
	search:SetWidth(150)

	-- Column headers
	local head = CreateFrame("Frame", nil, frame)
	head:SetPoint("TOPLEFT", bar, "BOTTOMLEFT", 0, -T.space.md)
	head:SetPoint("TOPRIGHT", bar, "BOTTOMRIGHT", 0, -T.space.md)
	head:SetHeight(18)

	local function column(text, x, width, justify)
		local fs = UI:Label(head, text)
		fs:SetPoint("LEFT", x, 0)
		fs:SetWidth(width)
		fs:SetJustifyH(justify or "LEFT")
		return fs
	end
	column(L["Time"], T.space.md, 78)
	column(L["Customer"], T.space.md + 86, 110)
	column(L["Destination"], T.space.md + 204, 100)
	column("Type", T.space.md + 312, 64)

	local amountHeader = UI:Label(head, L["Amount"])
	-- Must match row.amount's inset, which allows for the refund and delete buttons.
	amountHeader:SetPoint("RIGHT", -104, 0)
	amountHeader:SetJustifyH("RIGHT")

	local divider = UI:Divider(frame)
	divider:SetPoint("TOPLEFT", head, "BOTTOMLEFT", 0, -2)
	divider:SetPoint("TOPRIGHT", head, "BOTTOMRIGHT", 0, -2)

	local list = UI:ScrollList(frame, ROW_HEIGHT, createRow, updateRow, 1)
	list:SetPoint("TOPLEFT", divider, "BOTTOMLEFT", 0, -T.space.xs)
	list:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -T.space.lg, T.space.xl + 18)
	list:SetEmptyState(L["No transactions yet"], "Payments are captured automatically from trades and mail.")
	frame.list = list

	-- Footer totals
	local footer = CreateFrame("Frame", nil, frame)
	footer:SetPoint("BOTTOMLEFT", T.space.lg, T.space.md)
	footer:SetPoint("BOTTOMRIGHT", -T.space.lg, T.space.md)
	footer:SetHeight(24)

	local footerDivider = UI:Divider(footer)
	footerDivider:SetPoint("TOPLEFT", 0, 6)
	footerDivider:SetPoint("TOPRIGHT", 0, 6)

	frame.footerText = UI:Text(footer, "", T.fontSize.sm, "", "textSecondary")
	frame.footerText:SetPoint("LEFT")

	frame.footerNet = UI:Text(footer, "", T.fontSize.md, "", "textPrimary", "RIGHT")
	frame.footerNet:SetPoint("RIGHT")

	return frame
end

local function refresh(frame)
	local all = PP.Ledger:All()
	local rows = {}

	-- Newest first.
	for i = #all, 1, -1 do
		local entry = all[i]
		local passesFilter = (frame.filter == "all") or (entry.kind == frame.filter)
			or (frame.filter == "payment" and entry.kind == "manual")
		local passesSearch = frame.search == ""
			or ((entry.customer or ""):lower():find(frame.search, 1, true) ~= nil)
		if passesFilter and passesSearch then
			rows[#rows + 1] = entry
		end
	end

	frame.list:SetData(rows)

	local totals = PP.Ledger:Totals()
	frame.footerText:SetText(("%s %s   ·   %s %s   ·   %s %s"):format(
		L["Gross"], U.MoneyFull(totals.gross),
		L["Refunds"], U.MoneyFull(-totals.refunds),
		L["Reagents"], U.MoneyFull(-totals.reagents)))
	frame.footerNet:SetText(L["Net"] .. "  " .. U.MoneyFull(totals.net, true))
	frame.footerNet:SetTextColor(T:C(totals.net >= 0 and "positive" or "negative"))
end

UI:RegisterTab({
	key = "ledger",
	label = L["Ledger"],
	icon = "Interface\\ICONS\\INV_Misc_Coin_01",
	order = 3,
	build = build,
	refresh = refresh,
})
