PlaceableSpeedCamera = {
    prerequisitesPresent = function(specializations)
        return true
    end,
    TRANSACTION_FEE = 0.9
}

-- =====================================================
-- Register
-- =====================================================

function PlaceableSpeedCamera.registerFunctions(placeableType)
    SpecializationUtil.registerFunction(placeableType, "onSpeedCameraTriggerCallback", PlaceableSpeedCamera.onSpeedCameraTriggerCallback)
    SpecializationUtil.registerFunction(placeableType, "calculateCost", PlaceableSpeedCamera.calculateCost)
    SpecializationUtil.registerFunction(placeableType, "round", PlaceableSpeedCamera.round)
    SpecializationUtil.registerFunction(placeableType, "openConfigDialog", PlaceableSpeedCamera.openConfigDialog)
    SpecializationUtil.registerFunction(placeableType, "askLimit", PlaceableSpeedCamera.askLimit)
    SpecializationUtil.registerFunction(placeableType, "askCost", PlaceableSpeedCamera.askCost)
    SpecializationUtil.registerFunction(placeableType, "applyConfig", PlaceableSpeedCamera.applyConfig)
    SpecializationUtil.registerFunction(placeableType, "cfgFallbackWarn", PlaceableSpeedCamera.cfgFallbackWarn)
    SpecializationUtil.registerFunction(placeableType, "updatePlayerProximity", PlaceableSpeedCamera.updatePlayerProximity)
    SpecializationUtil.registerFunction(placeableType, "showInput", PlaceableSpeedCamera.showInput)
    SpecializationUtil.registerFunction(placeableType, "applyConfigValues", PlaceableSpeedCamera.applyConfigValues)
end

function PlaceableSpeedCamera.registerEventListeners(placeableType)
    SpecializationUtil.registerEventListener(placeableType, "onLoad", PlaceableSpeedCamera)
    SpecializationUtil.registerEventListener(placeableType, "onDelete", PlaceableSpeedCamera)
    SpecializationUtil.registerEventListener(placeableType, "onUpdate", PlaceableSpeedCamera)
    SpecializationUtil.registerEventListener(placeableType, "onFinalizePlacement", PlaceableSpeedCamera)
    SpecializationUtil.registerEventListener(placeableType, "onWriteStream", PlaceableSpeedCamera)
    SpecializationUtil.registerEventListener(placeableType, "onReadStream", PlaceableSpeedCamera)
end

function PlaceableSpeedCamera.registerXMLPaths(schema, basePath)
    schema:setXMLSpecializationType("speedCamera")
    basePath = basePath .. ".speedCamera"

    schema:register(XMLValueType.NODE_INDEX, basePath .. "#triggerNode")
    schema:register(XMLValueType.NODE_INDEX, basePath .. "#flashLightNode")
    schema:register(XMLValueType.NODE_INDEX, basePath .. "#triggerMarkersNode")

    schema:register(XMLValueType.INT, basePath .. ".speedLimit")
    schema:register(XMLValueType.INT, basePath .. ".costPerKMH")
    schema:register(XMLValueType.INT, basePath .. ".fine1")
    schema:register(XMLValueType.INT, basePath .. ".fine2")
    schema:register(XMLValueType.INT, basePath .. ".fine3")
    schema:register(XMLValueType.BOOL, basePath .. ".ownerGetsMoney")

    schema:setXMLSpecializationType()
end

-- =====================================================
-- Load
-- =====================================================

-- Helper de log simples
function DepotLog_scLog(msg)
    print("[SpeedCameraBR] " .. tostring(msg))
end


