local addonName = ...
local SlickUI = _G.SlickUI
if not SlickUI then return end

local VendorData = {
	SchemaVersion = 1,
	-- Development seed records belong here. Keep these immutable at runtime;
	-- merchant observations are stored separately in SlickUIDB.
	Seed = {},
}
SlickUI.CraftingVendorData = VendorData

local function CraftingSettings()
	SlickUI.DB.crafting = SlickUI.DB.crafting or {}
	SlickUI.DB.crafting.vendorItems = SlickUI.DB.crafting.vendorItems or {}
	return SlickUI.DB.crafting
end

local function BuildNumber()
	local build = select(4, GetBuildInfo())
	return tonumber(build) or 0
end

local function MerchantItemInfo(index)
	if C_MerchantFrame and type(C_MerchantFrame.GetItemInfo) == "function" then
		return C_MerchantFrame.GetItemInfo(index)
	end
	local name, texture, price, stackCount, numAvailable, isPurchasable, _, extendedCost = GetMerchantItemInfo(index)
	return {
		name = name,
		texture = texture,
		price = price,
		stackCount = stackCount,
		numAvailable = numAvailable,
		isPurchasable = isPurchasable,
		extendedCost = extendedCost,
	}
end

function VendorData:GetObservedItems()
	local settings = CraftingSettings()
	local items = settings.vendorItems
	for itemID, record in pairs(items) do
		if record == true then
			items[itemID] = {
				confirmed = true,
				source = "legacy-observation",
			}
		end
	end
	settings.vendorDataSchema = self.SchemaVersion
	return items
end

function VendorData:Get(itemID)
	itemID = tonumber(itemID)
	if not itemID then return nil end
	return self:GetObservedItems()[itemID] or self.Seed[itemID]
end

function VendorData:IsVendorItem(itemID)
	local record = self:Get(itemID)
	-- Limited-stock and alternate-currency offers are not reliable baseline
	-- acquisition sources for a persistent crafting estimate.
	return record ~= nil and record.confirmed ~= false and record.extendedCost ~= true and record.isPurchasable ~= false and record.limitedStock ~= true
end

function VendorData:GetUnitPrice(itemID)
	local record = self:Get(itemID)
	if not self:IsVendorItem(itemID) then return nil, record end
	local price = record and tonumber(record.unitPrice)
	if not price or price <= 0 then return nil, record end
	return price, record
end

function VendorData:GetCost(itemID, quantity)
	local unitPrice, record = self:GetUnitPrice(itemID)
	if not unitPrice then return nil, record end
	quantity = math.max(1, tonumber(quantity) or 1)
	local stackSize = math.max(1, tonumber(record and record.stackSize) or 1)
	local bundlePrice = tonumber(record and record.bundlePrice)
	if bundlePrice and bundlePrice > 0 then
		return bundlePrice * math.ceil(quantity / stackSize), record
	end
	return unitPrice * quantity, record
end

function VendorData:ObserveMerchant()
	local observed = self:GetObservedItems()
	local observedAt = time()
	local build = BuildNumber()
	local changed = 0

	for index = 1, (GetMerchantNumItems() or 0) do
		local itemID = GetMerchantItemID(index)
		if itemID then
			local info = MerchantItemInfo(index) or {}
			local stackSize = math.max(1, tonumber(info.stackCount) or 1)
			local bundlePrice = tonumber(info.price) or 0
			local alternateCostCount = GetMerchantItemCostInfo and (GetMerchantItemCostInfo(index) or 0) or 0
			local hasExtendedCost = info.extendedCost == true or alternateCostCount > 0
			local unitPrice = bundlePrice > 0 and math.ceil(bundlePrice / stackSize) or nil
			local previous = observed[itemID]

			observed[itemID] = {
				confirmed = true,
				source = "merchant-observation",
				unitPrice = unitPrice,
				bundlePrice = bundlePrice > 0 and bundlePrice or nil,
				stackSize = stackSize,
				limitedStock = (tonumber(info.numAvailable) or -1) >= 0,
				numAvailable = tonumber(info.numAvailable) or -1,
				extendedCost = hasExtendedCost,
				isPurchasable = info.isPurchasable ~= false,
				observedAt = observedAt,
				gameBuild = build,
			}

			if not previous or previous.unitPrice ~= unitPrice or previous.stackSize ~= stackSize or previous.extendedCost ~= hasExtendedCost then
				changed = changed + 1
			end
		end
	end

	return changed
end
