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

PanelWindow {
    id: root
    readonly property var log: Log.scoped("DankOSD")

    property string blurNamespace: "dms:osd"
    WlrLayershell.namespace: blurNamespace

    property alias content: contentLoader.sourceComponent
    property alias contentLoader: contentLoader
    property var modelData
    property bool shouldBeVisible: false
    property int autoHideInterval: 2000
    property bool enableMouseInteraction: false
    property real osdWidth: Theme.iconSize + Theme.spacingS * 2
    property real osdHeight: Theme.iconSize + Theme.spacingS * 2
    property int animationDuration: Theme.mediumDuration
    property var animationEasing: Theme.emphasizedEasing

    signal osdShown
    signal osdHidden

    function show() {
        if (SessionData.suppressOSD)
            return;
        if (shouldBeVisible) {
            hideTimer.restart();
            return;
        }
        OSDManager.showOSD(root);
        closeTimer.stop();
        shouldBeVisible = true;
        visible = true;
        hideTimer.restart();
        osdShown();
    }

    function hide() {
        shouldBeVisible = false;
        closeTimer.restart();
    }

    function resetHideTimer() {
        if (shouldBeVisible) {
            hideTimer.restart();
        }
    }

    function updateHoverState() {
        let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered;
        if (enableMouseInteraction) {
            if (isHovered) {
                hideTimer.stop();
            } else if (shouldBeVisible) {
                hideTimer.restart();
            }
        }
    }

    function setChildHovered(hovered) {
        osdContainer.childHovered = hovered;
        updateHoverState();
    }

    screen: modelData
    visible: false

    Connections {
        target: Quickshell
        function onScreensChanged() {
            if (!root.visible && !root.shouldBeVisible)
                return;
            const currentScreenName = root.screen?.name;
            if (!currentScreenName) {
                root.hide();
                return;
            }
            for (let i = 0; i < Quickshell.screens.length; i++) {
                if (Quickshell.screens[i].name === currentScreenName)
                    return;
            }
            root.shouldBeVisible = false;
            root.visible = false;
            hideTimer.stop();
            closeTimer.stop();
            osdHidden();
        }
    }

    WlrLayershell.layer: LayerShell.fromEnv("DMS_OSD_LAYER", WlrLayer.Overlay, {
        "allow": ["top", "overlay"],
        "invalidLayer": WlrLayer.Overlay,
        "label": "OSDs"
    })
    WlrLayershell.exclusiveZone: -1
    WlrLayershell.keyboardFocus: WlrKeyboardFocus.None

    WindowBlur {
        targetWindow: root
        blurX: shadowBuffer
        blurY: shadowBuffer
        blurWidth: shouldBeVisible ? alignedWidth : 0
        blurHeight: shouldBeVisible ? alignedHeight : 0
        blurRadius: Theme.cornerRadius
    }

    color: "transparent"

    readonly property real dpr: CompositorService.getScreenScale(screen)
    readonly property real screenWidth: screen.width
    readonly property real screenHeight: screen.height
    readonly property real shadowBuffer: 15
    readonly property real alignedWidth: Theme.px(osdWidth, dpr)
    readonly property real alignedHeight: Theme.px(osdHeight, dpr)

    readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter

    readonly property var barEdgeOffsets: {
        const offsets = {
            "top": 0,
            "bottom": 0,
            "left": 0,
            "right": 0
        };
        const configs = SettingsData.barConfigs;
        if (!screen || !configs)
            return offsets;
        const defaultBar = configs[0] || SettingsData.getBarConfig("default");
        for (var i = 0; i < configs.length; i++) {
            const bc = configs[i];
            if (!bc || !(bc.enabled ?? true) || !(bc.visible ?? true))
                continue;
            const prefs = bc.screenPreferences || ["all"];
            if (!prefs.includes("all") && !SettingsData.isScreenInPreferences(screen, prefs))
                continue;
            const innerPadding = bc.innerPadding ?? (defaultBar?.innerPadding ?? 4);
            const widgetThickness = Math.max(20, 26 + innerPadding * 0.6);
            const thickness = Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding));
            const spacing = bc.spacing ?? (defaultBar?.spacing ?? 4);
            const bottomGap = bc.bottomGap ?? (defaultBar?.bottomGap ?? 0);
            const offset = thickness + spacing + bottomGap;
            switch (bc.position ?? SettingsData.Position.Top) {
            case SettingsData.Position.Top:
                offsets.top = Math.max(offsets.top, offset);
                break;
            case SettingsData.Position.Bottom:
                offsets.bottom = Math.max(offsets.bottom, offset);
                break;
            case SettingsData.Position.Left:
                offsets.left = Math.max(offsets.left, offset);
                break;
            case SettingsData.Position.Right:
                offsets.right = Math.max(offsets.right, offset);
                break;
            }
        }
        return offsets;
    }

    readonly property real dockThickness: {
        if (!SettingsData.showDock)
            return 0;
        return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10;
    }

    readonly property real dockOffset: {
        if (!SettingsData.showDock || SettingsData.dockAutoHide || SettingsData.dockSmartAutoHide)
            return 0;
        return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin;
    }

    readonly property real alignedX: {
        const margin = Theme.spacingM;
        const centerX = (screenWidth - alignedWidth) / 2;

        switch (SettingsData.osdPosition) {
        case SettingsData.Position.Left:
        case SettingsData.Position.Bottom:
        case SettingsData.Position.LeftCenter:
            const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
            return Theme.snap(margin + Math.max(barEdgeOffsets.left, leftDockOffset), dpr);
        case SettingsData.Position.Top:
        case SettingsData.Position.Right:
        case SettingsData.Position.RightCenter:
            const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
            return Theme.snap(screenWidth - alignedWidth - margin - Math.max(barEdgeOffsets.right, rightDockOffset), dpr);
        case SettingsData.Position.TopCenter:
        case SettingsData.Position.BottomCenter:
        default:
            return Theme.snap(centerX, dpr);
        }
    }

    readonly property real alignedY: {
        const margin = Theme.spacingM;
        const centerY = (screenHeight - alignedHeight) / 2;

        switch (SettingsData.osdPosition) {
        case SettingsData.Position.Top:
        case SettingsData.Position.Left:
        case SettingsData.Position.TopCenter:
            const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0;
            return Theme.snap(margin + Math.max(barEdgeOffsets.top, topDockOffset), dpr);
        case SettingsData.Position.Right:
        case SettingsData.Position.Bottom:
        case SettingsData.Position.BottomCenter:
            const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0;
            return Theme.snap(screenHeight - alignedHeight - margin - Math.max(barEdgeOffsets.bottom, bottomDockOffset), dpr);
        case SettingsData.Position.LeftCenter:
        case SettingsData.Position.RightCenter:
        default:
            return Theme.snap(centerY, dpr);
        }
    }

    anchors {
        top: true
        left: true
    }

    WlrLayershell.margins {
        left: Math.max(0, Theme.snap(alignedX - shadowBuffer, dpr))
        top: Math.max(0, Theme.snap(alignedY - shadowBuffer, dpr))
    }

    implicitWidth: alignedWidth + (shadowBuffer * 2)
    implicitHeight: alignedHeight + (shadowBuffer * 2)

    Timer {
        id: hideTimer

        interval: autoHideInterval
        repeat: false
        onTriggered: {
            if (!enableMouseInteraction || !mouseArea.containsMouse) {
                hide();
            } else {
                hideTimer.restart();
            }
        }
    }

    Timer {
        id: closeTimer
        interval: animationDuration + 50
        onTriggered: {
            if (!shouldBeVisible) {
                visible = false;
                osdHidden();
            }
        }
    }

    Item {
        id: osdContainer
        x: shadowBuffer
        y: shadowBuffer
        width: alignedWidth
        height: alignedHeight
        opacity: shouldBeVisible ? 1 : 0
        scale: shouldBeVisible ? 1 : 0.9

        property bool childHovered: false
        readonly property real popupSurfaceAlpha: Theme.popupTransparency

        Rectangle {
            id: background
            anchors.fill: parent
            radius: Theme.cornerRadius
            color: "transparent"
            border.color: BlurService.borderColor
            border.width: BlurService.borderWidth
            z: -1
        }

        ElevationShadow {
            id: bgShadowLayer
            anchors.fill: parent
            z: -1
            level: Theme.elevationLevel3
            fallbackOffset: 6
            targetRadius: Theme.cornerRadius
            targetColor: Theme.withAlpha(Theme.surfaceContainer, osdContainer.popupSurfaceAlpha)
            borderColor: Theme.outlineMedium
            borderWidth: 1
            shadowEnabled: Theme.elevationEnabled && SettingsData.popoutElevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
        }

        MouseArea {
            id: mouseArea
            anchors.fill: parent
            hoverEnabled: enableMouseInteraction
            acceptedButtons: Qt.NoButton
            propagateComposedEvents: true
            z: -1
            onContainsMouseChanged: updateHoverState()
        }

        onChildHoveredChanged: updateHoverState()

        Loader {
            id: contentLoader
            anchors.fill: parent
            active: root.visible
            asynchronous: false
        }

        Behavior on opacity {
            NumberAnimation {
                duration: animationDuration
                easing.type: animationEasing
            }
        }

        Behavior on scale {
            NumberAnimation {
                duration: animationDuration
                easing.type: animationEasing
            }
        }
    }

    mask: Region {
        item: bgShadowLayer
    }
}
