function init()
    connect(g_app, {
        onExit = exit
    })

    local minSize
    if g_platform.isMobile() then
        minSize = { width = 640, height = 360 }
    else
        minSize = { width = 1020, height = 644 }
    end
    g_window.setMinimumSize(minSize)

    -- window size (treat values as logical points)
    local settings = g_configs.getSettings()
    local size = { width = 1020, height = 644 }
    size = g_settings.getSize('window-size', size)

    -- Compute display size in points (SDL reports pixels; divide by density)
    local scale = g_window.getDisplayDensity()
    if scale < 1 then scale = 1 end
    local displayPx = g_window.getDisplaySize()
    local display = {
        width = math.max(1, math.floor(displayPx.width  / scale + 0.5)),
        height = math.max(1, math.floor(displayPx.height / scale + 0.5)),
    }

    -- Always enforce minimum logical size (guards against bad or missing values)
    size.width = math.max(minSize.width, size.width)
    size.height = math.max(minSize.height, size.height)

    -- (debug logs removed)

    g_window.resize(size)

    -- window position, default is the screen center (use points)
    local defaultPos = {
        x = math.floor((display.width - size.width) / 2),
        y = math.floor((display.height - size.height) / 2),
    }
    local pos = g_settings.getPoint('window-pos', defaultPos)
    pos.x = math.max(pos.x, 0)
    pos.y = math.max(pos.y, 0)
    g_window.move(pos)

    -- window maximized?
    local maximized = g_settings.getBoolean('window-maximized', false)
    if maximized then g_window.maximize() end

    g_window.setTitle(g_app.getName())
    g_window.setIcon('/images/clienticon')

    -- poll resize events
    g_window.poll()

    -- generate machine uuid, this is a security measure for storing passwords
    if not g_crypt.setMachineUUID(g_settings.get('uuid')) then
        g_settings.set('uuid', g_crypt.getMachineUUID())
        g_settings.save()
    end
end

-- Save window state and settings (called on terminate and before restart)
function saveSettings()
    -- save window configs (use unmaximized frame; on mac this returns points)
    local saveSize = g_window.getUnmaximizedSize()
    local savePos = g_window.getUnmaximizedPos()
    -- fallback if cache not populated (rare): derive from current size/pos
    if not saveSize or saveSize.width <= 1 or saveSize.height <= 1 then
        local scale = g_window.getDisplayDensity()
        if scale < 1 then scale = 1 end
        local px = g_window.getSize() or { width = 1020, height = 644 }
        saveSize = {
            width = math.floor(px.width / scale + 0.5),
            height = math.floor(px.height / scale + 0.5),
        }
    end
    if not savePos or savePos.x < 0 or savePos.y < 0 then
        savePos = g_window.getPosition() or { x = 0, y = 0 }
    end

    -- enforce sane minimums on save to avoid persisting 1x1 or 0x0
    local minSize
    if g_platform.isMobile() then
        minSize = { width = 640, height = 360 }
    else
        minSize = { width = 1020, height = 644 }
    end
    saveSize.width = math.max(minSize.width, saveSize.width)
    saveSize.height = math.max(minSize.height, saveSize.height)
    savePos.x = math.max(0, savePos.x)
    savePos.y = math.max(0, savePos.y)

    g_settings.set('window-size', saveSize)
    g_settings.set('window-pos', savePos)
    -- Mark units/scaling to avoid any future migration ambiguity
    g_settings.set('window-units', 'points')
    g_settings.set('window-scale', tostring(g_window.getDisplayDensity()))
    g_settings.set('window-maximized', g_window.isMaximized())
    g_settings.save()
end

function terminate()
    disconnect(g_app, {
        onExit = exit
    })
    saveSettings()
end

function exit()
    g_logger.info('Exiting application..')
end
