-- ------------------------------------------------------------------------------ --
--                                TradeSkillMaster                                --
--                          https://tradeskillmaster.com                          --
--    All Rights Reserved - Detailed license information included with addon.     --
-- ------------------------------------------------------------------------------ --

local _, TSM = ...
local Ledger = TSM.MainUI:NewPackage("Ledger")
local L = TSM.Include("Locale").GetTable()
local TempTable = TSM.Include("Util.TempTable")
local Table = TSM.Include("Util.Table")
local Money = TSM.Include("Util.Money")
local Theme = TSM.Include("Util.Theme")
local Log = TSM.Include("Util.Log")
local ItemInfo = TSM.Include("Service.ItemInfo")
local Settings = TSM.Include("Service.Settings")
local UIElements = TSM.Include("UI.UIElements")
local SECONDS_PER_DAY = 24 * 60 * 60
local private = {
	settings = nil,
	pages = {},
	childPages = {},
	callback = {},
	contextPath = nil,
	contextItemString = nil,
	itemDetailType = "sale",
}
local NUM_TOP_PLAYERS = 3
local PAGE_PATH_SEP = "`"



-- ============================================================================
-- Module Functions
-- ============================================================================

function Ledger.OnInitialize()
	private.settings = Settings.NewView()
		:AddKey("global", "mainUIContext", "ledgerDetailScrollingTable")
	TSM.MainUI.RegisterTopLevelPage(L["Ledger"], private.GetLedgerFrame)
end

function Ledger.RegisterPage(name, callback)
	tinsert(private.pages, name)
	private.callback[name] = callback
end

function Ledger.RegisterChildPage(parentName, childName, callback)
	local path = parentName..PAGE_PATH_SEP..childName
	private.childPages[parentName] = private.childPages[parentName] or {}
	tinsert(private.childPages[parentName], childName)
	private.callback[path] = callback
end

function Ledger.ShowItemDetail(frame, itemString, detailType)
	assert(detailType == "sale" or detailType == "buy")
	private.contextItemString = itemString
	private.itemDetailType = detailType
	frame:SetPath("itemDetail", true)
end



-- ============================================================================
-- Ledger UI
-- ============================================================================

function private.GetLedgerFrame()
	TSM.UI.AnalyticsRecordPathChange("main", "ledger")
	local defaultPage = private.pages[1]
	local frame = UIElements.New("Frame", "ledger")
		:SetLayout("HORIZONTAL")
		:SetBackgroundColor("PRIMARY_BG_ALT")
		:AddChild(UIElements.New("Frame", "navigation")
			:SetLayout("VERTICAL")
			:SetWidth(160)
			:SetPadding(12, 12, 1, 9)
		)
		:AddChild(UIElements.New("Texture", "divider")
			:SetWidth(2)
			:SetTexture("ACTIVE_BG")
		)
		:AddChild(UIElements.New("Frame", "contentFrame")
			:SetLayout("VERTICAL")
			:AddChild(UIElements.New("ViewContainer", "content")
				:SetNavCallback(private.ContentNavCallback)
				:AddPath("itemDetail")
			)
		)
		:SetScript("OnHide", private.NavButtonOnHide)

	local content = frame:GetElement("contentFrame.content")
	local navFrame = frame:GetElement("navigation")
	for _, pageName in ipairs(private.pages) do
		navFrame:AddChild(UIElements.New("Button", pageName)
			:SetHeight(20)
			:SetMargin(0, 0, 8, 0)
			:SetFont("BODY_BODY2_BOLD")
			:SetJustifyH("LEFT")
			:SetContext(pageName)
			:SetText(pageName)
			:SetScript("OnClick", private.NavButtonOnClick)
		)
		content:AddPath(pageName, pageName == defaultPage)
		if private.childPages[pageName] then
			for _, childPageName in ipairs(private.childPages[pageName]) do
				local path = pageName..PAGE_PATH_SEP..childPageName
				navFrame:AddChild(UIElements.New("Button", path)
					:SetHeight(20)
					:SetMargin(9, 0, 8, 0)
					:SetFont("BODY_BODY3_MEDIUM")
					:SetJustifyH("LEFT")
					:SetContext(path)
					:SetText(childPageName)
					:SetScript("OnClick", private.NavButtonOnClick)
				)
				content:AddPath(path, path == defaultPage)
			end
		end
	end
	-- make all the navigation align to the top
	navFrame:AddChild(UIElements.New("Spacer", "spacer"))

	private.UpdateNavFrame(navFrame, defaultPage)
	private.contextPath = L["Inventory"]
	return frame
end

function private.ContentNavCallback(self, path)
	if path == "itemDetail" then
		return private.GetItemDetail()
	else
		return private.callback[path]()
	end