function PlaceableSpeedCamera:onLoad(savegame)
    local spec = {}
    self.spec_placeableSpeedCamera = spec

    local key = "placeable.speedCamera"

    spec.trigger = self.xmlFile:getValue(key .. "#triggerNode", nil, self.components, self.i3dMappings)
    if spec.trigger == nil then
        Logging.xmlError(self.xmlFile, "Missing triggerNode for speedCamera")
        return
    end

    addTrigger(spec.trigger, "onSpeedCameraTriggerCallback", self)

    spec.flashLight = self.xmlFile:getValue(key .. "#flashLightNode", nil, self.components, self.i3dMappings)
    spec.triggerMarkers = self.xmlFile:getValue(key .. "#triggerMarkersNode", nil, self.components, self.i3dMappings)

    -- Lê o limite PURO do XML (a tolerância de 10% é calculada na hora da multa)
    spec.speedLimit = self.xmlFile:getInt(key .. ".speedLimit", 40)

    -- Custo por km/h acima da tolerância (multa proporcional). Padrão: 25.
    spec.costPerKMH = self.xmlFile:getInt(key .. ".costPerKMH", 25)

    -- (compatibilidade: ainda lê fines antigos, mas não são mais usados no cálculo)
    spec.fines = {
        self.xmlFile:getInt(key .. ".fine1", 250) or 250,
        self.xmlFile:getInt(key .. ".fine2", 250) or 250,
        self.xmlFile:getInt(key .. ".fine3", 250) or 250,
    }

    spec.ownerGetsMoney = self.xmlFile:getBool(key .. ".ownerGetsMoney", false)

    DepotLog_scLog(string.format(
        "Radar carregado: limite=%d km/h · %d por km/h acima da tolerância (10%%)",
        spec.speedLimit, spec.costPerKMH))

    spec.count = 0
    spec.timer = 0
    spec.flashed = false

    if spec.flashLight ~= nil then
        setVisibility(spec.flashLight, false)
    end

    if spec.triggerMarkers ~= nil then
        setVisibility(spec.triggerMarkers, true)
    end
end

-- =====================================================
-- Save / Load
-- =====================================================

function PlaceableSpeedCamera:saveToXMLFile(xmlFile, key, usedModNames)
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    xmlFile:setInt(key .. ".speedLimit", spec.speedLimit)
    xmlFile:setInt(key .. ".costPerKMH", spec.costPerKMH or 25)
    xmlFile:setBool(key .. ".ownerGetsMoney", spec.ownerGetsMoney)
end

function PlaceableSpeedCamera:loadFromXMLFile(xmlFile, key)
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    spec.speedLimit = xmlFile:getInt(key .. ".speedLimit", spec.speedLimit)
    spec.costPerKMH = xmlFile:getInt(key .. ".costPerKMH", spec.costPerKMH or 25)
    spec.ownerGetsMoney = xmlFile:getBool(key .. ".ownerGetsMoney", spec.ownerGetsMoney)
end

-- Sincronização inicial: servidor envia os valores atuais quando um cliente entra
function PlaceableSpeedCamera:onWriteStream(streamId, connection)
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end
    streamWriteFloat32(streamId, spec.speedLimit or 40)
    streamWriteInt32(streamId, spec.costPerKMH or 25)
end

function PlaceableSpeedCamera:onReadStream(streamId, connection)
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end
    spec.speedLimit = streamReadFloat32(streamId)
    spec.costPerKMH = streamReadInt32(streamId)
    DepotLog_scLog(string.format(
        "Radar sincronizado ao entrar: limite=%d custo/kmh=%d",
        math.floor(spec.speedLimit), spec.costPerKMH))
end

-- =====================================================
-- Delete / Finalize
-- =====================================================

function PlaceableSpeedCamera:onDelete()
    local spec = self.spec_placeableSpeedCamera
    -- Remove o activatable se estiver registrado
    if spec ~= nil and spec.activatable ~= nil and spec.activatableRegistered then
        if g_currentMission ~= nil and g_currentMission.activatableObjectsSystem ~= nil then
            g_currentMission.activatableObjectsSystem:removeActivatable(spec.activatable)
        end
        spec.activatableRegistered = false
    end
    if spec ~= nil and spec.trigger ~= nil then
        removeTrigger(spec.trigger)
        spec.trigger = nil
    end
