-- =========================================================
-- FS25 Fertilizer Depot - Buy/Sell Dialog
-- =========================================================

local _depotDialogModDir  = g_currentModDirectory
local _depotDialogModName = g_currentModName
local _depotDialogInstance = nil

local function tr(key, fallback)
    local modEnv = g_modEnvironments and g_modEnvironments[_depotDialogModName]
    local i18n = (modEnv and modEnv.i18n) or g_i18n
    if i18n then
        local ok, text = pcall(function() return i18n:getText(key) end)
        -- FS25 returns "Missing 'key' in l10n_XX.xml" for unknown keys instead of throwing
        if ok and text and text ~= "" and text ~= ("$l10n_" .. key)
           and not text:find("^Missing '") then
            return text
        end
    end
    return fallback or key
end

---@class DepotDialog
DepotDialog = {}
DepotDialog.ROWS        = 8
DepotDialog.TAB_BUY      = "buy"
DepotDialog.TAB_SELL     = "sell"
DepotDialog.TAB_STOCK    = "stock"
DepotDialog.TAB_HISTORY  = "history"
DepotDialog.TAB_PRODUCTS = "products"
DepotDialog.TAB_ORDER    = "order"
DepotDialog.ORDER_STEP  = 1000
DepotDialog.ORDER_MIN   = 500
DepotDialog.ORDER_MAX   = 1000000

local DepotDialog_mt = Class(DepotDialog, MessageDialog)

function DepotDialog.new(depotId)
    local self = MessageDialog.new(nil, DepotDialog_mt)
    self.depotId          = depotId
    self.tab              = DepotDialog.TAB_BUY
    self.pageIndex        = 0
    self.fillTypes        = {}
    self.selectedFillType = nil
    self.orderAmount      = 1000

    -- Products tab state
    self.productFillTypes   = {}
    self.selectedProduct    = nil
    self.productQuantity    = 1

    -- Element caches
    self.seasonLabel      = nil
    self.pageLabel        = nil
    self.statusText       = nil
    self.titleText        = nil
    self.prevPageBtn      = nil
    self.nextPageBtn      = nil
    self.closeButton      = nil
    self.historyButton    = nil
    self.stockButton      = nil
    self.tabSellFooter    = nil
    self.tabBuyFooter     = nil
    self.colTypeHeader    = nil
    self.colStockHeader   = nil
    self.colPriceHeader   = nil
    self.preOrderDivider  = nil
    self.preOrderLabel    = nil
    self.preOrderRow      = nil
    self.selectedTypeName = nil
    self.amountDisplay    = nil
    self.confirmBuyTxt    = nil

    -- Products order row elements
    self.productsOrderRow     = nil
    self.prodSelectedName     = nil
    self.prodQtyDisplay       = nil
    self.prodTotalPrice       = nil

    -- Order tab elements
    self.orderPanel           = nil
    self.orderCostText        = nil
    self.orderActionBtn       = nil
    self.orderActionTxt       = nil

    -- [1..ROWS] = {nameEl, stockEl, priceEl, actionBtn, actionTxt}
    self.rows = {}
    -- Cached sell list built each refresh: [{ft, liters, revenue}, ...]
    self.sellList = {}
    return self
end

-- ─── Registration ────────────────────────────────────────

function DepotDialog.getInstance()
    return _depotDialogInstance
end

function DepotDialog.register()
    if _depotDialogInstance then return end
    _depotDialogInstance = DepotDialog.new(nil)
    DepotLogger.info("DepotDialog.register: loading GUI from %s", _depotDialogModDir)
    g_gui:loadGui(_depotDialogModDir .. "xml/gui/DepotDialog.xml",
        "DepotDialog", _depotDialogInstance)
end

function DepotDialog.show(depotId)
    DepotLogger.info("DepotDialog.show called for depot #%s", tostring(depotId))
    if not _depotDialogInstance then
        DepotDialog.register()
    end
    local dlg = _depotDialogInstance
    dlg.depotId          = depotId
    dlg.tab              = DepotDialog.TAB_BUY
    dlg.pageIndex        = 0
    dlg.selectedFillType = nil
    dlg.orderAmount      = 1000
    dlg.selectedProduct  = nil
    dlg.productQuantity  = 1
    g_gui:showDialog("DepotDialog")
end

-- ─── Lifecycle ───────────────────────────────────────────

function DepotDialog:onCreate()
    local ok, err = pcall(function() DepotDialog:superClass().onCreate(self) end)
    if not ok then DepotLogger.error("DepotDialog:onCreate error: %s", tostring(err)) end
end

function DepotDialog:onGuiSetupFinished()
    DepotDialog:superClass().onGuiSetupFinished(self)

    self.seasonLabel     = self:getDescendantById("seasonLabel")
    self.pageLabel       = self:getDescendantById("pageLabel")
    self.statusText      = self:getDescendantById("statusText")
    self.titleText       = self:getDescendantById("depotTitleText")
    self.prevPageBtn     = self:getDescendantById("prevPageBtn")
    self.nextPageBtn     = self:getDescendantById("nextPageBtn")
    self.closeButton     = self:getDescendantById("closeButton")
    self.historyButton   = self:getDescendantById("historyButton")
    self.stockButton     = self:getDescendantById("stockButton")
    self.tabSellFooter   = self:getDescendantById("tabSellFooter")
    self.tabBuyFooter    = self:getDescendantById("tabBuyFooter")
    self.colTypeHeader   = self:getDescendantById("colTypeHeader")
    self.colStockHeader  = self:getDescendantById("colStockHeader")
    self.colPriceHeader  = self:getDescendantById("colPriceHeader")
    self.preOrderDivider  = self:getDescendantById("preOrderDivider")
    self.preOrderLabel    = self:getDescendantById("preOrderLabel")
    self.preOrderRow      = self:getDescendantById("preOrderRow")
    self.selectedTypeName = self:getDescendantById("selectedTypeName")
    self.amountDisplay    = self:getDescendantById("amountDisplay")
    self.confirmBuyTxt    = self:getDescendantById("confirmBuyTxt")

    self.productsOrderRow = self:getDescendantById("productsOrderRow")
    self.prodSelectedName = self:getDescendantById("prodSelectedName")
    self.prodQtyDisplay   = self:getDescendantById("prodQtyDisplay")
    self.prodTotalPrice   = self:getDescendantById("prodTotalPrice")

    self.orderPanel     = self:getDescendantById("orderPanel")
    self.orderCostText  = self:getDescendantById("orderCostText")
    self.orderActionBtn = self:getDescendantById("orderActionBtn")
    self.orderActionTxt = self:getDescendantById("orderActionTxt")

    for i = 0, DepotDialog.ROWS - 1 do
        local p = "row" .. i
        self.rows[i + 1] = {
            nameEl    = self:getDescendantById(p .. "name"),
            stockEl   = self:getDescendantById(p .. "stock"),
            priceEl   = self:getDescendantById(p .. "price"),
            actionBtn = self:getDescendantById(p .. "action"),
            actionTxt = self:getDescendantById(p .. "actionTxt"),
        }
    end