end



-- ============================================================================
-- Local Script Handlers
-- ============================================================================

function private.NavButtonOnClick(button)
	local path = button:GetContext()
	if private.contextPath == path then
		return
	end
	if private.childPages[path] then
		-- select the first child
		path = path..PAGE_PATH_SEP..private.childPages[path][1]
	end

	local ledgerFrame = button:GetParentElement():GetParentElement()
	local contentFrame = ledgerFrame:GetElement("contentFrame")
	local navFrame = ledgerFrame:GetElement("navigation")
	private.UpdateNavFrame(navFrame, path)
	navFrame:Draw()
	contentFrame:GetElement("content"):SetPath(path, private.contextPath ~= path)
	private.contextPath = path
end

function private.NavButtonOnHide(button)
	private.contextPath = nil
end



-- ============================================================================
-- Private Helper Functions
-- ============================================================================

function private.UpdateNavFrame(navFrame, selectedPath)
	local selectedPage = strsplit(PAGE_PATH_SEP, selectedPath)
	for _, pageName in ipairs(private.pages) do
		navFrame:GetElement(pageName):SetTextColor(pageName == selectedPage and "TEXT" or "ACTIVE_BG_ALT")
		if private.childPages[pageName] then
			for _, childPageName in ipairs(private.childPages[pageName]) do
				local path = pageName..PAGE_PATH_SEP..childPageName
				if pageName == selectedPage then
					navFrame:GetElement(path)
						:SetTextColor(path == selectedPath and "INDICATOR" or "TEXT")
						:Show()
				else
					navFrame:GetElement(path):Hide()
				end
			end
		end
	end
end