end

function PlaceableSpeedCamera:onFinalizePlacement()
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    if spec.triggerMarkers ~= nil then
        setVisibility(spec.triggerMarkers, false)
    end

    if spec.flashLight ~= nil then
        setVisibility(spec.flashLight, false)
    end

    -- Cria o objeto de interação (configurar pelo E)
    if SpeedCameraActivatable ~= nil then
        spec.activatable = SpeedCameraActivatable.new(self)
        -- Ativa o loop de update (pra detecção de proximidade do player)
        self:raiseActive()
    end

    -- ===== PROPRIEDADE: a 1ª fazenda a colocar vira dona =====
    if g_currentMission ~= nil and g_currentMission:getIsServer() then
        local placingFarmId = self:getOwnerFarmId()
        if SpeedCameraOwner ~= nil and placingFarmId ~= nil and placingFarmId > 0 then
            if not SpeedCameraOwner.hasOwner() then
                -- Primeira colocação: esta fazenda vira dona
                SpeedCameraOwner.claimOwnership(placingFarmId)
                if SC_OwnerSyncEvent ~= nil then
                    SC_OwnerSyncEvent.broadcast()
                end
                DepotLog_scLog(string.format(
                    "Fazenda %d colocou o 1º radar e virou DONA", placingFarmId))
            elseif not SpeedCameraOwner.isOwner(placingFarmId) then
                -- Outra fazenda tentou colocar → BLOQUEIA (remove o radar)
                DepotLog_scLog(string.format(
                    "Fazenda %d tentou colocar radar, mas o dono é a fazenda %d → BLOQUEADO",
                    placingFarmId, SpeedCameraOwner.ownerFarmId))

                -- Devolve o dinheiro da compra e remove o radar
                local price = 0
                if self.getPrice ~= nil then
                    local ok, p = pcall(function() return self:getPrice() end)
                    if ok and p then price = p end
                end
                if price > 0 then
                    g_currentMission:addMoney(price, placingFarmId, MoneyType.SHOP_PROPERTY_BUY, true, true)
                end

                -- Avisa o jogador
                if g_currentMission.hud ~= nil and g_currentMission.hud.addSideNotification ~= nil then
                    local msg = string.format(
                        g_i18n:getText("sc_already_owned"),
                        SpeedCameraOwner.getOwnerName())
                    g_currentMission.hud:addSideNotification({1.0, 0.3, 0.3, 1.0}, msg)
                end

                -- Remove o radar (agenda deleção segura)
                spec.blockedPlacement = true
                self:delete()
                return
            end
        end
    end
end

-- =====================================================
-- Update
-- =====================================================

function PlaceableSpeedCamera:onUpdate(dt)
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    if spec.flashed then
        spec.timer = spec.timer + dt
        self:raiseActive()
    end

    if spec.timer >= 125 then
        spec.timer = 0
        spec.flashed = false

        if spec.flashLight ~= nil then
            setVisibility(spec.flashLight, false)
        end
    end

    -- ===== Detecção de proximidade do PLAYER (pra configurar pelo E) =====
    -- Verifica a cada ~500ms se o player-dono está perto, e registra/remove
    -- o activatable. (O trigger do i3d só pega veículos, então usamos distância.)
    spec.proxTimer = (spec.proxTimer or 0) + dt
    if spec.proxTimer >= 500 then
        spec.proxTimer = 0
        self:updatePlayerProximity()
        self:raiseActive()  -- mantém o update rodando
    else
        self:raiseActive()
    end
end

