import QtQuick
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Widgets
import qs.Services

Variants {
    readonly property var log: Log.scoped("BlurredWallpaperBackground")
    model: SettingsData.getFilteredScreens("wallpaper")

    PanelWindow {
        id: blurWallpaperWindow

        required property var modelData

        screen: modelData

        WlrLayershell.layer: WlrLayer.Background
        WlrLayershell.namespace: "dms:blurwallpaper"
        WlrLayershell.exclusionMode: ExclusionMode.Ignore

        anchors.top: true
        anchors.bottom: true
        anchors.left: true
        anchors.right: true

        color: "transparent"

        visible: root.contentReady && !root.surfaceBounce

        updatesEnabled: root.renderActive || root._settleFrames > 0

        mask: Region {
            item: Item {}
        }

        Item {
            id: root
            anchors.fill: parent

            Rectangle {
                anchors.fill: parent
                color: SettingsData.effectiveWallpaperBackgroundColor
            }

            function encodeFileUrl(path) {
                if (!path)
                    return "";
                return "file://" + path.split('/').map(s => encodeURIComponent(s)).join('/');
            }

            property string source: SessionData.getMonitorWallpaper(modelData.name) || ""
            readonly property bool isColorSource: source.startsWith("#")
            readonly property string displaySource: {
                if (!source || isColorSource)
                    return "";
                return source.startsWith("file://") ? source : encodeFileUrl(source);
            }
            property bool contentReady: false
            property bool surfaceBounce: false

            // Live stack is captured into frozenLayer once stable, then unloaded; if the capture stalls the stack just stays live.
            property bool liveActive: false
            property bool frozenValid: false
            property string _frozenSource: ""
            property bool loadFailed: false
            property int _freezeWaitFrames: 0

            readonly property var backingWindow: Window.window
            readonly property bool renderActive: !source || liveActive || _freezeWaitFrames > 0
            property int _settleFrames: 3

            readonly property int maxTextureSize: 8192
            readonly property int textureWidth: Math.min(modelData.width, maxTextureSize)
            readonly property int textureHeight: Math.min(modelData.height, maxTextureSize)

            Component.onCompleted: {
                if (!displaySource) {
                    contentReady = true;
                    return;
                }
                liveActive = true;
            }

            onDisplaySourceChanged: {
                invalidate();
                loadFailed = false;
                _freezeWaitFrames = 0;
                if (!displaySource) {
                    liveActive = false;
                    frozenValid = false;
                    _frozenSource = "";
                    contentReady = true;
                    return;
                }
                liveActive = true;
            }

            function regenerate() {
                invalidate();
                if (!displaySource)
                    return;
                if (liveActive) {
                    scheduleFreeze();
                    return;
                }
                liveActive = true;
            }

            function handleDisplayable() {
                contentReady = true;
                if (liveLoader.item?.currentFailed) {
                    if (!frozenValid)
                        loadFailed = true;
                    liveActive = false;
                }
                invalidate();
            }

            function scheduleFreeze() {
                if (!liveLoader.item?.stable)
                    return;
                frozenLayer.scheduleUpdate();
                _freezeWaitFrames = 3;
                _settleFrames = 3;
                // No wedge watchdog: an occluded surface may never produce frames, the freeze just waits
                backingWindow?.update();
            }

            function completeFreeze() {
                const live = liveLoader.item;
                if (!live || !live.stable)
                    return;
                frozenValid = true;
                _frozenSource = displaySource;
                liveActive = false;
                log.info("froze blur layer for", modelData.name);
                invalidate();
            }

            onTextureWidthChanged: regenerate()
            onTextureHeightChanged: regenerate()

            function invalidate() {
                _settleFrames = 3;
                backingWindow?.update();
                if (!_wedgeBounced)
                    wedgeWatchdog.restart();
            }

            onRenderActiveChanged: invalidate()
            onBackingWindowChanged: invalidate()

            // Same wedge recovery as WallpaperBackground
            property bool _wedgeBounced: false

            Timer {
                id: wedgeWatchdog
                interval: 3000
                repeat: false
                onTriggered: {
                    if (!root.backingWindow || !blurWallpaperWindow.visible || IdleService.isShellLocked)
                        return;
                    log.warn("no frame swapped on", modelData.name, "since last invalidate, re-attaching surface");
                    root._wedgeBounced = true;
                    surfaceReattach.restart();
                }
            }

            Timer {
                id: surfaceReattach
                interval: 0
                repeat: false
                onTriggered: {
                    root.surfaceBounce = true;
                    Qt.callLater(() => {
                        root.surfaceBounce = false;
                    });
                }
            }

            Connections {
                target: root.backingWindow
                function onFrameSwapped() {
                    if (root._settleFrames > 0)
                        root._settleFrames--;
                    root._wedgeBounced = false;
                    wedgeWatchdog.stop();
                    if (root._freezeWaitFrames > 0 && --root._freezeWaitFrames === 0)
                        root.completeFreeze();
                }
                function onVisibleChanged() {
                    root.invalidate();
                }
                function onWidthChanged() {
                    root.regenerate();
                }
                function onHeightChanged() {
                    root.regenerate();
                }
                function onResourcesLost() {
                    root.frozenValid = false;
                    root.regenerate();
                }
            }

            Connections {
                target: Quickshell
                function onScreensChanged() {
                    root.regenerate();
                }
            }

            Connections {
                target: SettingsData
                function onWallpaperFillModeChanged() {
                    root.regenerate();
                }
                function onEffectiveWallpaperBackgroundColorChanged() {
                    root.invalidate();
                }
            }

            Connections {
                target: SessionData
                function onIsLightModeChanged() {
                    if (SessionData.perModeWallpaper) {
                        var newSource = SessionData.getMonitorWallpaper(modelData.name) || "";
                        if (newSource !== root.source) {
                            root.source = newSource;
                        }
                    }
                }
                function onMonitorWallpaperFillModesChanged() {
                    root.regenerate();
                }
                function onPerMonitorWallpaperChanged() {
                    root.regenerate();
                }
            }

            // Theme changes repaint DankBackdrop but nothing else wakes the render loop
            Connections {
                target: Theme
                enabled: root.isColorSource || root.loadFailed
                function onPrimaryChanged() {
                    root.invalidate();
                }
                function onBackgroundChanged() {
                    root.invalidate();
                }
            }

            Connections {
                target: IdleService
                function onIsShellLockedChanged() {
                    if (IdleService.isShellLocked)
                        return;
                    root.invalidate();
                }
            }

            Connections {
                target: liveLoader.item
                function onBecameDisplayable() {
                    root.handleDisplayable();
                }
                function onStableChanged() {
                    if (liveLoader.item.stable)
                        root.scheduleFreeze();
                }
                function onTransitioningChanged() {
                    root.invalidate();
                }
            }

            Loader {
                anchors.fill: parent
                active: !root.source || root.isColorSource || root.loadFailed
                asynchronous: true

                sourceComponent: DankBackdrop {
                    screenName: modelData.name
                }
            }

            ShaderEffectSource {
                id: frozenLayer
                anchors.fill: parent
                sourceItem: liveContainer
                live: false
                mipmap: false
                recursive: false
                smooth: true
                visible: root.frozenValid || root.liveActive
                textureSize: Qt.size(root.textureWidth, root.textureHeight)
            }

            Item {
                id: liveContainer
                anchors.fill: parent
                visible: root.liveActive

                Loader {
                    id: liveLoader
                    anchors.fill: parent
                    active: root.liveActive
                    asynchronous: false

                    // Cached images reach Ready synchronously during creation, before Connections retargets
                    onLoaded: {
                        if (item.displayableNow)
                            root.handleDisplayable();
                        if (item.stable)
                            root.scheduleFreeze();
                    }

                    sourceComponent: BlurredWallpaperLive {
                        wallpaperSource: root.displaySource
                        initialSource: root._frozenSource
                        screenName: modelData.name
                        blurTextureSize: Qt.size(root.textureWidth, root.textureHeight)
                    }
                }
            }
        }
    }
}
