pragma Singleton
pragma ComponentBehavior: Bound

import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services

Singleton {
    id: root
    readonly property var log: Log.scoped("DisplayService")

    property bool brightnessAvailable: devices.length > 0
    property var devices: []
    property var deviceBrightness: ({})
    property var deviceBrightnessUserSet: ({})
    property var deviceMaxCache: ({})
    property var userControlledDevices: ({})
    property var pendingOsdDevices: ({})
    property int brightnessVersion: 0
    property string currentDevice: ""
    property string lastIpcDevice: ""
    property int brightnessLevel: {
        brightnessVersion;
        const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice);
        if (!deviceToUse) {
            return 50;
        }

        return getDeviceBrightness(deviceToUse);
    }
    property int maxBrightness: 100
    property bool brightnessInitialized: false
    property bool suppressOsd: true

    readonly property int batteryRefreshRateTarget: 60000
    readonly property int batteryRefreshRateTolerance: 1000

    property var _lastAppliedTargets: ({})

    Timer {
        id: cascadeGuard
        interval: 3000
        repeat: false
        onTriggered: root._lastAppliedTargets = ({})
    }

    property string _pendingReason: ""

    Timer {
        id: syncDebounce
        interval: 300
        repeat: false
        onTriggered: root._runSync()
    }

    Timer {
        id: startupRefreshRateSync
        interval: 500
        repeat: false
        running: true
        onTriggered: root.requestSync("startup")
    }

    signal brightnessChanged(bool showOsd)
    signal deviceSwitched

    Connections {
        target: BatteryService
        function onIsPluggedInChanged() {
            root.requestSync("power-change");
        }
    }

    Connections {
        target: SettingsData
        function onLowerDisplayRefreshRateOnBatteryChanged() {
            root.requestSync("setting-change");
        }

        function onActiveDisplayProfileChanged() {
            root.requestSync("profile-change");
        }

        function onActiveDisplayProfileModesChanged() {
            root.requestSync("profile-change");
        }
    }

    Connections {
        target: NiriService
        function onOutputsChanged() {
            root.requestSync("output-change");
        }
    }

    Connections {
        target: WlrOutputService
        function onStateChanged() {
            root.requestSync("output-change");
        }
    }

    property bool nightModeActive: nightModeEnabled

    property bool nightModeEnabled: false
    property bool automationAvailable: false
    property bool gammaControlAvailable: false
    property int resumeRecoveryAttempt: 0

    property var gammaState: ({})
    property int gammaCurrentTemp: gammaState?.currentTemp ?? 0
    property string gammaNextTransition: gammaState?.nextTransition ?? ""
    property string gammaSunriseTime: gammaState?.sunriseTime ?? ""
    property string gammaSunsetTime: gammaState?.sunsetTime ?? ""
    property string gammaDawnTime: gammaState?.dawnTime ?? ""
    property string gammaNightTime: gammaState?.nightTime ?? ""
    property bool gammaIsDay: gammaState?.isDay ?? true
    property real gammaSunPosition: gammaState?.sunPosition ?? 0
    property int gammaLowTemp: gammaState?.config?.LowTemp ?? 0
    property int gammaHighTemp: gammaState?.config?.HighTemp ?? 0

    function syncRefreshRates(isPluggedIn, reason) {
        if (!SettingsData.lowerDisplayRefreshRateOnBattery) {
            if (reason === "setting-change")
                applyConfiguredTargets("disabled", reason);
            return;
        }

        if (!isPluggedIn) {
            applyBatteryTargets(reason);
            return;
        }

        applyConfiguredTargets("AC", reason);
    }

    function requestSync(reason) {
        _pendingReason = reason || _pendingReason;
        syncDebounce.restart();
    }

    function _runSync() {
        syncRefreshRates(BatteryService.isPluggedIn, _pendingReason || "sync");
        _pendingReason = "";
    }

    function outputIdentifiers(outputName, output) {
        const identifiers = [outputName];
        const make = output?.make || "";
        const model = output?.model || "";
        if (make && model) {
            const serial = output.serial || output.serialNumber || "Unknown";
            const modelId = make + " " + model;
            const serialId = modelId + " " + serial;
            const descId = ("desc:" + [make, model, output.serial || output.serialNumber].filter(p => p).join(" ")).replace(/,/g, "");
            if (!identifiers.includes(serialId))
                identifiers.push(serialId);
            if (!identifiers.includes(modelId))
                identifiers.push(modelId);
            if (descId !== "desc:" && !identifiers.includes(descId))
                identifiers.push(descId);
        }
        return identifiers;
    }

    function previousRefreshModes() {
        return SettingsData.displayPreviousRefreshModes?.[CompositorService.compositor] || {};
    }

    function findPreviousRefreshMode(outputName, output) {
        const modes = previousRefreshModes();
        for (const identifier of outputIdentifiers(outputName, output)) {
            const entry = modes[identifier];
            const mode = typeof entry === "string" ? entry : entry?.mode;
            if (mode)
                return mode;
        }
        return "";
    }

    function storePreviousRefreshMode(outputName, output, mode) {
        const modeString = formatModeString(mode);
        if (!modeString)
            return;
        const modes = JSON.parse(JSON.stringify(previousRefreshModes()));
        for (const identifier of outputIdentifiers(outputName, output)) {
            modes[identifier] = {
                "mode": modeString
            };
        }
        SettingsData.setDisplayPreviousRefreshModes(CompositorService.compositor, modes);
    }

    function clearPreviousRefreshMode(outputName, output) {
        const modes = JSON.parse(JSON.stringify(previousRefreshModes()));
        let changed = false;
        for (const identifier of outputIdentifiers(outputName, output)) {
            if (modes[identifier] === undefined)
                continue;
            delete modes[identifier];
            changed = true;
        }
        if (changed)
            SettingsData.setDisplayPreviousRefreshModes(CompositorService.compositor, modes);
    }

    function applyConfiguredTargets(context, reason) {
        if (CompositorService.isNiri) {
            const outputs = NiriService.outputs || {};
            const applied = [];
            for (const name in outputs) {
                const currentMode = getNiriCurrentMode(outputs[name]);
                const target = computeTargetMode(name, outputs[name], "niri");
                if (!target || !target.value)
                    continue;
                if (isModeAlreadyCurrent(currentMode, target.mode)) {
                    if (target.source === "previous")
                        clearPreviousRefreshMode(name, outputs[name]);
                    continue;
                }
                if (root._lastAppliedTargets[name] === target.value)
                    continue;
                root._lastAppliedTargets[name] = target.value;
                cascadeGuard.restart();
                NiriService.applyOutputConfig(name, {
                    "mode": target.value
                }, success => {
                    if (success && target.source === "previous")
                        clearPreviousRefreshMode(name, outputs[name]);
                });
                applied.push(name + " " + (getModeRefresh(target.mode) / 1000).toFixed(0) + "Hz (" + target.source + ")");
            }
            if (applied.length > 0)
                log.info("Updated display refresh rate: ", applied.join(", "), " (", context, ")");
            return;
        }

        if (!WlrOutputService.wlrOutputAvailable)
            return;

        const outputs = WlrOutputService.outputs || [];
        const modeOverrides = ({});
        const restoredOutputs = [];
        const applied = [];

        for (const output of outputs) {
            const target = computeTargetMode(output.name, output, "wlr");
            if (!target || !target.value)
                continue;
            if (isModeAlreadyCurrent(output.currentMode, target.mode)) {
                if (target.source === "previous")
                    clearPreviousRefreshMode(output.name, output);
                continue;
            }
            if (root._lastAppliedTargets[output.name] === target.value)
                continue;
            root._lastAppliedTargets[output.name] = target.value;
            cascadeGuard.restart();
            modeOverrides[output.name] = target;
            if (target.source === "previous")
                restoredOutputs.push(output);
            applied.push(output.name + " " + (getModeRefresh(target.mode) / 1000).toFixed(0) + "Hz (" + target.source + ")");
        }

        if (applied.length > 0) {
            log.info("Updated display refresh rate: ", applied.join(", "), " (", context, ")");
            applyWlrModeOverrides(modeOverrides, success => {
                if (!success)
                    return;
                for (const output of restoredOutputs)
                    clearPreviousRefreshMode(output.name, output);
            });
        }
    }

    function applyBatteryTargets(reason) {
        if (CompositorService.isNiri) {
            const outputs = NiriService.outputs || {};
            const applied = [];
            for (const name in outputs) {
                const output = outputs[name];
                const currentMode = getNiriCurrentMode(output);
                if (!currentMode)
                    continue;
                const currentRefresh = getModeRefresh(currentMode);
                if (currentRefresh <= batteryRefreshRateTarget + batteryRefreshRateTolerance)
                    continue;
                const target = findBatteryRefreshMode(output, currentMode, "niri");
                if (!target)
                    continue;
                const targetValue = formatNiriMode(target);
                storePreviousRefreshMode(name, output, currentMode);
                if (root._lastAppliedTargets[name] === targetValue)
                    continue;
                root._lastAppliedTargets[name] = targetValue;
                cascadeGuard.restart();
                NiriService.applyOutputConfig(name, {
                    "mode": targetValue
                });
                applied.push(name + " " + (currentRefresh / 1000).toFixed(0) + "\u2192" + (getModeRefresh(target) / 1000).toFixed(0) + "Hz");
            }
            if (applied.length > 0)
                log.info("Updated display refresh rate: ", applied.join(", "), " (battery)");
            return;
        }

        if (!WlrOutputService.wlrOutputAvailable)
            return;

        const outputs = WlrOutputService.outputs || [];
        const modeOverrides = ({});
        const applied = [];

        for (const output of outputs) {
            const currentMode = output.currentMode;
            if (!currentMode)
                continue;
            const currentRefresh = getModeRefresh(currentMode);
            if (currentRefresh <= batteryRefreshRateTarget + batteryRefreshRateTolerance)
                continue;
            const target = findBatteryRefreshMode(output, currentMode, "wlr");
            if (!target)
                continue;
            storePreviousRefreshMode(output.name, output, currentMode);
            if (root._lastAppliedTargets[output.name] === target.id)
                continue;
            root._lastAppliedTargets[output.name] = target.id;
            cascadeGuard.restart();
            modeOverrides[output.name] = {
                "value": target.id,
                "mode": target,
                "source": "battery"
            };
            applied.push(output.name + " " + (currentRefresh / 1000).toFixed(0) + "\u2192" + (getModeRefresh(target) / 1000).toFixed(0) + "Hz");
        }

        if (applied.length > 0) {
            log.info("Updated display refresh rate: ", applied.join(", "), " (battery)");
            applyWlrModeOverrides(modeOverrides);
        }
    }

    function buildWlrHeads(modeOverrides) {
        const outputs = WlrOutputService.outputs || [];
        const heads = [];

        for (const output of outputs) {
            const enabled = output.enabled !== false;
            const head = {
                "name": output.name,
                "enabled": enabled
            };

            if (enabled) {
                const modeId = modeOverrides[output.name] !== undefined ? modeOverrides[output.name].value : output.currentMode?.id;
                if (modeId !== undefined)
                    head.modeId = modeId;

                head.position = {
                    "x": output.x ?? 0,
                    "y": output.y ?? 0
                };
                head.scale = output.scale ?? 1.0;
                head.transform = output.transform ?? 0;

                if (output.adaptiveSyncSupported)
                    head.adaptiveSync = output.adaptiveSync ?? 0;
            }

            heads.push(head);
        }

        return heads;
    }

    function applyWlrModeOverrides(modeOverrides, callback) {
        if (CompositorService.isHyprland) {
            HyprlandService.generateOutputsConfig(buildWlrOutputsData(modeOverrides), SettingsData.hyprlandOutputSettings, callback);
            return;
        }

        if (CompositorService.isMango) {
            MangoService.generateOutputsConfig(buildWlrOutputsData(modeOverrides), callback);
            return;
        }

        WlrOutputService.applyConfiguration(buildWlrHeads(modeOverrides), callback);
    }

    function buildWlrOutputsData(modeOverrides) {
        const outputs = WlrOutputService.outputs || [];
        const data = ({});

        for (const output of outputs) {
            const target = modeOverrides[output.name]?.mode || output.currentMode;
            const normalizedModes = (output.modes || []).map(mode => ({
                        "id": mode.id,
                        "width": getModeWidth(mode),
                        "height": getModeHeight(mode),
                        "refresh_rate": getModeRefresh(mode),
                        "preferred": mode.preferred === true || mode.is_preferred === true
                    }));
            const currentMode = target ? normalizedModes.findIndex(mode => getModeWidth(mode) === getModeWidth(target) && getModeHeight(mode) === getModeHeight(target) && Math.abs(getModeRefresh(mode) - getModeRefresh(target)) <= batteryRefreshRateTolerance) : -1;

            data[output.name] = {
                "name": output.name,
                "enabled": output.enabled !== false,
                "make": output.make || "",
                "model": output.model || "",
                "serial": output.serial || output.serialNumber || "",
                "modes": normalizedModes,
                "current_mode": currentMode,
                "configured_mode": target ? formatModeString(target) : "",
                "vrr_supported": output.adaptiveSyncSupported ?? output.vrr_supported ?? false,
                "vrr_enabled": isOutputVrrEnabled(output),
                "logical": {
                    "x": output.x ?? 0,
                    "y": output.y ?? 0,
                    "width": getModeWidth(target || output.currentMode) || 1920,
                    "height": getModeHeight(target || output.currentMode) || 1080,
                    "scale": output.scale ?? 1.0,
                    "transform": mapWlrTransform(output.transform)
                }
            };
        }

        return data;
    }

    function mapWlrTransform(transform) {
        switch (transform) {
        case 1:
            return "90";
        case 2:
            return "180";
        case 3:
            return "270";
        case 4:
            return "Flipped";
        case 5:
            return "Flipped90";
        case 6:
            return "Flipped180";
        case 7:
            return "Flipped270";
        default:
            return "Normal";
        }
    }

    function findBatteryRefreshMode(output, currentMode, backend) {
        if (!output || !currentMode || (backend === "wlr" && !output.enabled))
            return null;

        if (isOutputVrrEnabled(output))
            return null;

        const modes = output.modes || [];
        const sameResolutionModes = modes.filter(m => getModeWidth(m) === getModeWidth(currentMode) && getModeHeight(m) === getModeHeight(currentMode));
        const uniqueRefreshRates = [];
        for (const mode of sameResolutionModes) {
            const refresh = getModeRefresh(mode);
            if (!uniqueRefreshRates.some(r => Math.abs(r - refresh) <= batteryRefreshRateTolerance))
                uniqueRefreshRates.push(refresh);
        }

        if (uniqueRefreshRates.length <= 1)
            return null;

        let bestMode = null;
        let bestDiff = Infinity;
        for (const mode of sameResolutionModes) {
            const diff = Math.abs(getModeRefresh(mode) - batteryRefreshRateTarget);
            if (diff < bestDiff) {
                bestMode = mode;
                bestDiff = diff;
            }
        }

        if (!bestMode || bestDiff > batteryRefreshRateTolerance)
            return null;

        return bestMode;
    }

    function computeTargetMode(outputName, output, backend) {
        const profileMode = findActiveProfileMode(outputName, output);
        if (profileMode) {
            const mode = findModeByString(output?.modes || [], profileMode);
            if (mode) {
                return {
                    "value": formatRestoreModeValue(mode, backend),
                    "mode": mode,
                    "source": "profile"
                };
            }
        }

        const previousMode = findPreviousRefreshMode(outputName, output);
        if (previousMode) {
            const mode = findModeByString(output?.modes || [], previousMode);
            if (mode) {
                return {
                    "value": formatRestoreModeValue(mode, backend),
                    "mode": mode,
                    "source": "previous"
                };
            }
        }

        return {
            "value": null,
            "mode": null,
            "source": "none"
        };
    }

    function findActiveProfileMode(outputName, output) {
        const compositor = CompositorService.compositor;
        const profileModes = SettingsData.activeDisplayProfileModes?.[compositor] || {};
        if (Object.keys(profileModes).length === 0)
            return "";

        const identifiers = [outputName];
        if (output?.make && output?.model) {
            const modelId = output.make + " " + output.model;
            const serial = output.serial || output.serialNumber || "Unknown";
            const serialId = modelId + " " + serial;
            if (!identifiers.includes(serialId))
                identifiers.push(serialId);
            if (!identifiers.includes(modelId))
                identifiers.push(modelId);
        }

        for (const identifier of identifiers) {
            const mode = profileModes[identifier]?.mode;
            if (mode)
                return mode;
        }

        return "";
    }

    function findModeByString(modes, modeString) {
        for (const mode of modes) {
            if (formatModeString(mode) === modeString)
                return mode;
        }

        const parsed = parseModeString(modeString);
        if (!parsed)
            return null;

        for (const mode of modes) {
            if (getModeWidth(mode) === parsed.width && getModeHeight(mode) === parsed.height && Math.abs(getModeRefresh(mode) - parsed.refresh) <= batteryRefreshRateTolerance)
                return mode;
        }

        return null;
    }

    function parseModeString(modeString) {
        const match = (modeString || "").match(/^(\d+)x(\d+)@([\d.]+)$/);
        if (!match)
            return null;
        return {
            "width": parseInt(match[1]),
            "height": parseInt(match[2]),
            "refresh": Math.round(parseFloat(match[3]) * 1000)
        };
    }

    function isModeAlreadyCurrent(currentMode, targetMode) {
        if (!currentMode || !targetMode)
            return true;
        return getModeWidth(currentMode) === getModeWidth(targetMode) && getModeHeight(currentMode) === getModeHeight(targetMode) && Math.abs(getModeRefresh(currentMode) - getModeRefresh(targetMode)) <= batteryRefreshRateTolerance;
    }

    function formatRestoreModeValue(mode, backend) {
        if (!mode)
            return null;
        if (backend === "wlr")
            return mode.id ?? null;
        return formatNiriMode(mode);
    }

    function isOutputVrrEnabled(output) {
        return output.vrr_enabled === true || output.adaptiveSync === 1;
    }

    function getNiriCurrentMode(output) {
        if (!output || !output.modes || output.current_mode === undefined)
            return null;
        return output.modes[output.current_mode] || null;
    }

    function getModeWidth(mode) {
        return mode?.width ?? 0;
    }

    function getModeHeight(mode) {
        return mode?.height ?? 0;
    }

    function getModeRefresh(mode) {
        return mode?.refresh_rate ?? mode?.refresh ?? 0;
    }

    function formatModeString(mode) {
        if (!mode)
            return "";
        return getModeWidth(mode) + "x" + getModeHeight(mode) + "@" + (getModeRefresh(mode) / 1000).toFixed(3);
    }

    function formatNiriMode(mode) {
        return mode.width + "x" + mode.height + "@" + (getModeRefresh(mode) / 1000).toFixed(3);
    }

    function markDeviceUserControlled(deviceId) {
        const newControlled = Object.assign({}, userControlledDevices);
        newControlled[deviceId] = Date.now();
        userControlledDevices = newControlled;
    }

    function isDeviceUserControlled(deviceId) {
        const controlTime = userControlledDevices[deviceId];
        if (!controlTime) {
            return false;
        }
        return (Date.now() - controlTime) < 1000;
    }

    function clearDeviceUserControlled(deviceId) {
        const newControlled = Object.assign({}, userControlledDevices);
        delete newControlled[deviceId];
        userControlledDevices = newControlled;
    }

    function markDevicePendingOsd(deviceId) {
        const newPending = Object.assign({}, pendingOsdDevices);
        newPending[deviceId] = true;
        pendingOsdDevices = newPending;
    }

    function clearDevicePendingOsd(deviceId) {
        const newPending = Object.assign({}, pendingOsdDevices);
        delete newPending[deviceId];
        pendingOsdDevices = newPending;
    }

    function updateSingleDevice(device) {
        if (device.class === "leds") {
            return;
        }

        const isUserControlled = isDeviceUserControlled(device.id);
        if (isUserControlled) {
            return;
        }

        const deviceIndex = devices.findIndex(d => d.id === device.id);
        if (deviceIndex !== -1) {
            const newDevices = [...devices];
            const cachedMax = deviceMaxCache[device.id];

            let displayMax = cachedMax || (device.class === "ddc" ? device.max : 100);
            if (displayMax > 0 && !cachedMax) {
                const newCache = Object.assign({}, deviceMaxCache);
                newCache[device.id] = displayMax;
                deviceMaxCache = newCache;
            }

            newDevices[deviceIndex] = {
                "id": device.id,
                "name": device.id,
                "class": device.class,
                "current": device.current,
                "percentage": device.currentPercent,
                "max": device.max,
                "backend": device.backend,
                "displayMax": displayMax
            };
            devices = newDevices;
        }

        const isExponential = SessionData.getBrightnessExponential(device.id);
        const userSetValue = deviceBrightnessUserSet[device.id];

        let displayValue = device.currentPercent;
        if (isExponential) {
            if (userSetValue !== undefined) {
                const exponent = SessionData.getBrightnessExponent(device.id);
                const expectedHardware = Math.round(Math.pow(userSetValue / 100.0, exponent) * 100.0);
                if (Math.abs(device.currentPercent - expectedHardware) > 2) {
                    const newUserSet = Object.assign({}, deviceBrightnessUserSet);
                    delete newUserSet[device.id];
                    deviceBrightnessUserSet = newUserSet;
                    SessionData.clearBrightnessUserSetValue(device.id);
                    displayValue = linearToExponential(device.currentPercent, device.id);
                } else {
                    displayValue = userSetValue;
                }
            } else {
                displayValue = linearToExponential(device.currentPercent, device.id);
            }
        }

        const oldValue = deviceBrightness[device.id];
        const newBrightness = Object.assign({}, deviceBrightness);
        newBrightness[device.id] = displayValue;
        deviceBrightness = newBrightness;
        brightnessVersion++;

        const isPendingOsd = pendingOsdDevices[device.id] === true;
        if (isPendingOsd) {
            clearDevicePendingOsd(device.id);
            if (!suppressOsd) {
                brightnessChanged(true);
            }
            return;
        }

        if (!brightnessInitialized || oldValue === displayValue) {
            return;
        }
        if (suppressOsd) {
            return;
        }
        brightnessChanged(true);
    }

    function updateFromBrightnessState(state) {
        if (!state || !state.devices) {
            return;
        }

        const newMaxCache = Object.assign({}, deviceMaxCache);
        devices = state.devices.map(d => {
            const cachedMax = deviceMaxCache[d.id];
            let displayMax = cachedMax || (d.class === "ddc" ? d.max : 100);
            if (displayMax > 0 && !cachedMax) {
                newMaxCache[d.id] = displayMax;
            }
            return {
                "id": d.id,
                "name": d.id,
                "class": d.class,
                "current": d.current,
                "percentage": d.currentPercent,
                "max": d.max,
                "backend": d.backend,
                "displayMax": displayMax
            };
        });
        deviceMaxCache = newMaxCache;

        const newBrightness = {};
        let anyDeviceBrightnessChanged = false;

        for (const device of state.devices) {
            const isExponential = SessionData.getBrightnessExponential(device.id);
            const userSetValue = deviceBrightnessUserSet[device.id];
            const oldValue = deviceBrightness[device.id];

            if (isExponential) {
                if (userSetValue !== undefined) {
                    newBrightness[device.id] = userSetValue;
                } else {
                    newBrightness[device.id] = linearToExponential(device.currentPercent, device.id);
                }
            } else {
                newBrightness[device.id] = device.currentPercent;
            }

            const newValue = newBrightness[device.id];
            if (oldValue !== undefined && oldValue !== newValue) {
                anyDeviceBrightnessChanged = true;
            }
        }
        deviceBrightness = newBrightness;
        brightnessVersion++;

        brightnessAvailable = devices.length > 0;

        if (devices.length > 0 && !currentDevice) {
            const lastDevice = SessionData.lastBrightnessDevice || "";
            const deviceExists = devices.some(d => d.id === lastDevice);
            if (deviceExists) {
                setCurrentDevice(lastDevice, false);
            } else {
                const backlight = internalPanelActive() ? devices.find(d => d.class === "backlight") : null;
                const nonKbdDevice = devices.find(d => !d.id.includes("kbd"));
                const defaultDevice = backlight || nonKbdDevice || devices[0];
                setCurrentDevice(defaultDevice.id, false);
            }
        }

        const shouldShowOsd = brightnessInitialized && anyDeviceBrightnessChanged && !suppressOsd;

        if (!brightnessInitialized) {
            brightnessInitialized = true;
        }

        if (shouldShowOsd) {
            brightnessChanged(true);
        }
    }

    function setBrightness(percentage, device, suppressOsd) {
        const actualDevice = device === "" ? getDefaultDevice() : (device || currentDevice || getDefaultDevice());
        if (!actualDevice) {
            log.warn("No device selected for brightness change");
            return;
        }

        if (actualDevice !== lastIpcDevice) {
            lastIpcDevice = actualDevice;
        }

        const deviceInfo = getCurrentDeviceInfoByName(actualDevice);
        const isExponential = SessionData.getBrightnessExponential(actualDevice);

        let minValue = 0;
        let maxValue = 100;

        switch (true) {
        case isExponential:
            minValue = 1;
            maxValue = 100;
            break;
        default:
            minValue = (deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc")) ? 1 : 0;
            maxValue = deviceInfo?.displayMax || 100;
            break;
        }

        if (maxValue <= 0) {
            log.warn("Invalid max value for device", actualDevice, "- skipping brightness change");
            return;
        }

        const clampedValue = Math.max(minValue, Math.min(maxValue, percentage));

        if (!DMSService.isConnected) {
            log.warn("Not connected to DMS");
            return;
        }

        const isLedDevice = deviceInfo?.class === "leds";

        if (suppressOsd) {
            markDeviceUserControlled(actualDevice);
        } else if (!isLedDevice) {
            markDevicePendingOsd(actualDevice);
        }

        const newBrightness = Object.assign({}, deviceBrightness);
        newBrightness[actualDevice] = clampedValue;
        deviceBrightness = newBrightness;
        brightnessVersion++;

        if (isLedDevice && !suppressOsd) {
            brightnessChanged(true);
        }

        if (isExponential) {
            const newUserSet = Object.assign({}, deviceBrightnessUserSet);
            newUserSet[actualDevice] = clampedValue;
            deviceBrightnessUserSet = newUserSet;
            SessionData.setBrightnessUserSetValue(actualDevice, clampedValue);
        }

        const params = {
            "device": actualDevice,
            "percent": clampedValue
        };
        if (isExponential) {
            params.exponential = true;
            params.exponent = SessionData.getBrightnessExponent(actualDevice);
        }

        DMSService.sendRequest("brightness.setBrightness", params, response => {
            if (response.error) {
                log.error("Failed to set brightness:", response.error);
                ToastService.showError(I18n.tr("Failed to set brightness"), response.error, "", "brightness");
            } else {
                ToastService.dismissCategory("brightness");
            }
        });
    }

    function setCurrentDevice(deviceName, saveToSession = false) {
        if (currentDevice === deviceName) {
            return;
        }

        currentDevice = deviceName;
        lastIpcDevice = deviceName;

        if (saveToSession) {
            SessionData.setLastBrightnessDevice(deviceName);
        }

        deviceSwitched();
    }

    function getDeviceBrightness(deviceName) {
        if (!deviceName) {
            return 50;
        }

        if (deviceName in deviceBrightness) {
            return deviceBrightness[deviceName];
        }

        return 50;
    }

    function linearToExponential(linearPercent, deviceName) {
        const exponent = SessionData.getBrightnessExponent(deviceName);
        const hardwarePercent = linearPercent / 100.0;
        const normalizedPercent = Math.pow(hardwarePercent, 1.0 / exponent);
        return Math.round(normalizedPercent * 100.0);
    }

    function internalPanelActive() {
        return Quickshell.screens.some(s => /^(eDP|LVDS|DSI)/i.test(s.name));
    }

    function getDefaultDevice() {
        if (internalPanelActive()) {
            for (const device of devices) {
                if (device.class === "backlight") {
                    return device.id;
                }
            }
        }
        const nonKbdDevice = devices.find(d => d.class !== "backlight" && !d.id.includes("kbd"));
        if (nonKbdDevice)
            return nonKbdDevice.id;
        return devices.length > 0 ? devices[0].id : "";
    }

    function getPinnedDeviceForFocusedScreen() {
        const focusedScreen = CompositorService.getFocusedScreen();
        if (!focusedScreen)
            return "";

        const pins = CacheData.brightnessDevicePins || {};
        const screenKey = SettingsData.getScreenDisplayName(focusedScreen);
        if (!screenKey)
            return "";

        const pinnedDevice = pins[screenKey];
        if (!pinnedDevice)
            return "";

        const deviceExists = devices.some(d => d.id === pinnedDevice);
        if (!deviceExists)
            return "";

        return pinnedDevice;
    }

    function getPreferredDevice() {
        const pinned = getPinnedDeviceForFocusedScreen();
        if (pinned)
            return pinned;

        return getDefaultDevice();
    }

    function getCurrentDeviceInfo() {
        const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice);
        if (!deviceToUse) {
            return null;
        }

        for (const device of devices) {
            if (device.id === deviceToUse) {
                return device;
            }
        }
        return null;
    }

    function isCurrentDeviceReady() {
        const deviceToUse = lastIpcDevice === "" ? getDefaultDevice() : (lastIpcDevice || currentDevice);
        return deviceToUse !== "";
    }

    function getCurrentDeviceInfoByName(deviceName) {
        if (!deviceName) {
            return null;
        }

        for (const device of devices) {
            if (device.id === deviceName) {
                return device;
            }
        }
        return null;
    }

    function getDeviceMax(deviceName) {
        const deviceInfo = getCurrentDeviceInfoByName(deviceName);
        if (!deviceInfo) {
            return 100;
        }
        return deviceInfo.displayMax || 100;
    }

    // Night Mode Functions - Simplified
    function enableNightMode() {
        if (!gammaControlAvailable) {
            ToastService.showWarning(I18n.tr("Night mode failed: DMS gamma control not available"));
            return;
        }

        nightModeEnabled = true;
        SessionData.setNightModeEnabled(true);

        DMSService.sendRequest("wayland.gamma.setEnabled", {
            "enabled": true
        }, response => {
            if (response.error) {
                log.error("Failed to enable gamma control:", response.error);
                ToastService.showError(I18n.tr("Failed to enable night mode"), response.error, "", "night-mode");
                nightModeEnabled = false;
                SessionData.setNightModeEnabled(false);
                return;
            }
            ToastService.dismissCategory("night-mode");

            if (SessionData.nightModeAutoEnabled) {
                startAutomation();
            } else {
                applyNightModeDirectly();
            }
        });
    }

    function disableNightMode() {
        nightModeEnabled = false;
        SessionData.setNightModeEnabled(false);

        if (!gammaControlAvailable) {
            return;
        }

        DMSService.sendRequest("wayland.gamma.setEnabled", {
            "enabled": false
        }, response => {
            if (response.error) {
                log.error("Failed to disable gamma control:", response.error);
                ToastService.showError(I18n.tr("Failed to disable night mode"), response.error, "", "night-mode");
            } else {
                ToastService.dismissCategory("night-mode");
            }
        });
    }

    function toggleNightMode() {
        if (nightModeEnabled) {
            disableNightMode();
        } else {
            enableNightMode();
        }
    }

    function applyNightModeDirectly() {
        const temperature = SessionData.nightModeTemperature || 4000;

        DMSService.sendRequest("wayland.gamma.setManualTimes", {
            "sunrise": null,
            "sunset": null
        }, response => {
            if (response.error) {
                log.error("Failed to clear manual times:", response.error);
                return;
            }

            DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
                "use": false
            }, response => {
                if (response.error) {
                    log.error("Failed to disable IP location:", response.error);
                    return;
                }

                DMSService.sendRequest("wayland.gamma.setTemperature", {
                    "low": temperature,
                    "high": temperature
                }, response => {
                    if (response.error) {
                        log.error("Failed to set temperature:", response.error);
                        ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
                    } else {
                        ToastService.dismissCategory("night-mode");
                    }
                });
            });
        });
    }

    function startAutomation() {
        if (!automationAvailable) {
            return;
        }

        const mode = SessionData.nightModeAutoMode || "time";

        switch (mode) {
        case "time":
            startTimeBasedMode();
            break;
        case "location":
            startLocationBasedMode();
            break;
        }
    }

    function startTimeBasedMode() {
        const temperature = SessionData.nightModeTemperature || 4000;
        const highTemp = SessionData.nightModeHighTemperature || 6500;
        const sunriseHour = SessionData.nightModeEndHour;
        const sunriseMinute = SessionData.nightModeEndMinute;
        const sunsetHour = SessionData.nightModeStartHour;
        const sunsetMinute = SessionData.nightModeStartMinute;

        const sunrise = `${String(sunriseHour).padStart(2, '0')}:${String(sunriseMinute).padStart(2, '0')}`;
        const sunset = `${String(sunsetHour).padStart(2, '0')}:${String(sunsetMinute).padStart(2, '0')}`;

        DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
            "use": false
        }, response => {
            if (response.error) {
                log.error("Failed to disable IP location:", response.error);
                return;
            }

            DMSService.sendRequest("wayland.gamma.setTemperature", {
                "low": temperature,
                "high": highTemp
            }, response => {
                if (response.error) {
                    log.error("Failed to set temperature:", response.error);
                    ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
                    return;
                }

                DMSService.sendRequest("wayland.gamma.setManualTimes", {
                    "sunrise": sunrise,
                    "sunset": sunset
                }, response => {
                    if (response.error) {
                        log.error("Failed to set manual times:", response.error);
                        ToastService.showError(I18n.tr("Failed to set night mode schedule"), response.error, "", "night-mode");
                    } else {
                        ToastService.dismissCategory("night-mode");
                    }
                });
            });
        });
    }

    function startLocationBasedMode() {
        const temperature = SessionData.nightModeTemperature || 4000;
        const highTemp = SessionData.nightModeHighTemperature || 6500;

        DMSService.sendRequest("wayland.gamma.setManualTimes", {
            "sunrise": null,
            "sunset": null
        }, response => {
            if (response.error) {
                log.error("Failed to clear manual times:", response.error);
                return;
            }

            DMSService.sendRequest("wayland.gamma.setTemperature", {
                "low": temperature,
                "high": highTemp
            }, response => {
                if (response.error) {
                    log.error("Failed to set temperature:", response.error);
                    ToastService.showError(I18n.tr("Failed to set night mode temperature"), response.error, "", "night-mode");
                    return;
                }

                if (SessionData.nightModeUseIPLocation) {
                    DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
                        "use": true
                    }, response => {
                        if (response.error) {
                            log.error("Failed to enable IP location:", response.error);
                            ToastService.showError(I18n.tr("Failed to enable IP location"), response.error, "", "night-mode");
                        } else {
                            ToastService.dismissCategory("night-mode");
                        }
                    });
                } else if (SessionData.latitude !== 0.0 && SessionData.longitude !== 0.0) {
                    DMSService.sendRequest("wayland.gamma.setUseIPLocation", {
                        "use": false
                    }, response => {
                        if (response.error) {
                            log.error("Failed to disable IP location:", response.error);
                            return;
                        }

                        DMSService.sendRequest("wayland.gamma.setLocation", {
                            "latitude": SessionData.latitude,
                            "longitude": SessionData.longitude
                        }, response => {
                            if (response.error) {
                                log.error("Failed to set location:", response.error);
                                ToastService.showError(I18n.tr("Failed to set night mode location"), response.error, "", "night-mode");
                            } else {
                                ToastService.dismissCategory("night-mode");
                            }
                        });
                    });
                } else {
                    log.warn("Location mode selected but no coordinates set and IP location disabled");
                }
            });
        });
    }

    function evaluateNightMode() {
        if (!nightModeEnabled) {
            return;
        }

        if (SessionData.nightModeAutoEnabled) {
            restartTimer.nextAction = "automation";
            restartTimer.start();
        } else {
            restartTimer.nextAction = "direct";
            restartTimer.start();
        }
    }

    function runResumeRecoveryPass() {
        checkGammaControlAvailability();
        rescanDevices();

        if (nightModeEnabled) {
            evaluateNightMode();
        }
    }

    function checkGammaControlAvailability() {
        if (!DMSService.isConnected) {
            return;
        }

        if (DMSService.apiVersion < 6) {
            gammaControlAvailable = false;
            automationAvailable = false;
            return;
        }

        if (!DMSService.capabilities.includes("gamma")) {
            gammaControlAvailable = false;
            automationAvailable = false;
            return;
        }

        DMSService.sendRequest("wayland.gamma.getState", null, response => {
            if (response.error) {
                gammaControlAvailable = false;
                automationAvailable = false;
                log.error("Gamma control not available:", response.error);
            } else {
                gammaControlAvailable = true;
                automationAvailable = true;

                if (nightModeEnabled) {
                    DMSService.sendRequest("wayland.gamma.setEnabled", {
                        "enabled": true
                    }, enableResponse => {
                        if (enableResponse.error) {
                            log.error("Failed to enable gamma control on startup:", enableResponse.error);
                            return;
                        }

                        evaluateNightMode();
                    });
                }
            }
        });
    }

    Timer {
        id: restartTimer
        property string nextAction: ""
        interval: 250
        repeat: false

        onTriggered: {
            if (nextAction === "automation") {
                startAutomation();
            } else if (nextAction === "direct") {
                applyNightModeDirectly();
            }
            nextAction = "";
        }
    }

    Timer {
        id: resumeRecoveryTimer
        interval: 400
        repeat: false

        onTriggered: {
            runResumeRecoveryPass();
            resumeRecoveryAttempt++;

            switch (resumeRecoveryAttempt) {
            case 1:
                interval = 1400;
                restart();
                return;
            case 2:
                interval = 2600;
                restart();
                return;
            }

            resumeRecoveryAttempt = 0;
            interval = 400;
        }
    }

    function rescanDevices() {
        if (!DMSService.isConnected) {
            return;
        }

        DMSService.sendRequest("brightness.rescan", null, response => {
            if (response.error) {
                log.error("Failed to rescan brightness devices:", response.error);
            }
        });
    }

    function updateDeviceBrightnessDisplay(deviceName) {
        brightnessVersion++;
        brightnessChanged();
    }

    Connections {
        target: SessionData

        function onBrightnessDisplayHintChanged(deviceName) {
            root.updateDeviceBrightnessDisplay(deviceName);
        }
    }

    Timer {
        id: osdSuppressTimer
        interval: 2000
        running: true
        onTriggered: suppressOsd = false
    }

    Component.onCompleted: {
        nightModeEnabled = SessionData.nightModeEnabled;
        deviceBrightnessUserSet = Object.assign({}, SessionData.brightnessUserSetValues);
        if (DMSService.isConnected) {
            checkGammaControlAvailability();
        }
    }

    Timer {
        id: screenChangeRescanTimer
        property int rescanAttempt: 0
        interval: 3000
        repeat: false
        onTriggered: {
            rescanDevices();
            rescanAttempt++;
            if (rescanAttempt < 3) {
                interval = rescanAttempt === 1 ? 5000 : 8000;
                restart();
                return;
            }
            rescanAttempt = 0;
            interval = 3000;
            osdSuppressTimer.restart();
        }
    }

    Connections {
        target: Quickshell

        function onScreensChanged() {
            suppressOsd = true;
            screenChangeRescanTimer.rescanAttempt = 0;
            screenChangeRescanTimer.interval = 3000;
            screenChangeRescanTimer.restart();
        }
    }

    Connections {
        target: DMSService

        function onConnectionStateChanged() {
            if (DMSService.isConnected) {
                checkGammaControlAvailability();
            } else {
                brightnessAvailable = false;
                gammaControlAvailable = false;
                automationAvailable = false;
            }
        }

        function onCapabilitiesReceived() {
            checkGammaControlAvailability();
        }

        function onBrightnessStateUpdate(data) {
            updateFromBrightnessState(data);
        }

        function onBrightnessDeviceUpdate(device) {
            updateSingleDevice(device);
        }

        function onGammaStateUpdate(data) {
            root.gammaState = data;
        }
    }

    Connections {
        target: SessionService

        function onSessionResumed() {
            suppressOsd = true;
            osdSuppressTimer.restart();
            resumeRecoveryAttempt = 0;
            resumeRecoveryTimer.interval = 400;
            resumeRecoveryTimer.restart();
        }
    }

    // Session Data Connections
    Connections {
        target: SessionData

        function onNightModeEnabledChanged() {
            nightModeEnabled = SessionData.nightModeEnabled;
            evaluateNightMode();
        }

        function onNightModeAutoEnabledChanged() {
            evaluateNightMode();
        }
        function onNightModeAutoModeChanged() {
            evaluateNightMode();
        }
        function onNightModeStartHourChanged() {
            evaluateNightMode();
        }
        function onNightModeStartMinuteChanged() {
            evaluateNightMode();
        }
        function onNightModeEndHourChanged() {
            evaluateNightMode();
        }
        function onNightModeEndMinuteChanged() {
            evaluateNightMode();
        }
        function onNightModeTemperatureChanged() {
            evaluateNightMode();
        }
        function onNightModeHighTemperatureChanged() {
            evaluateNightMode();
        }
        function onLatitudeChanged() {
            evaluateNightMode();
        }
        function onLongitudeChanged() {
            evaluateNightMode();
        }
        function onNightModeUseIPLocationChanged() {
            evaluateNightMode();
        }
    }

    // IPC Handler for external control
    IpcHandler {
        function set(percentage: string, device: string): string {
            if (!root.brightnessAvailable)
                return "Brightness control not available";

            const value = parseInt(percentage);
            if (isNaN(value))
                return "Invalid brightness value: " + percentage;

            const actualDevice = device || root.getPreferredDevice();

            if (actualDevice && !root.devices.some(d => d.id === actualDevice))
                return "Device not found: " + actualDevice;

            const deviceInfo = actualDevice ? root.getCurrentDeviceInfoByName(actualDevice) : null;
            const minValue = (deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc")) ? 1 : 0;
            const clampedValue = Math.max(minValue, Math.min(100, value));

            root.lastIpcDevice = actualDevice;
            if (actualDevice && actualDevice !== root.currentDevice)
                root.setCurrentDevice(actualDevice, false);

            root.setBrightness(clampedValue, actualDevice);

            return actualDevice ? "Brightness set to " + clampedValue + "% on " + actualDevice : "Brightness set to " + clampedValue + "%";
        }

        function increment(step: string, device: string): string {
            if (!root.brightnessAvailable)
                return "Brightness control not available";

            const actualDevice = device || root.getPreferredDevice();

            if (actualDevice && !root.devices.some(d => d.id === actualDevice))
                return "Device not found: " + actualDevice;

            const stepValue = parseInt(step || "5");

            root.lastIpcDevice = actualDevice;
            if (actualDevice && actualDevice !== root.currentDevice)
                root.setCurrentDevice(actualDevice, false);

            const isExponential = SessionData.getBrightnessExponential(actualDevice);
            const currentBrightness = root.getDeviceBrightness(actualDevice);
            const deviceInfo = root.getCurrentDeviceInfoByName(actualDevice);

            const maxValue = isExponential ? 100 : (deviceInfo?.displayMax || 100);
            const newBrightness = Math.min(maxValue, currentBrightness + stepValue);

            root.setBrightness(newBrightness, actualDevice);

            return "Brightness increased by " + stepValue + "%" + (device ? " on " + actualDevice : "");
        }

        function decrement(step: string, device: string): string {
            if (!root.brightnessAvailable)
                return "Brightness control not available";

            const actualDevice = device || root.getPreferredDevice();

            if (actualDevice && !root.devices.some(d => d.id === actualDevice))
                return "Device not found: " + actualDevice;

            const stepValue = parseInt(step || "5");

            root.lastIpcDevice = actualDevice;
            if (actualDevice && actualDevice !== root.currentDevice)
                root.setCurrentDevice(actualDevice, false);

            const isExponential = SessionData.getBrightnessExponential(actualDevice);
            const currentBrightness = root.getDeviceBrightness(actualDevice);
            const deviceInfo = root.getCurrentDeviceInfoByName(actualDevice);

            let minValue = 0;
            switch (true) {
            case isExponential:
                minValue = 1;
                break;
            case deviceInfo && (deviceInfo.class === "backlight" || deviceInfo.class === "ddc"):
                minValue = 1;
                break;
            default:
                minValue = 0;
                break;
            }

            const newBrightness = Math.max(minValue, currentBrightness - stepValue);

            root.setBrightness(newBrightness, actualDevice);

            return "Brightness decreased by " + stepValue + "%" + (device ? " on " + actualDevice : "");
        }

        function status(): string {
            if (!root.brightnessAvailable) {
                return "Brightness control not available";
            }

            return "Device: " + root.currentDevice + " - Brightness: " + root.brightnessLevel + "%";
        }

        function list(): string {
            if (!root.brightnessAvailable) {
                return "No brightness devices available";
            }

            let result = "Available devices:\n";
            for (const device of root.devices) {
                const isExp = SessionData.getBrightnessExponential(device.id);
                result += device.id + " (" + device.class + ")" + (isExp ? " [exponential]" : "") + "\n";
            }
            return result;
        }

        function enableExponential(device: string): string {
            const targetDevice = device || root.currentDevice;
            if (!targetDevice) {
                return "No device specified";
            }

            if (!root.devices.some(d => d.id === targetDevice)) {
                return "Device not found: " + targetDevice;
            }

            SessionData.setBrightnessExponential(targetDevice, true);
            return "Exponential mode enabled for " + targetDevice;
        }

        function disableExponential(device: string): string {
            const targetDevice = device || root.currentDevice;
            if (!targetDevice) {
                return "No device specified";
            }

            if (!root.devices.some(d => d.id === targetDevice)) {
                return "Device not found: " + targetDevice;
            }

            SessionData.setBrightnessExponential(targetDevice, false);
            return "Exponential mode disabled for " + targetDevice;
        }

        function toggleExponential(device: string): string {
            const targetDevice = device || root.currentDevice;
            if (!targetDevice) {
                return "No device specified";
            }

            if (!root.devices.some(d => d.id === targetDevice)) {
                return "Device not found: " + targetDevice;
            }

            const currentState = SessionData.getBrightnessExponential(targetDevice);
            SessionData.setBrightnessExponential(targetDevice, !currentState);
            return "Exponential mode " + (!currentState ? "enabled" : "disabled") + " for " + targetDevice;
        }

        target: "brightness"
    }

    IpcHandler {
        function toggle(): string {
            root.toggleNightMode();
            return root.nightModeEnabled ? "Night mode enabled" : "Night mode disabled";
        }

        function enable(): string {
            root.enableNightMode();
            return "Night mode enabled";
        }

        function disable(): string {
            root.disableNightMode();
            return "Night mode disabled";
        }

        function status(): string {
            if (!root.gammaControlAvailable)
                return "Night mode: unavailable (no gamma control)";

            const parts = ["Night mode: " + (root.nightModeEnabled ? "enabled" : "disabled")];

            if (root.gammaCurrentTemp > 0)
                parts.push("Current temperature: " + root.gammaCurrentTemp + "K");

            parts.push("Target night temperature: " + SessionData.nightModeTemperature + "K");

            if (SessionData.nightModeAutoEnabled) {
                parts.push("Target day temperature: " + SessionData.nightModeHighTemperature + "K");
                parts.push("Automation: " + SessionData.nightModeAutoMode);
                parts.push("Period: " + (root.gammaIsDay ? "day" : "night"));

                if (root.gammaNextTransition)
                    parts.push("Next transition: " + root.gammaNextTransition);
                if (root.gammaSunriseTime)
                    parts.push("Sunrise: " + root.gammaSunriseTime);
                if (root.gammaSunsetTime)
                    parts.push("Sunset: " + root.gammaSunsetTime);
            }

            return parts.join("\n");
        }

        function getCurrentTemp(): string {
            if (!root.gammaControlAvailable)
                return "Gamma control not available";
            if (root.gammaCurrentTemp <= 0)
                return "No current temperature reported";
            return root.gammaCurrentTemp.toString();
        }

        function getTargetTemp(): string {
            return SessionData.nightModeTemperature.toString();
        }

        function getDayTemp(): string {
            return SessionData.nightModeHighTemperature.toString();
        }

        function setTargetTemp(value: string): string {
            if (!value)
                return "Usage: night setTargetTemp <2500-6000>";

            const temp = parseInt(value);
            if (isNaN(temp))
                return "Invalid temperature: " + value;
            if (temp < 2500 || temp > 6000)
                return "Temperature must be between 2500K and 6000K";

            const rounded = Math.round(temp / 500) * 500;
            SessionData.setNightModeTemperature(rounded);

            if (root.nightModeEnabled) {
                switch (true) {
                case SessionData.nightModeAutoEnabled:
                    root.startAutomation();
                    break;
                default:
                    root.applyNightModeDirectly();
                    break;
                }
            }

            if (rounded !== temp)
                return "Night temperature set to " + rounded + "K (rounded from " + temp + "K)";
            return "Night temperature set to " + rounded + "K";
        }

        function setDayTemp(value: string): string {
            if (!value)
                return "Usage: night setDayTemp <2500-6500>";

            const temp = parseInt(value);
            if (isNaN(temp))
                return "Invalid temperature: " + value;
            if (temp < 2500 || temp > 6500)
                return "Temperature must be between 2500K and 6500K";

            const rounded = Math.round(temp / 500) * 500;
            SessionData.setNightModeHighTemperature(rounded);

            if (root.nightModeEnabled && SessionData.nightModeAutoEnabled)
                root.startAutomation();

            if (rounded !== temp)
                return "Day temperature set to " + rounded + "K (rounded from " + temp + "K)";
            return "Day temperature set to " + rounded + "K";
        }

        function getSchedule(): string {
            if (!SessionData.nightModeAutoEnabled)
                return "Automation disabled";

            const parts = ["Mode: " + SessionData.nightModeAutoMode];
            parts.push("Period: " + (root.gammaIsDay ? "day" : "night"));

            if (root.gammaDawnTime)
                parts.push("Dawn: " + root.gammaDawnTime);
            if (root.gammaSunriseTime)
                parts.push("Sunrise: " + root.gammaSunriseTime);
            if (root.gammaSunsetTime)
                parts.push("Sunset: " + root.gammaSunsetTime);
            if (root.gammaNightTime)
                parts.push("Night: " + root.gammaNightTime);
            if (root.gammaNextTransition)
                parts.push("Next transition: " + root.gammaNextTransition);
            if (root.gammaSunPosition > 0)
                parts.push("Sun position: " + root.gammaSunPosition.toFixed(2) + "°");

            return parts.join("\n");
        }

        target: "night"
    }
}