-- Registra/remove o activatable conforme o player-dono se aproxima
function PlaceableSpeedCamera:updatePlayerProximity()
    local spec = self.spec_placeableSpeedCamera
    if spec == nil or spec.activatable == nil then return end
    if g_currentMission == nil or g_currentMission.activatableObjectsSystem == nil then return end

    -- Precisa ter um player local a pé
    if g_localPlayer == nil then return end

    -- Player está a pé? (não dentro de veículo)
    local isOnFoot = true
    if g_localPlayer.getIsInVehicle ~= nil then
        local ok, inVeh = pcall(function() return g_localPlayer:getIsInVehicle() end)
        if ok and inVeh then isOnFoot = false end
    end

    local shouldBeActive = false

    if isOnFoot and self.rootNode ~= nil then
        -- Distância entre player e radar
        local px, py, pz = getWorldTranslation(g_localPlayer.rootNode)
        local rx, ry, rz = getWorldTranslation(self.rootNode)
        local dx, dy, dz = px - rx, py - ry, pz - rz
        local dist = math.sqrt(dx*dx + dy*dy + dz*dz)

        if dist <= 5.0 then  -- 5 metros de alcance
            shouldBeActive = true
        end
    end

    -- (des)registra conforme necessário
    if shouldBeActive and not spec.activatableRegistered then
        g_currentMission.activatableObjectsSystem:addActivatable(spec.activatable)
        spec.activatableRegistered = true
    elseif not shouldBeActive and spec.activatableRegistered then
        g_currentMission.activatableObjectsSystem:removeActivatable(spec.activatable)
        spec.activatableRegistered = false
    end
end

-- =====================================================
-- Logic
-- =====================================================

function PlaceableSpeedCamera:calculateCost(spec, speed)
    local overSpeed = speed - g_i18n:getSpeed(spec.speedLimit)
    local cost = 0

    if overSpeed > 20 then
        cost = (overSpeed * 4 - 40) * spec.costPerKMH
    else
        cost =
            (((-3.7625e-06) * overSpeed ^ 6) +
            ((1.609262e-04) * overSpeed ^ 5) +
            ((-0.0023444736) * overSpeed ^ 4) +
            ((0.144011223) * overSpeed ^ 3) +
            ((1.35190505) * overSpeed ^ 2) +
            ((47.92206974) * overSpeed) - 0.5611455)
            * spec.costPerKMH + 1
    end

    return math.ceil(cost)
end

function PlaceableSpeedCamera:round(number)
    if math.abs(number - math.floor(number)) >= 0.5 then
        return math.ceil(number)
    end
    return math.floor(number)
end

-- =====================================================
-- Trigger
-- =====================================================