end

function DepotDialog:onOpen()
    DepotDialog:superClass().onOpen(self)
    if self.titleText then self.titleText:setText(tr("fd_depot_title", "Agricultural Cooperative")) end
    if self.preOrderLabel then self.preOrderLabel:setText(tr("fd_depot_buy", "Buy") .. ":") end
    if self.confirmBuyTxt then self.confirmBuyTxt:setText(tr("fd_depot_buy", "Buy")) end
    if self.closeButton then self.closeButton:setText(tr("fd_depot_close", "Close")) end
    if self.historyButton then self.historyButton:setText(tr("fd_depot_history", "History")) end
    if self.stockButton then self.stockButton:setText(tr("fd_depot_stock", "Stock")) end
    if self.tabSellFooter then self.tabSellFooter:setText(tr("fd_depot_sell", "Sell")) end
    if self.tabBuyFooter then self.tabBuyFooter:setText(tr("fd_depot_buy", "Buy")) end

    if g_DepotManager then
        self.fillTypes       = g_DepotManager.sfBridge:getFillTypeList()
        self.productFillTypes = g_DepotManager.sfBridge:getProductFillTypeList()
    else
        self.fillTypes        = {}
        self.productFillTypes = {}
    end

    -- Botão "Estoque" só aparece pro DONO da cooperativa
    if self.stockButton then
        local isOwner = false
        local myFarmId = 0
        if g_DepotManager and g_DepotManager.coopState then
            myFarmId = g_DepotManager:getLocalFarmId()
            isOwner = g_DepotManager.coopState:isOwner(myFarmId)
        end
        DepotLogger.info(">>> [DIALOG] Checagem dono: myFarmId=%d ownerFarmId=%d isOwner=%s userId=%s",
            myFarmId,
            (g_DepotManager and g_DepotManager.coopState and g_DepotManager.coopState.ownerFarmId) or -1,
            tostring(isOwner),
            tostring(g_localPlayer and g_localPlayer.userId or "?"))
        self.stockButton:setVisible(isOwner)
    end

    -- Botão "Histórico" só aparece pro DONO
    if self.historyButton then
        local isOwner = false
        if g_DepotManager and g_DepotManager.coopState then
            local myFarmId = g_DepotManager:getLocalFarmId()
            isOwner = g_DepotManager.coopState:isOwner(myFarmId)
        end
        self.historyButton:setVisible(isOwner)
    end

    self:_syncTabSections()
    self:refresh()
end

-- Aba Estoque (só dono): lista o estoque acumulado da coop, vende item a item
function DepotDialog:onTabStock()
    local myFarmId = g_DepotManager and g_DepotManager:getLocalFarmId() or 0
    if not (g_DepotManager and g_DepotManager.coopState
            and g_DepotManager.coopState:isOwner(myFarmId)) then
        self:showStatus(tr("fd_settings_admin_only",
            "Apenas o proprietário pode ver o estoque da cooperativa."))
        return
    end
    self.tab = DepotDialog.TAB_STOCK
    self.pageIndex = 0
    self:_syncTabSections()
    self:refresh()
end

function DepotDialog:refreshStockTab()
    local system  = g_DepotManager and g_DepotManager.depotSystem
    local pricing = g_DepotManager and g_DepotManager.pricing
    local depot   = system and system:getDepot(self.depotId)

    -- Monta lista do estoque com quantidade > 0
    local stockList = {}
    if depot then
        for name, liters in pairs(depot.storageLevel) do
            if liters and liters > 0 then
                -- Acha o displayName
                local displayName = name
                for _, ft in ipairs(self.fillTypes) do
                    if ft.name == name then displayName = ft.displayName or name break end
                end
                local marketPrice = pricing and pricing:getMarketPrice(name) or 0
                table.insert(stockList, {
                    name        = name,
                    displayName = displayName,
                    liters      = liters,
                    revenue     = marketPrice * liters,
                })
            end
        end
    end
    table.sort(stockList, function(a, b) return a.displayName < b.displayName end)
    self.stockList = stockList

    for slot = 1, DepotDialog.ROWS do
        local entry = stockList[self.pageIndex + slot]
        local row   = self.rows[slot]
        if entry and row then
            local litersStr = string.format("%.0fL", entry.liters)
            local revStr    = g_i18n and g_i18n:formatMoney(entry.revenue, 0, true)
                              or string.format("$%.0f", entry.revenue)
            if row.nameEl   then row.nameEl:setText(entry.displayName) end
            if row.stockEl  then row.stockEl:setText(litersStr) end
            if row.priceEl  then row.priceEl:setText(revStr) end
            if row.actionTxt then row.actionTxt:setText(tr("fd_coop_sell_market", "Vender")) end
            if row.actionBtn then row.actionBtn:setVisible(true) end
        else
            self:clearRow(slot)
        end
    end

    if self.statusText then
        if #stockList == 0 then
            self.statusText:setText(tr("fd_coop_stock_empty",
                "A cooperativa não tem estoque acumulado."))
        else
            self.statusText:setText(tr("fd_coop_stock_hint",
                "Clique em Vender para liquidar cada item ao preço de mercado."))
        end
    end
