-- this is the first file executed when the application starts
-- we have to load the first modules form here

-- Global helper to check if running in local development environment
-- Checks RELENCIA_ENV environment variable or /relencia.env file
local function isLocalEnv()
    local function readFileTrim(path)
        if g_resources.fileExists(path) then
            local s = g_resources.readFileContents(path)
            if type(s) == 'string' then
                return (s:gsub('^%s+', ''):gsub('%s+$', ''))
            end
        end
        return nil
    end

    local env = os.getenv("RELENCIA_ENV") or ""
    if env == "" then
        env = readFileTrim('/relencia.env') or ""
    end
    return env == "local"
end
_G.isLocalEnv = isLocalEnv -- Make globally accessible to all modules

-- Services holds addresses derived from a single source of truth: the server
-- host name. We keep everything else derived from it.
Services = {
    -- Canonical server hostname (no scheme, no path). Example: "relencia.online".
    server = nil,
    -- Derived API base (scheme + host). Example: "https://relencia.online".
    apiBase = nil,
    -- Derived updater endpoint. Example: "https://relencia.online/api/updater.php".
    updater = nil,
    --status = "http://localhost/login.php", --./client_entergame | ./client_topmenu
    --websites = "http://localhost/?subtopic=accountmanagement", --./client_entergame "Forgot password and/or email"
    --createAccount = "http://localhost/clientcreateaccount.php", --./client_entergame -- createAccount.lua
}

local function deriveFromServer(server)
    if not server or server == '' then return end
    Services.server = server
    -- For simplicity we default to https for API; operators can override later if
    -- they need http in development.
    Services.apiBase = 'https://' .. server
    Services.updater = Services.apiBase .. '/index.php/api/updater.php'
end

-- Helper to read deployment environment from /relencia.env file
local function getDeploymentEnv()
    local function readFileTrim(path)
        if g_resources.fileExists(path) then
            local s = g_resources.readFileContents(path)
            if type(s) == 'string' then
                return (s:gsub('^%s+', ''):gsub('%s+$', ''))
            end
        end
        return nil
    end

    return readFileTrim('/relencia.env') or "prod"
end

do
    local isWasm = (g_app.getBuildArch and g_app.getBuildArch() == "WASM32")
    if not isWasm then
        -- Single source of truth: choose the server host and port based on environment
        local server
        local port

        if isLocalEnv() then
            -- Local development environment
            server = '127.0.0.1'
            port = 7171
            g_logger.info(string.format('Env=local: using %s:%d', server, port))
        else
            -- Determine based on deployment environment from /relencia.env
            local deployEnv = getDeploymentEnv()
            if deployEnv == "stage" then
                server = 'game-test.relencia.online'
                port = 17171
                g_logger.info(string.format('Stage environment: using %s:%d', server, port))
            else
                server = 'game-new.relencia.online'
                port = 7171
                g_logger.info(string.format('Prod environment: using %s:%d', server, port))
            end
        end

        -- API/website uses relencia.online, game server uses game.relencia.online
        if isLocalEnv() then
            deriveFromServer(server)
            Services.apiBase = 'http://127.0.0.1:8082'
            Services.updater = nil -- no updater locally
        else
            -- Website API on main domain
            local websiteDomain = (getDeploymentEnv() == "stage") and 'test.relencia.online' or 'new.relencia.online'
            deriveFromServer(websiteDomain)
        end

        -- For non-local environments, ALL connections must go through g_proxy
        -- because alpha-proxy only speaks the g_proxy multiplexing protocol.
        -- Using "proxy" as host triggers g_proxy mode in Protocol::connect()
        if not isLocalEnv() and g_proxy then
            -- Register the real server address with g_proxy
            g_proxy.addProxy(server, port, 1)
            g_logger.info(string.format('Registered g_proxy: %s:%d', server, port))

            -- Use "proxy" as host to trigger g_proxy mode for ALL connections
            -- (login AND game). The actual server is handled by g_proxy.
            Servers_init = {
                ["proxy"] = { port = port, protocol = 860, httpLogin = false }
            }
        else
            -- Local dev: direct connection to TFS
            Servers_init = {
                [server] = { port = port, protocol = 860, httpLogin = false }
            }
        end

        -- Log final configuration for verification
        g_logger.info('=== Client Connection Configuration ===')
        g_logger.info(string.format('  Game Server:  %s:%d', server, port))
        g_logger.info(string.format('  API Base:     %s', tostring(Services.apiBase)))
        g_logger.info(string.format('  Updater:      %s', tostring(Services.updater)))
        g_logger.info('========================================')
    end
end