function PlaceableSpeedCamera:onSpeedCameraTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay)
    if not (onEnter or onLeave) then
        return
    end

    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    local vehicle = g_currentMission:getNodeObject(otherId)
    if vehicle == nil or vehicle.spec_drivable == nil or vehicle.spec_enterable == nil then
        return
    end

    if onEnter then
        local vehicleSpeed = self:round(vehicle:getLastSpeed())

        -- Radares desligados globalmente? Não multa.
        if SpeedCameraConfig ~= nil and not SpeedCameraConfig.enabled then
            return
        end

        -- Cooldown por veículo: evita multar 6x pelo mesmo veículo
        -- (o trigger dispara várias vezes por causa dos componentes de colisão).
        spec.recentlyFined = spec.recentlyFined or {}
        local vId = NetworkUtil.getObjectId(vehicle) or tostring(vehicle)
        local now = g_currentMission and g_currentMission.time or 0
        local lastFined = spec.recentlyFined[vId] or 0
        local onCooldown = (now - lastFined) < 5000  -- 5 segundos

        -- ===== TOLERÂNCIA: 10% acima do limite não multa =====
        -- Ex: limite 100 → tolerância até 110 km/h (livre)
        local baseLimit = spec.speedLimit  -- já é o limite "puro" (sem tolerância no onLoad novo)
        local tolerance = baseLimit * 1.10  -- 10% acima
        local tolKmh = g_i18n:getSpeed(tolerance)

        if vehicleSpeed > tolKmh and not onCooldown then
            spec.recentlyFined[vId] = now

            -- km/h acima da TOLERÂNCIA (não do limite)
            local overTolerance = vehicleSpeed - tolKmh

            DepotLog_scLog(string.format(
                "INFRAÇÃO! speed=%d limite=%d tolerância=%d (%.0f km/h acima da tol.)",
                vehicleSpeed, math.floor(g_i18n:getSpeed(baseLimit)),
                math.floor(tolKmh), overTolerance))
            spec.flashed = true

            if spec.flashLight ~= nil then
                setVisibility(spec.flashLight, true)
                self:raiseActive()
            end

            local isServer = g_currentMission:getIsServer()

            if isServer then
                local offenderFarmId = vehicle:getActiveFarm()

                -- ===== MULTA PROPORCIONAL =====
                -- valor = (custo por km/h) × (km/h acima da tolerância)
                local costPerKMH = spec.costPerKMH or 25
                local money = math.floor(costPerKMH * overTolerance)
                if money < 1 then money = 1 end

                -- Velocidades reais (pra mensagem)
                local realSpeed  = self:round(vehicleSpeed)
                local realLimit  = self:round(g_i18n:getSpeed(baseLimit))
                local realOver   = self:round(overTolerance)

                -- ===== COBRANÇA AUTOMÁTICA =====
                -- Tira a multa da conta do infrator e envia pra fazenda DONA dos radares.
                g_currentMission:addMoney(-money, offenderFarmId, MoneyType.OTHER, true, true)

                local destFarmId = SpeedCameraOwner and SpeedCameraOwner.ownerFarmId or 0
                if destFarmId > 0 and destFarmId ~= offenderFarmId then
                    g_currentMission:addMoney(money, destFarmId, MoneyType.OTHER, true, true)
                    DepotLog_scLog(string.format(
                        "Multa: infrator=%d -%d → dono dos radares=%d",
                        offenderFarmId, money, destFarmId))
                else
                    DepotLog_scLog(string.format(
                        "Multa: infrator=%d -%d (dono é o próprio infrator ou sem dono)",
                        offenderFarmId, money))
                end

                -- Mensagem em português na tela do infrator
                local moneyStr = g_i18n:formatMoney(money, 0, true)
                local text = string.format(
                    g_i18n:getText("sc_fine_message"), moneyStr, realOver)

                g_currentMission:addIngameNotification(
                    FSBaseMission.INGAME_NOTIFICATION_CRITICAL, text)
            end
        end
    end
end

-- =====================================================
-- Integração com a Prefeitura (nota fiscal)
-- =====================================================

-- Descobre o objeto PrefeituraMunicipal (com fallbacks, padrão do Dynamic Finance)
function PlaceableSpeedCamera.findPrefeitura()
    if PrefeituraMunicipal ~= nil then
        return PrefeituraMunicipal
    end
    if g_currentMission ~= nil then
        return g_currentMission.prefeituraMunicipal or g_currentMission.FS25_PrefeituraMunicipal
    end
    if _G ~= nil then
        return _G.PrefeituraMunicipal or _G.g_prefeituraMunicipal
    end
    return nil
end

-- Descobre o manager de notas (com fallback)
function PlaceableSpeedCamera.findInvoicesManager()
    if g_currentMission ~= nil and g_currentMission.pmInvoicesManager ~= nil then
        return g_currentMission.pmInvoicesManager
    end
    if PrefeituraMunicipal ~= nil and PrefeituraMunicipal.pmInvoicesManager ~= nil then
        return PrefeituraMunicipal.pmInvoicesManager
    end
    return nil
end