end

-- Vende UM item do estoque (chamado ao clicar numa linha da aba Estoque)
function DepotDialog:executeStockSell(rowSlot)
    local entry = self.stockList and self.stockList[self.pageIndex + rowSlot]
    if not entry then return end

    local myFarmId = g_DepotManager and g_DepotManager:getLocalFarmId() or 0
    if not (g_DepotManager and g_DepotManager.coopState
            and g_DepotManager.coopState:isOwner(myFarmId)) then
        return
    end

    local revStr = g_i18n and g_i18n:formatMoney(entry.revenue, 0, true)
                   or string.format("$%.0f", entry.revenue)
    local text = string.format(
        tr("fd_coop_sell_item_confirm", "Vender %.0f L de %s por %s ao preço de mercado?"),
        entry.liters, entry.displayName, revStr)

    YesNoDialog.show(function(confirmed)
        if confirmed then
            DepotSellItemEvent.sendToServer(self.depotId, entry.name, myFarmId)
            self:showStatus(string.format(
                tr("fd_coop_item_sold_msg", "%s vendido ao mercado!"), entry.displayName))
        end
    end, nil, text)
end

function DepotDialog:fdOnClose()
    DepotDialog:superClass().onClose(self)
    if g_DepotManager then
        g_DepotManager.activeDialog = nil
    end
end

-- ─── Aba Histórico (só dono): lista as últimas transações ──
function DepotDialog:onTabHistory()
    local myFarmId = g_DepotManager and g_DepotManager:getLocalFarmId() or 0
    if not (g_DepotManager and g_DepotManager.coopState
            and g_DepotManager.coopState:isOwner(myFarmId)) then
        self:showStatus(tr("fd_settings_admin_only",
            "Apenas o proprietário pode ver o histórico."))
        return
    end
    self.tab = DepotDialog.TAB_HISTORY
    self.pageIndex = 0
    self:_syncTabSections()
    self:refresh()
end

-- Formata número com separador de milhar (64152 -> "64.152")
function DepotDialog:_fmtNum(n)
    local s = tostring(math.floor(n or 0))
    local formatted = s
    local k
    repeat
        formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", "%1.%2")
    until k == 0
    return formatted
end