-- Note: For WASM we only set WebBridge/Servers_init after loading runtime
-- config. We do not prefill localhost defaults here to avoid accidental
-- fallbacks in production. Local dev can still supply relencia.config.json.

g_app.setName("Relencia Online");
g_app.setCompactName("relencia");
-- Use Relencia as the organization name so OS-specific
-- preference/app-data folders are created under Relencia
-- instead of the previous otcr directory.
g_app.setOrganizationName("Relencia");

g_app.hasUpdater = function()
    -- Disable updater in WebAssembly builds for local dev to avoid DNS/CORS issues.
    -- Also disable updater on macOS desktop builds (server manifest is legacy for now).
    if g_app.getBuildArch and g_app.getBuildArch() == "WASM32" then
        return false
    end
    if g_app.getOs and g_app.getOs() == "mac" then
        return false
    end
    return (Services.updater and Services.updater ~= "" and g_modules.getModule("updater"))
end

-- setup logger
g_logger.setLogFile(g_resources.getWorkDir() .. g_app.getCompactName() .. '.log')
g_logger.info(os.date('== application started at %b %d %Y %X'))
g_logger.info("== operating system: " .. g_platform.getOSName())

-- print first terminal message
g_logger.info(g_app.getName() .. ' ' .. g_app.getVersion() .. ' rev ' .. g_app.getBuildRevision() .. ' (' ..
    g_app.getBuildCommit() .. ') built on ' .. g_app.getBuildDate() .. ' for arch ' ..
    g_app.getBuildArch())

-- setup lua debugger
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
    require("lldebugger").start()
    g_logger.debug("Started LUA debugger.")
else
    g_logger.debug("LUA debugger not started (not launched with VSCode local-lua).")
end

-- add data directory to the search path
if not g_resources.addSearchPath(g_resources.getWorkDir() .. 'data', true) then
    g_logger.fatal('Unable to add data directory to the search path.')
end

-- add modules directory to the search path
if not g_resources.addSearchPath(g_resources.getWorkDir() .. 'modules', true) then
    g_logger.fatal('Unable to add modules directory to the search path.')
end

-- try to add mods path too
g_resources.addSearchPath(g_resources.getWorkDir() .. 'mods', true)

-- setup directory for saving configurations
g_resources.setupUserWriteDir(('%s/'):format(g_app.getCompactName()))

-- search all packages
g_resources.searchAndAddPackages('/', '.otpkg', true)

-- load settings
g_configs.loadSettings('/config.otml')

g_modules.discoverModules()

-- libraries modules 0-99
g_modules.autoLoadModules(99)
g_modules.ensureModuleLoaded('corelib')
g_modules.ensureModuleLoaded('gamelib')
g_modules.ensureModuleLoaded('modulelib')
g_modules.ensureModuleLoaded("startup")

-- REPL server: Lua REPL over TCP for testing/scripting the client
-- Enabled for local development
if isLocalEnv() then
    dofile('/modules/repl/init.lua')
end

-- Headless mode: hide window for automated testing
if os.getenv("RELENCIA_HEADLESS") == "1" then
    g_resources.setupUserWriteDir('relencia-headless-' .. os.time() .. '/')
    connect(g_app, { onRun = function() g_window.hide() end })
end

-- After core libs exist, ensure default client version for fresh browsers
if g_app.getBuildArch and g_app.getBuildArch() == "WASM32" then
    if g_settings and g_settings.getInteger then
        local cv = g_settings.getInteger('client-version')
        if not cv or cv == 0 then
            g_settings.set('client-version', 860)
        end
    end
end

g_modules.autoLoadModules(999)
g_modules.ensureModuleLoaded('game_shaders') -- pre load

local function loadModules()
    -- client modules 100-499
    g_modules.autoLoadModules(499)
    g_modules.ensureModuleLoaded('client')

    -- Ensure default client version is sane on first run (WASM)
    if g_app.getBuildArch and g_app.getBuildArch() == "WASM32" then
        if g_settings and g_settings.getInteger then
            local cv = g_settings.getInteger('client-version')
            if not cv or cv == 0 then
                local def = 860
                g_settings.set('client-version', def)
                if g_game and g_game.setClientVersion then
                    g_game.setClientVersion(def)
                    if g_game.getClientProtocolVersion then
                        g_game.setProtocolVersion(g_game.getClientProtocolVersion(def))
                    end
                end
            end
        end
    end

    -- game modules 500-999
    g_modules.autoLoadModules(999)
    g_modules.ensureModuleLoaded('game_interface')

    -- mods 1000-9999
    g_modules.autoLoadModules(9999)
    g_modules.ensureModuleLoaded('client_mods')

    local script = '/' .. g_app.getCompactName() .. 'rc.lua'

    if g_resources.fileExists(script) then
        dofile(script)
    end

    -- auto-reload modules in dev mode
    if isLocalEnv() then
        g_modules.enableAutoReload()
    end