-- Resolve a classe PmInvoice de forma robusta.
-- Ela é carregada via source() pela Prefeitura; o escopo entre mods pode
-- não compartilhar direto, então tentamos várias fontes.
function PlaceableSpeedCamera.resolvePmInvoiceClass(manager)
    -- 1) Escopo global direto
    if rawget(_G, "PmInvoice") ~= nil then
        return rawget(_G, "PmInvoice")
    end
    if PmInvoice ~= nil then
        return PmInvoice
    end

    -- 2) Via metatable de uma nota já existente no repositório
    if manager ~= nil and manager.repository ~= nil then
        local ok, all = pcall(function() return manager.repository:getAll() end)
        if ok and all ~= nil then
            for _, inv in pairs(all) do
                local mt = getmetatable(inv)
                if mt ~= nil and mt.__index ~= nil then
                    -- mt.__index é a "classe" PmInvoice
                    if mt.__index.new ~= nil and mt.__index.populateFromData ~= nil then
                        return mt.__index
                    end
                end
            end
        end
    end

    return nil
end

function PlaceableSpeedCamera.tryEmitInvoice(offenderFarmId, money, realSpeed, realLimit, realOver)
    local manager = PlaceableSpeedCamera.findInvoicesManager()
    if manager == nil then
        DepotLog_scLog("Prefeitura não instalada (sem manager) → fallback desconto")
        return false
    end

    -- Emissor = tesouraria da Prefeitura (definida internamente por ela).
    local senderFarmId = 0
    local prefeitura = PlaceableSpeedCamera.findPrefeitura()
    if prefeitura ~= nil and prefeitura.treasuryFarmId ~= nil then
        senderFarmId = tonumber(prefeitura.treasuryFarmId) or 0
    end
    -- Fallback: se não achou a tesouraria, tenta a config do SpeedCamera
    if senderFarmId == 0 then
        senderFarmId = SpeedCameraConfig and SpeedCameraConfig.destFarmId or 0
    end
    if senderFarmId == 0 then
        DepotLog_scLog("Sem tesouraria/destino → fallback desconto")
        return false
    end

    -- Não emite nota pra própria tesouraria (evita nota de si pra si)
    if senderFarmId == offenderFarmId then
        DepotLog_scLog("Infrator é a própria tesouraria → sem nota")
        return true  -- considera "tratado" (não desconta nem emite)
    end

    -- Protege contra mudança de API da Prefeitura (não trava o jogo)
    local ok, err = pcall(function()
        local note = string.format(
            g_i18n:getText("sc_invoice_note"),
            realSpeed, realLimit, realOver)

        local items = {{
            workTypeId = 54,   -- "Diversos" na Prefeitura
            amount     = money,
            quantity   = 1,
            unitType   = 1,    -- UNIT_PIECE (valor direto; a classe PmInvoice pode não estar acessível)
            note       = note,
            vatRate    = 0,
            name       = g_i18n:getText("sc_invoice_name"),
            price      = money,
        }}

        -- Resolve a classe PmInvoice (múltiplas fontes)
        local PmInvoiceClass = PlaceableSpeedCamera.resolvePmInvoiceClass(manager)

        if PmInvoiceClass == nil then
            error("classe PmInvoice ainda não acessível (crie 1 nota manual na Prefeitura primeiro)")
        end

        local pmInvoice = PmInvoiceClass.new()
        pmInvoice:populateFromData(0, items, offenderFarmId, senderFarmId)
        manager:createAndSendPmInvoice(pmInvoice)
    end)

    if not ok then
        DepotLog_scLog("Falha ao emitir nota fiscal: " .. tostring(err))
        return false  -- deu erro → cai no fallback
    end

    DepotLog_scLog(string.format(
        "Nota fiscal emitida: infrator=%d valor=%d (%d km/h, limite %d)",
        offenderFarmId, money, realSpeed, realLimit))
    return true
end

-- =====================================================
-- Configuração pelo E (sequência de caixas de digitação)
-- =====================================================

-- Abre a sequência: limite → custo por km/h
function PlaceableSpeedCamera:openConfigDialog()
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    DepotLog_scLog("openConfigDialog chamado — abrindo caixa de limite")

    -- Estado temporário da edição
    self._cfgTemp = {
        limit = math.floor(spec.speedLimit),
        cost  = math.floor(spec.costPerKMH or 25),
    }

    self:askLimit()
end

