-- EmSayLongerDays60
-- Sets Subnautica 2 day length to 60 real minutes.
-- Safer version: limited retries, no permanent object lock, protected calls.

local MOD_NAME = "EmSayLongerDays60"

local TARGET_DAY_LENGTH_MINUTES = 60

local INITIAL_DELAY_MS = 5000
local RETRY_DELAY_MS = 5000
local MAX_ATTEMPTS = 12

local runId = 0

local function Log(message)
    print("[" .. MOD_NAME .. "] " .. tostring(message) .. "\n")
end

local function IsValidObject(object)
    return object ~= nil and object:IsValid()
end

local function GetObjectName(object)
    if not IsValidObject(object) then
        return "InvalidObject"
    end

    local ok, name = pcall(function()
        return object:GetFullName()
    end)

    if ok and name ~= nil then
        return name
    end

    return "UnknownObject"
end

local function ApplyDayLength(component)
    if not IsValidObject(component) then
        return false
    end

    if component.DayLengthMinutes == nil then
        return false
    end

    local oldValue = tonumber(component.DayLengthMinutes)

    if oldValue == TARGET_DAY_LENGTH_MINUTES then
        return false
    end

    local ok, err = pcall(function()
        component.DayLengthMinutes = TARGET_DAY_LENGTH_MINUTES
    end)

    if ok then
        Log(
            "Patched " ..
            GetObjectName(component) ..
            " | DayLengthMinutes " ..
            tostring(oldValue) ..
            " -> " ..
            tostring(component.DayLengthMinutes)
        )

        return true
    else
        Log("Failed to patch DayLengthMinutes: " .. tostring(err))
        return false
    end
end

local function ApplyPass(currentRunId, attempt)
    if currentRunId ~= runId then
        return
    end

    ExecuteInGameThread(function()
        local ok, err = pcall(function()
            local components = FindAllOf("UWETimeOfDayComponent")

            if not components or #components == 0 then
                if attempt == 1 or attempt == MAX_ATTEMPTS then
                    Log("No UWETimeOfDayComponent found. Attempt " .. tostring(attempt) .. "/" .. tostring(MAX_ATTEMPTS))
                end
            else
                local patched = 0

                for _, component in ipairs(components) do
                    if ApplyDayLength(component) then
                        patched = patched + 1
                    end
                end

                if patched > 0 then
                    Log("Patched components this pass: " .. tostring(patched))
                end
            end
        end)

        if not ok then
            Log("Apply pass error: " .. tostring(err))
        end

        if currentRunId == runId and attempt < MAX_ATTEMPTS then
            ExecuteWithDelay(RETRY_DELAY_MS, function()
                ApplyPass(currentRunId, attempt + 1)
            end)
        elseif currentRunId == runId then
            Log("Finished limited apply loop. Target day length = " .. tostring(TARGET_DAY_LENGTH_MINUTES) .. " minutes.")
        end
    end)
end

local function StartApplyLoop()
    runId = runId + 1

    local currentRunId = runId

    ExecuteWithDelay(INITIAL_DELAY_MS, function()
        ApplyPass(currentRunId, 1)
    end)
end

RegisterHook("/Script/Engine.PlayerController:ClientRestart", function()
    Log("PlayerController restart detected. Starting safe day-length apply loop.")
    StartApplyLoop()
end)

Log("Loaded. Target DayLengthMinutes = " .. tostring(TARGET_DAY_LENGTH_MINUTES))