end

local function startClient()
    -- run updater, must use data.zip
    if g_app.hasUpdater() then
        g_modules.ensureModuleLoaded("updater")
        return Updater.init(loadModules)
    end
    loadModules()
end

-- Optional runtime config for WASM via /relencia.config.json generated by Docker env.
if g_app.getBuildArch and g_app.getBuildArch() == "WASM32" then
    local function applyConfig(cfg)
        if type(cfg) == 'table' then
            WebBridge = WebBridge or {}
            -- Prefer explicit cfg.server if provided; else try to infer from loginHost.
            if type(cfg.server) == 'string' and cfg.server ~= '' then
                deriveFromServer(cfg.server)
            end
            if cfg.loginHost then WebBridge.loginHost = cfg.loginHost end
            if cfg.gameHost then WebBridge.gameHost = cfg.gameHost end
            if cfg.loginPort then WebBridge.loginPort = tonumber(cfg.loginPort) end
            if cfg.gamePort then WebBridge.gamePort = tonumber(cfg.gamePort) end
            if type(cfg.apiBase) == 'string' and cfg.apiBase ~= '' then
                Services.apiBase = cfg.apiBase
                local hostOnly = cfg.apiBase:match('^https?://([^/]+)/')
                if hostOnly and hostOnly ~= '' then
                    deriveFromServer(hostOnly)
                end
            end
            if cfg.updaterUrl and type(cfg.updaterUrl) == 'string' then
                Services.updater = cfg.updaterUrl
                -- Backfill server/apiBase if missing and updaterUrl is set
                if not Services.server or not Services.apiBase then
                    local hostOnly = cfg.updaterUrl:match('^https?://([^/]+)/')
                    if hostOnly then deriveFromServer(hostOnly) end
                end
            end

            -- If server still not set, try to infer from loginHost.
            if (not Services.server or Services.server == '') and type(WebBridge.loginHost) == 'string' then
                local hostOnly = tostring(WebBridge.loginHost):match('^([^/:]+)')
                if hostOnly and hostOnly ~= '' then deriveFromServer(hostOnly) end
            end

            -- If updater still not set, derive from server.
            if not Services.updater or Services.updater == '' then
                if Services.server and Services.server ~= '' then
                    Services.updater = (Services.apiBase or ('https://' .. Services.server)) .. '/index.php/api/updater.php'
                end
            end

            Servers_init = {
                [WebBridge.loginHost] = {
                    port = WebBridge.loginPort,
                    protocol = 860,
                    httpLogin = false
                }
            }
            g_logger.info(string.format("WASM cfg: login %s:%d, game %s:%d, updater %s (server=%s)",
                tostring(WebBridge.loginHost), tonumber(WebBridge.loginPort or 0), tostring(WebBridge.gameHost),
                tonumber(WebBridge.gamePort or 0), tostring(Services.updater), tostring(Services.server)))
        end
    end

    -- Prefer file written by shell.html preRun (same-origin fetch). If not
    -- present, try HTTP.getJSON once; on failure, abort.
    local function startWithConfigTable(tbl)
        local ok, cfg = pcall(function()
            if type(tbl) == 'table' then return tbl end
            return json.decode(tbl)
        end)
        if not ok then
            g_logger.fatal('WASM: Invalid relencia.config.json content; refusing to start.')
            return
        end
        applyConfig(cfg)
        startClient()
    end

    if g_resources.fileExists('/relencia.config.json') then
        startWithConfigTable(g_resources.readFileContents('/relencia.config.json'))
        return
    end

    local ok = pcall(function()
        HTTP.getJSON('/relencia.config.json', function(data, err)
            if err then
                -- Zero-config fallback: derive from location
                local host = js.global.location and js.global.location.host or 'localhost'
                -- Strip any :port; we always go through TLS 443 here
                local colon = host:find(':', 1, true)
                if colon then host = host:sub(1, colon - 1) end
                -- Set server and derive everything else from it
                deriveFromServer(host)
                local cfg = {
                    server     = host,
                    loginHost  = host .. '/ws-login',
                    loginPort  = 443,
                    gameHost   = host .. '/ws-game',
                    gamePort   = 443,
                    updaterUrl = (Services.apiBase or ('https://' .. host)) .. '/api/updater.php'
                }
                startWithConfigTable(cfg)
                return
            end
            startWithConfigTable(data)
        end)
    end)
    if ok then return end
end

startClient()
