local addonName, ns = ...

------------------------------------------------------------
-- Shopping list data layer. One source of truth in
-- HousingHeraldDB.shoppingList.entries; every view subscribes
-- via OnChanged.
------------------------------------------------------------

local ShoppingList = {}
ns.ShoppingList = ShoppingList

local callbacks = {}

local function entries()
    return ns.db.shoppingList.entries
end

local function notify()
    -- pcall each subscriber: these are our own UI refreshers, and one
    -- throwing would otherwise abort the remaining subscribers and
    -- propagate out of Core's event handler, bricking the list.
    for _, cb in ipairs(callbacks) do
        pcall(cb)
    end
end

function ShoppingList.OnChanged(cb)
    table.insert(callbacks, cb)
end

function ShoppingList.GetEntries()
    return entries()
end

-- Entries partitioned into display groups, in this fixed order, skipping
-- empty groups. Views use this to render "Decors" / "Dyes" sections;
-- entry order within a group is preserved from the list.
local GROUP_ORDER = {
    { key = "DECOR", label = "Decors" },
    { key = "DYE", label = "Dyes" },
}

function ShoppingList.GetGroupedEntries()
    local byType = {}
    for _, e in ipairs(entries()) do
        byType[e.type] = byType[e.type] or {}
        table.insert(byType[e.type], e)
    end
    local groups = {}
    for _, spec in ipairs(GROUP_ORDER) do
        local list = byType[spec.key]
        if list and #list > 0 then
            table.insert(groups, { key = spec.key, label = spec.label, entries = list })
        end
    end
    return groups
end

function ShoppingList.Find(entryType, id)
    for i, e in ipairs(entries()) do
        if e.type == entryType and e.id == id then
            return e, i
        end
    end
    return nil, nil
end

-- Upsert without notifying; Import (Task 3) batches many of these
-- under a single notify.
local function upsert(entryType, id, qty, name)
    qty = math.max(1, math.floor(tonumber(qty) or 1))
    local existing = ShoppingList.Find(entryType, id)
    if existing then
        existing.qty = qty
        if name then existing.name = name end
    else
        table.insert(entries(), { type = entryType, id = id, qty = qty, name = name })
    end
end
ShoppingList._upsert = upsert

function ShoppingList.Add(entryType, id, qty, name)
    upsert(entryType, id, qty, name)
    notify()
end

-- Add `delta` (default 1) to an entry's quantity, creating it if absent. Used
-- by the click-to-add gestures: the first Ctrl+Alt+Click adds the decor, each
-- subsequent one bumps the count. Returns the resulting quantity. (Add/SetQty
-- set an absolute quantity; this one is relative.)
function ShoppingList.Increment(entryType, id, delta, name)
    local existing = ShoppingList.Find(entryType, id)
    local current = existing and existing.qty or 0
    local newQty = math.max(1, math.floor(current + (tonumber(delta) or 1)))
    upsert(entryType, id, newQty, name)
    notify()
    return newQty
end

function ShoppingList.SetQty(entryType, id, qty)
    local existing = ShoppingList.Find(entryType, id)
    if not existing then return end
    existing.qty = math.max(1, math.floor(tonumber(qty) or 1))
    notify()
end

function ShoppingList.Remove(entryType, id)
    local _, index = ShoppingList.Find(entryType, id)
    if not index then return end
    table.remove(entries(), index)
    notify()
end

function ShoppingList.Clear()
    local list = entries()
    for i = #list, 1, -1 do
        list[i] = nil
    end
    notify()
end

ShoppingList._notify = notify

------------------------------------------------------------
-- Website exchange format: HH:1:TYPE:id:qty per line.
-- Must stay byte-compatible with the website's
-- parseImportString/serializeExportString.
------------------------------------------------------------

local VALID_TYPES = { DECOR = true, DYE = true }

function ShoppingList.Import(text)
    local imported, skipped = 0, 0
    for line in tostring(text or ""):gmatch("[^\n]+") do
        local trimmed = line:match("^%s*(.-)%s*$")
        if trimmed ~= "" then
            local entryType, id, qty = trimmed:match("^HH:1:(%u+):(%d+):(%d+)$")
            id = tonumber(id)
            if entryType and VALID_TYPES[entryType] and id and id > 0 then
                local name
                if ns.ResolveName then
                    name = ns.ResolveName(entryType, id)
                end
                ShoppingList._upsert(entryType, id, tonumber(qty), name)
                imported = imported + 1
            else
                skipped = skipped + 1
            end
        end
    end
    if imported > 0 then
        ShoppingList._notify()
    end
    return imported, skipped
end

function ShoppingList.Export()
    local lines = {}
    for _, e in ipairs(entries()) do
        table.insert(lines, string.format("HH:1:%s:%d:%d", e.type, e.id, e.qty))
    end
    return table.concat(lines, "\n")
end