function DepotDialog:refreshHistoryTab()
    local log = g_DepotManager and g_DepotManager.transactionLog
    local list = log and log:getRecent(50) or {}
    self.historyList = list

    for slot = 1, DepotDialog.ROWS do
        local entry = list[self.pageIndex + slot]
        local row   = self.rows[slot]
        if entry and row then
            local isBuy = (entry.kind == "buy")

            -- Nome do produto (encurtado com cuidado)
            local displayName = entry.fillType
            if g_fillTypeManager then
                local ft = g_fillTypeManager:getFillTypeByName(entry.fillType)
                if ft and ft.title then displayName = ft.title end
            end
            if #displayName > 20 then
                displayName = string.sub(displayName, 1, 19) .. "..."
            end

            -- Nome da fazenda (encurtado)
            local farmName = g_DepotManager and g_DepotManager:getFarmName(entry.farmId)
                             or ("Fazenda " .. entry.farmId)
            if #farmName > 16 then
                farmName = string.sub(farmName, 1, 15) .. "..."
            end

            -- ── Célula 1: símbolo + tipo + fazenda ──
            -- ▼ = dono pagou (venda a ele) / ▲ = dono recebeu (compra dele)
            local arrow   = isBuy and "+" or "-"
            local kindTxt = isBuy and tr("fd_hist_in", "Entrada") or tr("fd_hist_out", "Saída")
            local col1 = string.format("%s %s - %s", arrow, kindTxt, farmName)

            -- ── Célula 2: produto + litros ──
            local col2 = string.format("%s  (%s L)",
                displayName, self:_fmtNum(math.floor(entry.liters)))

            -- ── Célula 3: valor + data ──
            local moneyStr = g_i18n and g_i18n:formatMoney(entry.value, 0, true)
                             or string.format("$%.0f", entry.value)
            local sign = isBuy and "+" or "-"
            local col3 = string.format("%s%s - D%d %02dh",
                sign, moneyStr, entry.day or 1, math.floor(entry.hour or 0))

            if row.nameEl then
                row.nameEl:setText(col1)
                -- Cor no tipo: verde (entrada) / laranja (saída)
                if row.nameEl.setTextColor then
                    if isBuy then row.nameEl:setTextColor(0.4, 0.9, 0.4, 1.0)
                    else          row.nameEl:setTextColor(1.0, 0.6, 0.3, 1.0) end
                end
            end
            if row.stockEl then row.stockEl:setText(col2) end
            if row.priceEl then
                row.priceEl:setText(col3)
                if row.priceEl.setTextColor then
                    if isBuy then row.priceEl:setTextColor(0.5, 0.85, 0.5, 1.0)
                    else          row.priceEl:setTextColor(0.95, 0.65, 0.35, 1.0) end
                end
            end
            if row.actionBtn then row.actionBtn:setVisible(false) end
        else
            self:clearRow(slot)
        end
    end

    if self.statusText then
        if #list == 0 then
            self.statusText:setText(tr("fd_hist_empty",
                "Nenhuma transação registrada ainda."))
        else
            self.statusText:setText(string.format(
                tr("fd_hist_count", "%d transações registradas (mostrando as mais recentes)."),
                #list))
        end
    end
end

function DepotDialog:onSyncReceived(depotId)
    if depotId == self.depotId then self:refresh() end
end

function DepotDialog:showStatus(text)
    if self.statusText then self.statusText:setText(text) end
end

-- ─── Tab Switching ───────────────────────────────────────

function DepotDialog:onTabBuy()
    self.tab = DepotDialog.TAB_BUY
    self.pageIndex = 0
    self:_syncTabSections()
    self:refresh()
end

function DepotDialog:onTabSell()
    self.tab = DepotDialog.TAB_SELL
    self.pageIndex = 0
    self:_syncTabSections()
    self:refresh()
end

function DepotDialog:onTabProducts()
    -- COOPERATIVA: aba "Produtos" removida - redireciona pra Comprar
    self:onTabBuy()
end

function DepotDialog:onTabOrder()
    -- COOPERATIVA: aba "Order" removida - redireciona pra Comprar
    self:onTabBuy()
end

function DepotDialog:_syncTabSections()
    local isBuy = (self.tab == DepotDialog.TAB_BUY)
    -- COOPERATIVA: só a aba Comprar mostra o seletor de quantidade.
    -- Os painéis de Products e Order ficam SEMPRE escondidos.
    if self.preOrderDivider  then self.preOrderDivider:setVisible(isBuy) end
    if self.preOrderRow      then self.preOrderRow:setVisible(isBuy) end
    if self.productsOrderRow then self.productsOrderRow:setVisible(false) end
    if self.orderPanel       then self.orderPanel:setVisible(false) end
end

-- ─── Refresh ─────────────────────────────────────────────

function DepotDialog:refresh()
    self:updateSeasonLabel()

    -- Reset de cor: só o histórico usa cor nas células. Nas outras abas,
    -- garante que o texto volte ao branco (senão a cor "vaza" entre abas).
    if self.tab ~= DepotDialog.TAB_HISTORY then
        for _, row in ipairs(self.rows or {}) do
            if row.nameEl  and row.nameEl.setTextColor  then row.nameEl:setTextColor(1,1,1,1) end
            if row.priceEl and row.priceEl.setTextColor then row.priceEl:setTextColor(1,1,1,1) end
        end
    end

    if self.tab == DepotDialog.TAB_BUY then
        if self.colTypeHeader  then self.colTypeHeader:setText(tr("fd_col_type",   "Fill Type"))    end
        if self.colStockHeader then self.colStockHeader:setText(tr("fd_col_stock", "Depot Stock"))  end
        if self.colPriceHeader then self.colPriceHeader:setText(tr("fd_col_price", "Price / 1kL")) end
        self:refreshBuyTab()
    elseif self.tab == DepotDialog.TAB_SELL then
        if self.colTypeHeader  then self.colTypeHeader:setText(tr("fd_col_type",    "Fill Type"))   end
        if self.colStockHeader then self.colStockHeader:setText(tr("fd_col_amount", "Amount"))      end
        if self.colPriceHeader then self.colPriceHeader:setText(tr("fd_col_revenue","Revenue"))     end
        self:refreshSellTab()
    elseif self.tab == DepotDialog.TAB_STOCK then
        if self.colTypeHeader  then self.colTypeHeader:setText(tr("fd_col_type",     "Produto"))     end
        if self.colStockHeader then self.colStockHeader:setText(tr("fd_col_stock",   "Estoque"))     end
        if self.colPriceHeader then self.colPriceHeader:setText(tr("fd_col_revenue", "Valor mercado")) end
        self:refreshStockTab()
    elseif self.tab == DepotDialog.TAB_HISTORY then
        if self.colTypeHeader  then self.colTypeHeader:setText(tr("fd_col_operation", "Operação"))  end
        if self.colStockHeader then self.colStockHeader:setText(tr("fd_col_product",   "Produto"))   end
        if self.colPriceHeader then self.colPriceHeader:setText(tr("fd_col_when",      "Valor / Quando")) end
        self:refreshHistoryTab()
    elseif self.tab == DepotDialog.TAB_PRODUCTS then
        if self.colTypeHeader  then self.colTypeHeader:setText(tr("fd_col_type",   "Fill Type"))    end
        if self.colStockHeader then self.colStockHeader:setText(tr("fd_col_stock", "Depot Stock"))  end
        if self.colPriceHeader then self.colPriceHeader:setText(tr("fd_col_price", "Price / 1kL")) end
        self:refreshProductsTab()
    else
        if self.colTypeHeader  then self.colTypeHeader:setText(tr("fd_col_type",   "Fill Type"))    end
        if self.colStockHeader then self.colStockHeader:setText(tr("fd_col_stock", "In Stock"))     end
        if self.colPriceHeader then self.colPriceHeader:setText(tr("fd_col_needed","Needed"))       end
        self:refreshOrderTab()
    end
    self:updatePagination()
end

function DepotDialog:updateSeasonLabel()
    -- COOPERATIVA: sem sazonalidade. Label escondido.
    if self.seasonLabel then self.seasonLabel:setVisible(false) end
end

function DepotDialog:refreshBuyTab()
    local system  = g_DepotManager and g_DepotManager.depotSystem
    local pricing = g_DepotManager and g_DepotManager.pricing

    -- COOPERATIVA: na aba comprar, só mostra fillTypes COM estoque > 0
    -- (você só pode comprar o que algum fazendeiro depositou).
    local buyList = {}
    for _, ft in ipairs(self.fillTypes) do
        local stored = system and system:getStorageLevel(self.depotId, ft.name) or 0
        if stored > 0 then
            table.insert(buyList, ft)
        end
    end
    self.buyList = buyList

    -- Mensagem se não há nada pra comprar
    if #buyList == 0 and self.statusText then
        self.statusText:setText(tr("fd_coop_empty",
            "A cooperativa está sem estoque. Quando alguém vender, os produtos aparecem aqui."))
    end

    for slot = 1, DepotDialog.ROWS do
        local ftIdx = self.pageIndex + slot
        local row   = self.rows[slot]
        local ft    = buyList[ftIdx]

        if ft and row then
            local stored   = system and system:getStorageLevel(self.depotId, ft.name) or 0
            local buyPrice = pricing and pricing:getBuyPrice(ft.name) or 0
            local priceStr = string.format("$%.2f/kL", buyPrice * 1000)
            local stockStr = string.format("%dL", math.floor(stored))

            if row.nameEl  then row.nameEl:setText(ft.displayName or ft.name) end
            if row.stockEl then row.stockEl:setText(stockStr) end
            if row.priceEl then row.priceEl:setText(priceStr) end

            -- COOPERATIVA: botão compra direto
            if row.actionTxt then
                row.actionTxt:setText(tr("fd_depot_buy_btn", "Comprar"))
            end
            if row.actionBtn then row.actionBtn:setVisible(true) end
        else
            self:clearRow(slot)
        end
    end
end

function DepotDialog:refreshSellTab()
    local system  = g_DepotManager and g_DepotManager.depotSystem
    local pricing = g_DepotManager and g_DepotManager.pricing
    local sellTypes = {}

    if system and self.depotId then
        -- Single vehicle scan across all fill types (avoids N separate 96-vehicle searches)
        local nearbyFills = system:buildNearbyFillMap(self.depotId)
        for _, ft in ipairs(self.fillTypes) do
            if ft.fillTypeIndex and ft.fillTypeIndex > 0 then
                local entry = nearbyFills[ft.fillTypeIndex]
                if entry then
                    local rev = pricing and pricing:calculateSellRevenue(ft.name, entry.fillLevel) or 0
                    table.insert(sellTypes, {ft = ft, liters = entry.fillLevel, revenue = rev})
                end
            end
        end
    end

    -- Cache so executeSell uses the same snapshot as what was displayed
    self.sellList = sellTypes

    for slot = 1, DepotDialog.ROWS do
        local entry = sellTypes[self.pageIndex + slot]
        local row   = self.rows[slot]
        if entry and row then
            local litersStr = string.format("%.0fL", entry.liters)
            local revStr    = string.format("$%.2f", entry.revenue)
            if row.nameEl   then row.nameEl:setText(entry.ft.displayName or entry.ft.name) end
            if row.stockEl  then row.stockEl:setText(litersStr) end
            if row.priceEl  then row.priceEl:setText(revStr) end
            if row.actionTxt then row.actionTxt:setText(tr("fd_depot_sell_btn", "Sell All")) end
            if row.actionBtn then row.actionBtn:setVisible(true) end
        else
            self:clearRow(slot)
        end
    end

    if self.statusText then
        if #sellTypes == 0 then
            self.statusText:setText(tr("fd_depot_no_trailer", "No compatible trailer nearby."))
        else
            self.statusText:setText("")
        end
    end
end

function DepotDialog:refreshProductsTab()
    local system  = g_DepotManager and g_DepotManager.depotSystem
    local pricing = g_DepotManager and g_DepotManager.pricing

    if self.statusText then
        if #self.productFillTypes == 0 then
            self.statusText:setText(tr("fd_products_none", "No physical products available."))
        else
            self.statusText:setText("")
        end
    end

    for slot = 1, DepotDialog.ROWS do
        local ftIdx = self.pageIndex + slot
        local row   = self.rows[slot]
        local ft    = self.productFillTypes[ftIdx]

        if ft and row then
            local stored      = system and system:getStorageLevel(self.depotId, ft.name) or 0
            local pricePerUnit = pricing and
                (pricing:getBuyPrice(ft.name) * ft.litresPerUnit) or 0
            local priceStr = string.format("$%.2f/unit", pricePerUnit)
            local stockStr
            if stored <= 0 then
                stockStr = tr("fd_depot_stock_stocking", "Stocking")
            else
                local units = math.floor(stored / ft.litresPerUnit)
                stockStr = string.format("%d units", units)
            end

            if row.nameEl  then row.nameEl:setText(ft.displayName or ft.name) end
            if row.stockEl then row.stockEl:setText(stockStr) end
            if row.priceEl then row.priceEl:setText(priceStr) end

            local isSelected = self.selectedProduct and self.selectedProduct.name == ft.name
            if row.actionTxt then
                if isSelected then
                    row.actionTxt:setText(tr("fd_depot_selected", "Selected"))
                elseif ft.productLabel == "bag" then
                    row.actionTxt:setText(tr("fd_products_order_bag", "Order Bag"))
                else
                    row.actionTxt:setText(tr("fd_products_order_tank", "Order Tank"))
                end
            end
            if row.actionBtn then row.actionBtn:setVisible(true) end
        else
            self:clearRow(slot)
        end
    end

    self:_updateProductsOrderRow()
end

function DepotDialog:_updateProductsOrderRow()
    local pricing = g_DepotManager and g_DepotManager.pricing
    local ft = self.selectedProduct

    if self.prodSelectedName then
        self.prodSelectedName:setText(ft and (ft.displayName or ft.name) or "-")
    end
    if self.prodQtyDisplay then
        self.prodQtyDisplay:setText(string.format("x%d", self.productQuantity))
    end
    if self.prodTotalPrice then
        if ft and pricing then
            local total = pricing:getBuyPrice(ft.name) * ft.litresPerUnit * self.productQuantity
            self.prodTotalPrice:setText(string.format("$%.2f", total))
        else
            self.prodTotalPrice:setText("")
        end
    end
end

function DepotDialog:clearRow(slot)
    local row = self.rows[slot]
    if not row then return end
    if row.nameEl   then
        row.nameEl:setText("")
        if row.nameEl.setTextColor then row.nameEl:setTextColor(1, 1, 1, 1) end
    end
    if row.stockEl  then row.stockEl:setText("") end
    if row.priceEl  then
        row.priceEl:setText("")
        if row.priceEl.setTextColor then row.priceEl:setTextColor(1, 1, 1, 1) end
    end
    if row.actionBtn then row.actionBtn:setVisible(false) end
end

function DepotDialog:updatePagination()
    local total
    if self.tab == DepotDialog.TAB_BUY then
        total = #(self.buyList or self.fillTypes)
    elseif self.tab == DepotDialog.TAB_SELL then
        total = #self.sellList
    elseif self.tab == DepotDialog.TAB_STOCK then
        total = #(self.stockList or {})
    elseif self.tab == DepotDialog.TAB_HISTORY then
        total = #(self.historyList or {})
    else
        total = 0
    end

    local maxPage = math.max(0, math.ceil(total / DepotDialog.ROWS) - 1)
    local curPage = math.floor(self.pageIndex / DepotDialog.ROWS)

    if self.pageLabel then
        self.pageLabel:setText(string.format("%d / %d", curPage + 1, maxPage + 1))
    end
    if self.prevPageBtn then self.prevPageBtn:setVisible(self.pageIndex > 0) end
    if self.nextPageBtn then self.nextPageBtn:setVisible(self.pageIndex + DepotDialog.ROWS < total) end
end

function DepotDialog:getSellCount()
    return #self.sellList
end

-- ─── Pre-order Actions ───────────────────────────────────

-- Passo adaptativo: quanto maior a quantidade, maior o incremento.
-- Assim dá pra chegar rápido em valores altos sem apertar 100 vezes.
function DepotDialog:_getAdaptiveStep()
    local a = self.orderAmount or 0
    if a >= 100000 then return 25000 end
    if a >= 50000  then return 10000 end
    if a >= 10000  then return 5000  end
    if a >= 5000   then return 1000  end
    return 500
end

function DepotDialog:onAmountMinus()
    local step = self:_getAdaptiveStep()
    self.orderAmount = math.max(DepotDialog.ORDER_MIN, self.orderAmount - step)
    self:_updateAmountDisplay()
end

function DepotDialog:onAmountPlus()
    local available = self.selectedAvailable or 0
    local maxAllowed = math.min(DepotDialog.ORDER_MAX, math.floor(available))
    local step = self:_getAdaptiveStep()
    self.orderAmount = math.min(maxAllowed, self.orderAmount + step)
    if self.orderAmount < DepotDialog.ORDER_MIN then
        self.orderAmount = math.min(DepotDialog.ORDER_MIN, maxAllowed)
    end
    self:_updateAmountDisplay()
end

function DepotDialog:_updateAmountDisplay()
    if self.amountDisplay then
        local available = self.selectedAvailable or 0
        self.amountDisplay:setText(string.format("%dL / %dL",
            self.orderAmount, math.floor(available)))
    end
end

-- Digitação manual da quantidade (clicar no número abre o teclado)
function DepotDialog:onAmountManual()
    if TextInputDialog == nil then
        DepotLogger.warning(">>> TextInputDialog não disponível - digitação manual desativada")
        return
    end

    local available = math.floor(self.selectedAvailable or 0)
    local maxAllowed = math.min(DepotDialog.ORDER_MAX, available)

    local prompt = string.format(
        tr("fd_amount_manual_prompt", "Digite a quantidade em litros (máx %d L):"),
        maxAllowed)

    -- Callback que valida e aplica o valor digitado
    local function applyTyped(text, clickOk)
        if clickOk and text ~= nil then
            local n = tonumber((tostring(text):gsub("[^%d]", "")))
            if n and n > 0 then
                n = math.max(DepotDialog.ORDER_MIN, n)
                n = math.min(n, maxAllowed)
                self.orderAmount = n
                self:_updateAmountDisplay()
            end
        end
    end

    -- Protege contra variação de assinatura entre versões do FS25.
    -- Se a chamada falhar, o jogo NÃO trava (só não abre a digitação).
    local ok, err = pcall(function()
        TextInputDialog.show(
            applyTyped,
            self,
            tostring(self.orderAmount or ""),
            tr("fd_amount_manual_title", "Quantidade"),
            prompt,
            10,          -- maxCharacters
            false        -- não é senha
        )
    end)

    if not ok then
        DepotLogger.warning(">>> Falha ao abrir digitação manual: %s", tostring(err))
    end
end

function DepotDialog:onConfirmOrder()
    if not self.selectedFillType then
        DepotLogger.info(">>> onConfirmOrder: nenhum produto selecionado")
        self:showStatus(tr("fd_depot_select_first", "Selecione um produto primeiro."))
        return
    end

    local farmId = g_localPlayer and g_localPlayer.farmId or 1
    local ft     = self.selectedFillType
    local system = g_DepotManager and g_DepotManager.depotSystem

    DepotLogger.info(">>> onConfirmOrder: produto=%s farmId=%d amount=%d depot=%d",
        tostring(ft.name), farmId, self.orderAmount or 0, self.depotId or 0)

    -- COOPERATIVA: só compra se tem estoque
    local depot = system and system:getDepot(self.depotId)
    local available = (depot and depot.storageLevel[ft.name]) or 0
    DepotLogger.info(">>> onConfirmOrder: estoque disponível = %.0f L", available)
    if available <= 0 then
        self:showStatus(tr("fd_coop_no_stock", "A cooperativa não tem estoque deste produto."))
        return
    end

    -- COOPERATIVA: exige veículo compatível encostado (sem pedido/coleta no silo)
    local vehicle, unitIndex = system and system:findCompatibleVehicle(
        self.depotId, ft.fillTypeIndex, false)

    DepotLogger.info(">>> onConfirmOrder: veículo compatível = %s (unit=%s)",
        tostring(vehicle ~= nil), tostring(unitIndex))

    if vehicle and unitIndex then
        -- Limita a quantidade ao estoque disponível
        local amount = math.min(self.orderAmount, available)
        DepotLogger.info(">>> onConfirmOrder: veículo encostado, compra direta de %.0f L", amount)
        DepotPurchaseEvent.sendToServer(
            self.depotId, ft.name, ft.fillTypeIndex, amount, farmId)
        self:showStatus(string.format(
            tr("fd_depot_filling", "Enchendo %.0f L de %s no seu veículo..."),
            amount, ft.displayName or ft.name))
    else
        -- Sem veículo encostado: cria PEDIDO pra coletar no silo da cooperativa (E)
        local amount = math.min(self.orderAmount, available)
        DepotLogger.info(">>> onConfirmOrder: sem veículo - pedido de %.0f L pra coleta no silo", amount)
        if g_DepotManager then
            g_DepotManager:setPendingOrder(
                farmId, self.depotId,
                ft.name, ft.fillTypeIndex,
                amount,
                ft.displayName or ft.name)
        end
        self:showStatus(string.format(
            tr("fd_depot_order_set", "Pedido feito: %s %.0f L. Encoste o reboque na cooperativa e aperte E para coletar."),
            ft.displayName or ft.name, amount))
    end
end

-- ─── Row Action (Select for Buy/Products, Sell All for Sell) ──────

function DepotDialog:onRowAction(rowSlot)
    if self.tab == DepotDialog.TAB_SELL then
        self:executeSell(rowSlot)
    elseif self.tab == DepotDialog.TAB_STOCK then
        self:executeStockSell(rowSlot)
    elseif self.tab == DepotDialog.TAB_HISTORY then
        -- Histórico é somente leitura, sem ação
        return
    else
        -- COOPERATIVA: aba Comprar - seleciona E já tenta comprar direto
        self:selectRow(rowSlot)
        self:onConfirmOrder()
    end
end

function DepotDialog:selectRow(rowSlot)
    local ftIdx = self.pageIndex + rowSlot
    local ft    = (self.buyList or self.fillTypes)[ftIdx]
    if not ft then return end
    self.selectedFillType = ft

    -- COOPERATIVA: teto da quantidade = estoque disponível
    local system = g_DepotManager and g_DepotManager.depotSystem
    local depot  = system and system:getDepot(self.depotId)
    local available = (depot and depot.storageLevel[ft.name]) or 0
    self.selectedAvailable = available

    -- Quantidade inicial = TODO o estoque disponível (o dono reduz se quiser).
    -- Assim não precisa ficar apertando + várias vezes pra comprar bastante.
    self.orderAmount = math.max(0, math.floor(available))
    if self.orderAmount < DepotDialog.ORDER_MIN and available >= DepotDialog.ORDER_MIN then
        self.orderAmount = DepotDialog.ORDER_MIN
    end
    -- Respeita o teto máximo de compra
    self.orderAmount = math.min(self.orderAmount, DepotDialog.ORDER_MAX)

    if self.selectedTypeName then
        self.selectedTypeName:setText(ft.displayName or ft.name)
    end
    if self.amountDisplay then
        self.amountDisplay:setText(string.format("%dL / %dL", self.orderAmount, math.floor(available)))
    end
end

function DepotDialog:selectProductRow(rowSlot)
    local ftIdx = self.pageIndex + rowSlot
    local ft    = self.productFillTypes[ftIdx]
    if not ft then return end
    self.selectedProduct = ft
    self.productQuantity = 1
    self:showStatus("")
    self:refreshProductsTab()
end

function DepotDialog:onProductQtyMinus()
    self.productQuantity = math.max(1, self.productQuantity - 1)
    self:_updateProductsOrderRow()
end

function DepotDialog:onProductQtyPlus()
    self.productQuantity = math.min(DepotConstants.MAX_PRODUCT_QUANTITY, self.productQuantity + 1)
    self:_updateProductsOrderRow()
end

function DepotDialog:onProductConfirm()
    if not self.selectedProduct then
        self:showStatus(tr("fd_depot_select_first", "Select a fill type first."))
        return
    end

    local farmId = g_localPlayer and g_localPlayer.farmId or 1
    local ft     = self.selectedProduct
    local system = g_DepotManager and g_DepotManager.depotSystem

    local stored = system and system:getStorageLevel(self.depotId, ft.name) or 0
    local litresNeeded = self.productQuantity * ft.litresPerUnit
    if stored < litresNeeded then
        self:showStatus(tr("fd_products_no_stock", "Insufficient depot stock for this order."))
        return
    end

    DepotProductOrderEvent.sendToServer(
        self.depotId, ft.name, ft.fillTypeIndex, self.productQuantity, farmId)

    local label = ft.productLabel == "bag"
        and tr("fd_products_label_bag", "Bag(s)")
        or  tr("fd_products_label_tank", "Tank(s)")
    self:showStatus(string.format(
        tr("fd_products_ordered", "%dx %s %s ordered - delivering to depot."),
        self.productQuantity, ft.displayName or ft.name, label))
end

function DepotDialog:executeSell(rowSlot)
    local entry = self.sellList[self.pageIndex + rowSlot]
    if not entry then return end
    local farmId = g_localPlayer and g_localPlayer.farmId or 1
    self:showStatus(string.format(
        tr("fd_depot_filling", "Selling %.0fL %s..."),
        entry.liters, entry.ft.displayName or entry.ft.name))
    DepotSellEvent.sendToServer(
        self.depotId, entry.ft.name, entry.ft.fillTypeIndex, entry.liters, farmId)
end

-- ─── ORDER Tab ───────────────────────────────────────────

function DepotDialog:refreshOrderTab()
    local ds       = g_DepotManager and g_DepotManager.deliverySystem
    local delivery = ds and ds:getDelivery(self.depotId)

    -- When a delivery is active, show its items with live stock readings.
    -- Otherwise show a fresh calculation of what would be ordered.
    local order = ds and ds:calculateOrder(self.depotId)
    local items
    if delivery then
        -- Refresh stored values from live depot data so column 2 is current
        local system = g_DepotManager and g_DepotManager.depotSystem
        for _, item in ipairs(delivery.items) do
            item.stored = (system and system:getStorageLevel(self.depotId, item.fillTypeName)) or 0
        end
        items = delivery.items
    else
        items = (order and order.items) or {}
    end

    for slot = 1, DepotDialog.ROWS do
        local ftIdx = self.pageIndex + slot
        local row   = self.rows[slot]
        local item  = items[ftIdx]

        if item and row then
            local stockStr  = string.format("%dL", math.floor(item.stored or 0))
            local neededStr = string.format("%dL", math.floor(item.needed or 0))
            if row.nameEl  then row.nameEl:setText(item.displayName or item.fillTypeName) end
            if row.stockEl then row.stockEl:setText(stockStr) end
            if row.priceEl then row.priceEl:setText(neededStr) end
            if row.actionBtn then row.actionBtn:setVisible(false) end
        else
            self:clearRow(slot)
        end
    end

    self:_updateOrderPanel(delivery, order)
end

function DepotDialog:_updateOrderPanel(delivery, order)
    local ds = g_DepotManager and g_DepotManager.deliverySystem

    if delivery then
        local status = delivery.status
        if status == DeliverySystem.STATUS.PENDING then
            local penalty    = delivery.deliveryCost * DepotConstants.DELIVERY.CANCEL_PENALTY
            local penaltyStr = string.format("$%.2f", penalty)
            local costStr    = string.format(
                tr("fd_delivery_status_pending", "Delivery pending - drive to Pickup Zone to collect  |  Cancel penalty: %s"),
                penaltyStr)
            if self.orderCostText then self.orderCostText:setText(costStr) end
            if self.orderActionTxt then
                self.orderActionTxt:setText(tr("fd_delivery_cancel_btn", "Cancel Order"))
            end
            if self.orderActionBtn then self.orderActionBtn:setVisible(true) end

        elseif status == DeliverySystem.STATUS.LOADED then
            local canComplete = ds and ds:isDeliveryTruckNearDepot(self.depotId)
            local dist        = ds and ds:getDeliveryTruckDistance(self.depotId)
            local msg
            if canComplete then
                msg = tr("fd_delivery_status_ready", "Delivery ready - click to stock your depot.")
            elseif dist then
                msg = string.format(
                    tr("fd_delivery_status_park", "Park delivery vehicle within 25m of unload zone  (%.0fm away)"),
                    dist)
            else
                msg = tr("fd_delivery_status_return", "Return to depot and park near the unload zone.")
            end
            if self.orderCostText then self.orderCostText:setText(msg) end
            if self.orderActionTxt then
                self.orderActionTxt:setText(tr("fd_delivery_complete_btn", "Complete Delivery"))
            end
            if self.orderActionBtn then self.orderActionBtn:setVisible(true) end
        end

    else
        -- No active delivery
        if not ds or not next(g_DepotManager.pickupNodes or {}) then
            if self.orderCostText then
                self.orderCostText:setText(
                    tr("fd_delivery_no_pickup_placed",
                       "Place a Fertilizer Supplier pickup zone near the in-game shop first."))
            end
            if self.orderActionBtn then self.orderActionBtn:setVisible(false) end
        elseif not order or #order.items == 0 then
            if self.orderCostText then
                self.orderCostText:setText(
                    tr("fd_delivery_all_full", "All fill types are at capacity - no delivery needed."))
            end
            if self.orderActionBtn then self.orderActionBtn:setVisible(false) end
        else
            local costStr = string.format(
                tr("fd_delivery_cost_summary", "Base: $%.2f  +  Fee: $%.2f  =  Total: $%.2f"),
                order.baseCost, order.fee, order.deliveryCost)
            if self.orderCostText then self.orderCostText:setText(costStr) end
            if self.orderActionTxt then
                self.orderActionTxt:setText(tr("fd_delivery_place_btn", "Place Delivery Order"))
            end
            if self.orderActionBtn then self.orderActionBtn:setVisible(true) end
        end
    end
end

-- Single action button callback - dispatches based on current delivery state.
function DepotDialog:onOrderAction()
    local ds     = g_DepotManager and g_DepotManager.deliverySystem
    if not ds then return end
    local delivery = ds:getDelivery(self.depotId)
    local farmId   = g_localPlayer and g_localPlayer.farmId or 1

    if not delivery then
        -- Place new delivery order
        local errKey = ds:canPlaceOrder(self.depotId, farmId)
        if errKey then
            self:showStatus(tr(errKey, errKey))
            return
        end
        local order     = ds:calculateOrder(self.depotId)
        local costStr   = string.format("$%.2f", order.deliveryCost)
        local itemCount = #order.items
        local text = string.format(
            tr("fd_delivery_order_confirm",
               "Place delivery order for %d fill type(s)?\n\nTotal cost on pickup: %s\n(10%% delivery fee included)"),
            itemCount, costStr)
        YesNoDialog.show(function(yes)
            if not yes then return end
            DepotDeliveryOrderEvent.sendToServer(self.depotId, farmId)
            self:showStatus(tr("fd_delivery_order_placed", "Order placed - drive to the Pickup Zone."))
        end, nil, text)

    elseif delivery.status == DeliverySystem.STATUS.PENDING then
        -- Cancel with penalty warning
        local penalty  = delivery.deliveryCost * DepotConstants.DELIVERY.CANCEL_PENALTY
        local penaltyStr = string.format("$%.2f", penalty)
        local text = string.format(
            tr("fd_delivery_cancel_confirm",
               "Cancel delivery order?\n\nA penalty of %s (20%% of delivery cost) will be deducted."),
            penaltyStr)
        YesNoDialog.show(function(yes)
            if not yes then return end
            DepotDeliveryCancelEvent.sendToServer(self.depotId, farmId)
            self:showStatus(tr("fd_delivery_cancelled", "Delivery cancelled."))
        end, nil, text)

    elseif delivery.status == DeliverySystem.STATUS.LOADED then
        -- Complete delivery
        local text = tr("fd_delivery_complete_confirm",
            "Complete delivery and stock your depot?\n\nAll ordered fill types will be added to storage.")
        YesNoDialog.show(function(yes)
            if not yes then return end
            DepotDeliveryCompleteEvent.sendToServer(self.depotId, farmId)
            self:showStatus(tr("fd_delivery_completing", "Delivery complete! Stock has been added."))
        end, nil, text)
    end
end

-- ─── Close ───────────────────────────────────────────────

function DepotDialog:onClickClose()
    self:close()
end

-- ─── Pagination ──────────────────────────────────────────

function DepotDialog:onPrevPage()
    self.pageIndex = math.max(0, self.pageIndex - DepotDialog.ROWS)
    self:refresh()
end

function DepotDialog:onNextPage()
    local total
    if self.tab == DepotDialog.TAB_BUY then
        total = #(self.buyList or self.fillTypes)
    elseif self.tab == DepotDialog.TAB_SELL then
        total = #self.sellList
    else
        total = 0
    end
    self.pageIndex = math.min(self.pageIndex + DepotDialog.ROWS, math.max(0, total - 1))
    self:refresh()
end

-- ─── Generated Row Callbacks ─────────────────────────────

function DepotDialog:onAction0() self:onRowAction(1) end
function DepotDialog:onAction1() self:onRowAction(2) end
function DepotDialog:onAction2() self:onRowAction(3) end
function DepotDialog:onAction3() self:onRowAction(4) end
function DepotDialog:onAction4() self:onRowAction(5) end
function DepotDialog:onAction5() self:onRowAction(6) end
function DepotDialog:onAction6() self:onRowAction(7) end
function DepotDialog:onAction7() self:onRowAction(8) end