function private.GetItemDetail()
	local query = TSM.Accounting.Transactions.CreateQuery()
		:Equal("itemString", private.contextItemString)
		:OrderBy("time", false)

	local topPlayersQuantity = TempTable.Acquire()
	local topPlayers = TempTable.Acquire()
	for _, row in query:Iterator() do
		local recordType, otherPlayer, quantity = row:GetFields("type", "otherPlayer", "quantity")
		if recordType == private.itemDetailType then
			if not topPlayersQuantity[otherPlayer] then
				topPlayersQuantity[otherPlayer] = 0
				tinsert(topPlayers, otherPlayer)
			end
			topPlayersQuantity[otherPlayer] = topPlayersQuantity[otherPlayer] + quantity
		end
	end

	Table.SortWithValueLookup(topPlayers, topPlayersQuantity, true)
	local numTopPlayers = min(#topPlayers, NUM_TOP_PLAYERS)
	local topPlayersText = ""
	if numTopPlayers > 0 then
		for i = 1, numTopPlayers do
			local player = topPlayers[i]
			local quantity = topPlayersQuantity[player]
			topPlayers[i] = player..Theme.GetColor("INDICATOR_ALT"):ColorText(" (" .. quantity .. ")")
		end
		topPlayersText = table.concat(topPlayers, ", ", 1, numTopPlayers)
	else
		topPlayersText = L["None"]
	end
	TempTable.Release(topPlayers)
	TempTable.Release(topPlayersQuantity)

	return UIElements.New("Frame", "content")
		:SetLayout("VERTICAL")
		:AddChild(UIElements.New("Frame", "top")
			:SetLayout("VERTICAL")
			:SetPadding(8)
			:SetBackgroundColor("PRIMARY_BG_ALT")
			:AddChild(UIElements.New("Frame", "header")
				:SetLayout("HORIZONTAL")
				:SetHeight(24)
				:AddChild(UIElements.New("ActionButton", "button")
					:SetWidth(64)
					:SetIcon("iconPack.14x14/Chevron/Right@180")
					:SetText(BACK)
					:SetScript("OnClick", private.ItemDetailBackButtonOnClick)
				)
				:AddChild(UIElements.New("Button", "icon")
					:SetSize(24, 24)
					:SetMargin(14, 8, 0, 0)
					:SetBackground(ItemInfo.GetTexture(private.contextItemString))
					:SetTooltip(private.contextItemString)
				)
				:AddChild(UIElements.New("Text", "itemName")
					:SetFont("ITEM_BODY1")
					:SetText(TSM.UI.GetColoredItemName(private.contextItemString))
				)
			)
			:AddChild(UIElements.New("Frame", "content")
				:SetLayout("VERTICAL")
				:SetMargin(0, 0, 8, 0)
				:SetPadding(12, 12, 8, 10)
				:SetBackgroundColor("PRIMARY_BG_ALT", true)
				:SetBorderColor("ACTIVE_BG")
				:AddChild(UIElements.New("Frame", "heading")
					:SetLayout("HORIZONTAL")
					:SetHeight(20)
					:AddChild(UIElements.New("Button", "saleBtn")
						:SetWidth("AUTO")
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY2_BOLD")
						:SetTextColor(private.itemDetailType == "sale" and "INDICATOR" or "ACTIVE_BG_ALT")
						:SetContext("sale")
						:SetText(L["Sale Data"])
						:SetScript("OnClick", private.ItemDetailTabOnClick)
					)
					:AddChild(UIElements.New("Button", "buyBtn")
						:SetWidth("AUTO")
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY2_BOLD")
						:SetTextColor(private.itemDetailType == "buy" and "INDICATOR" or "ACTIVE_BG_ALT")
						:SetContext("buy")
						:SetText(L["Purchase Data"])
						:SetScript("OnClick", private.ItemDetailTabOnClick)
					)
					:AddChild(UIElements.New("Spacer", "spacer"))
					:AddChild(UIElements.New("Text", "total")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY3_MEDIUM")
						:SetJustifyH("RIGHT")
						:SetText(L["Total"])
					)
					:AddChild(UIElements.New("Text", "last7")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY3_MEDIUM")
						:SetJustifyH("RIGHT")
						:SetText(L["Last 7 Days"])
					)
					:AddChild(UIElements.New("Text", "last30")
						:SetWidth(120)
						:SetFont("BODY_BODY3_MEDIUM")
						:SetJustifyH("RIGHT")
						:SetText(L["Last 30 Days"])
					)
				)
				:AddChild(UIElements.New("Frame", "quantity")
					:SetLayout("HORIZONTAL")
					:SetHeight(20)
					:SetMargin(0, 0, 8, 0)
					:AddChild(UIElements.New("Text", "label")
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY3")
						:SetTextColor("TEXT_ALT")
						:SetText(private.itemDetailType == "sale" and L["Quantity Sold:"] or L["Quantity Purchased:"])
					)
					:AddChild(UIElements.New("Text", "total")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(TSM.Accounting.Transactions.GetQuantity(private.contextItemString, nil, private.itemDetailType))
					)
					:AddChild(UIElements.New("Text", "last7")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(TSM.Accounting.Transactions.GetQuantity(private.contextItemString, SECONDS_PER_DAY * 7, private.itemDetailType))
					)
					:AddChild(UIElements.New("Text", "last30")
						:SetWidth(120)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(TSM.Accounting.Transactions.GetQuantity(private.contextItemString, SECONDS_PER_DAY * 30, private.itemDetailType))
					)
				)
				:AddChild(UIElements.New("Frame", "avgPrice")
					:SetLayout("HORIZONTAL")
					:SetHeight(20)
					:SetMargin(0, 0, 8, 0)
					:AddChild(UIElements.New("Text", "label")
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY3")
						:SetTextColor("TEXT_ALT")
						:SetText(L["Average Prices:"])
					)
					:AddChild(UIElements.New("Text", "total")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(Money.ToString(TSM.Accounting.Transactions.GetAveragePrice(private.contextItemString, nil, private.itemDetailType)))
					)
					:AddChild(UIElements.New("Text", "last7")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(Money.ToString(TSM.Accounting.Transactions.GetAveragePrice(private.contextItemString, SECONDS_PER_DAY * 7, private.itemDetailType)))
					)
					:AddChild(UIElements.New("Text", "last30")
						:SetWidth(120)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(Money.ToString(TSM.Accounting.Transactions.GetAveragePrice(private.contextItemString, SECONDS_PER_DAY * 30, private.itemDetailType)))
					)
				)
				:AddChild(UIElements.New("Frame", "totalPrice")
					:SetLayout("HORIZONTAL")
					:SetHeight(20)
					:SetMargin(0, 0, 8, 0)
					:AddChild(UIElements.New("Text", "label")
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY3")
						:SetTextColor("TEXT_ALT")
						:SetText(L["Total Prices:"])
					)
					:AddChild(UIElements.New("Text", "total")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(Money.ToString(TSM.Accounting.Transactions.GetTotalPrice(private.contextItemString, nil, private.itemDetailType)))
					)
					:AddChild(UIElements.New("Text", "last7")
						:SetWidth(120)
						:SetMargin(0, 8, 0, 0)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(Money.ToString(TSM.Accounting.Transactions.GetTotalPrice(private.contextItemString, SECONDS_PER_DAY * 7, private.itemDetailType)))
					)
					:AddChild(UIElements.New("Text", "last30")
						:SetWidth(120)
						:SetFont("TABLE_TABLE1")
						:SetJustifyH("RIGHT")
						:SetText(Money.ToString(TSM.Accounting.Transactions.GetTotalPrice(private.contextItemString, SECONDS_PER_DAY * 30, private.itemDetailType)))
					)
				)
				:AddChild(UIElements.New("Frame", "top")
					:SetLayout("HORIZONTAL")
					:SetHeight(20)
					:SetMargin(0, 0, 8, 0)
					:AddChild(UIElements.New("Text", "label")
						:SetWidth("AUTO")
						:SetMargin(0, 8, 0, 0)
						:SetFont("BODY_BODY3")
						:SetTextColor("TEXT_ALT")
						:SetText(private.itemDetailType == "sale" and L["Top Buyers"]..":" or L["Top Sellers"]..":")
					)
					:AddChild(UIElements.New("Text", "value")
						:SetFont("BODY_BODY3")
						:SetText(topPlayersText)
					)
				)
			)
		)
		:AddChild(UIElements.New("QueryScrollingTable", "scrollingTable")
			:SetSettingsContext(private.settings, "ledgerDetailScrollingTable")
			:GetScrollingTableInfo()
				:NewColumn("activityType")
					:SetTitle(L["Activity Type"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("LEFT")
					:SetTextInfo("type", private.TableGetActivityTypeText)
					:Commit()
				:NewColumn("source")
					:SetTitle(L["Source"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("LEFT")
					:SetTextInfo("source")
					:Commit()
				:NewColumn("buyerSeller")
					:SetTitle(L["Buyer/Seller"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("LEFT")
					:SetTextInfo("otherPlayer")
					:Commit()
				:NewColumn("qty")
					:SetTitle(L["Qty"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("RIGHT")
					:SetTextInfo("quantity")
					:Commit()
				:NewColumn("perItem")
					:SetTitle(L["Per Item"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("RIGHT")
					:SetTextInfo(nil, private.TableGetPerItemText)
					:Commit()
				:NewColumn("totalPrice")
					:SetTitle(L["Total Price"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("RIGHT")
					:SetTextInfo(nil, private.TableGetTotalPriceText)
					:Commit()
				:NewColumn("time")
					:SetTitle(L["Time"])
					:SetFont("ITEM_BODY3")
					:SetJustifyH("RIGHT")
					:SetTextInfo("time", private.TableGetTimeframeText)
					:Commit()
				:Commit()
			:SetQuery(query)
			:SetAutoReleaseQuery(true)
			:SetSelectionDisabled(true)
			:SetScript("OnRowClick", private.ItemDetailScrollingTableOnRowClick)
		)
end

function private.ItemDetailBackButtonOnClick(button)
	button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():SetPath(private.contextPath, true)
end

function private.ItemDetailTabOnClick(button)
	private.itemDetailType = button:GetContext()
	button:GetParentElement():GetParentElement():GetParentElement():GetParentElement():GetParentElement():ReloadContent()
end

function private.ItemDetailScrollingTableOnRowClick(scrollingTable, row, button)
	if button ~= "RightButton" then
		return
	elseif not TSM.Accounting.Transactions.CanDeleteByUUID(row:GetUUID()) then
		Log.PrintUser(L["This record belongs to another account and can only be deleted on that account."])
		return
	end
	local subtitle = nil
	local recordType, itemString, quantity, otherPlayer, price = row:GetFields("type", "itemString", "quantity", "otherPlayer", "price")
	local name = TSM.UI.GetColoredItemName(itemString) or "?"
	local amount = Money.ToString(price * quantity)
	if recordType == "sale" then
		subtitle = format(L["Sold %d of %s to %s for %s"], quantity, name, otherPlayer, amount)
	elseif recordType == "buy" then
		subtitle = format(L["Bought %d of %s from %s for %s"], quantity, name, otherPlayer, amount)
	else
		error("Unexpected Type: "..tostring(recordType))
	end
	scrollingTable:GetBaseElement():ShowConfirmationDialog(L["Delete Record?"], subtitle, private.DeleteRecordConfirmed, row:GetUUID())
end

function private.DeleteRecordConfirmed(uuid)
	TSM.Accounting.Transactions.RemoveRowByUUID(uuid)
end



-- ============================================================================
-- Scrolling Table Helper Functions
-- ============================================================================

function private.TableGetActivityTypeText(recordType)
	if recordType == "sale" then
		return L["Sale"]
	elseif recordType == "buy" then
		return L["Buy"]
	else
		error("Unexpected Type: "..tostring(recordType))
	end
end

function private.TableGetTimeframeText(timestamp)
	return SecondsToTime(time() - timestamp)
end

function private.TableGetTotalPriceText(row)
	return Money.ToString(row:GetField("price") * row:GetField("quantity"))
end

function private.TableGetPerItemText(row)
	return Money.ToString(row:GetField("price"))
end