-- Helper: abre uma caixa de digitação nativa (assinatura correta do FS25)
function PlaceableSpeedCamera:showInput(titleKey, currentValue, onDone)
    local self_ref = self
    local ok = pcall(function()
        TextInputDialog.show(
            function(_, value, clickOk)
                if clickOk and value ~= nil then
                    local n = tonumber(value)
                    if n ~= nil then onDone(math.floor(n)) end
                end
            end,
            self_ref,                              -- target
            tostring(currentValue),                -- defaultText
            g_i18n:getText(titleKey),              -- title/dialogPrompt
            nil,                                    -- imagePath
            7,                                      -- maxCharacters
            g_i18n:getText("button_ok")            -- confirmText
        )
    end)
    if not ok then
        local ok2 = pcall(function()
            g_gui:showTextInputDialog({
                target = self_ref,
                callback = function(_, value, clickOk)
                    if clickOk and value ~= nil then
                        local n = tonumber(value)
                        if n ~= nil then onDone(math.floor(n)) end
                    end
                end,
                defaultText = tostring(currentValue),
                dialogPrompt = g_i18n:getText(titleKey),
                maxCharacters = 7,
                confirmText = g_i18n:getText("button_ok"),
            })
        end)
        if not ok2 then
            self:cfgFallbackWarn()
            return false
        end
    end
    return true
end

-- Passo 1: limite de velocidade
function PlaceableSpeedCamera:askLimit()
    local self_ref = self
    self:showInput("sc_cfg_limit_title", self._cfgTemp.limit, function(n)
        if n > 0 then self_ref._cfgTemp.limit = n end
        self_ref:askCost()
    end)
end

-- Passo 2: custo por km/h → aplica e salva
function PlaceableSpeedCamera:askCost()
    local self_ref = self
    self:showInput("sc_cfg_cost_title", self._cfgTemp.cost, function(n)
        if n >= 0 then self_ref._cfgTemp.cost = n end
        self_ref:applyConfig()
    end)
end

-- Chamado ao terminar de digitar: envia a config pra rede (MP-safe)
function PlaceableSpeedCamera:applyConfig()
    local spec = self.spec_placeableSpeedCamera
    if spec == nil or self._cfgTemp == nil then return end

    local t = self._cfgTemp

    -- Envia pra rede: o servidor aplica, salva e sincroniza pra todos.
    if SC_RadarConfigEvent ~= nil then
        SC_RadarConfigEvent.send(self, t.limit, t.cost)
    else
        self:applyConfigValues(t.limit, t.cost, 0, 0)
    end

    -- Feedback imediato na tela de quem configurou
    if g_currentMission.hud ~= nil and g_currentMission.hud.addSideNotification ~= nil then
        local tol = math.floor(t.limit * 1.10)
        local msg = string.format(
            g_i18n:getText("sc_cfg_saved"), t.limit, tol, t.cost)
        g_currentMission.hud:addSideNotification({0.4, 0.9, 0.4, 1.0}, msg)
    end

    self._cfgTemp = nil
end

-- Aplica os valores de fato ao radar (chamado pelo evento em servidor E clientes)
function PlaceableSpeedCamera:applyConfigValues(speedLimit, costPerKMH, unused2, unused3)
    local spec = self.spec_placeableSpeedCamera
    if spec == nil then return end

    spec.speedLimit = speedLimit
    spec.costPerKMH = costPerKMH

    DepotLog_scLog(string.format(
        "Radar configurado (aplicado): limite=%d · %d por km/h acima da tolerância [%s]",
        math.floor(spec.speedLimit), costPerKMH,
        g_currentMission:getIsServer() and "servidor" or "cliente"))
end

function PlaceableSpeedCamera:cfgFallbackWarn()
    if g_currentMission.hud ~= nil and g_currentMission.hud.addSideNotification ~= nil then
        g_currentMission.hud:addSideNotification({1.0, 0.5, 0.3, 1.0},
            "Não foi possível abrir a configuração neste momento.")
    end
end
