// One notification, rendered GNOME-style: app icon, app name, summary, body,
// optional image, action buttons and an inline reply box.
//
// Shared by the toasts and the notification center so a notification looks the
// same wherever you meet it.
//
// IconButton/ThemedIcon come from the quicksettings module — they belong in
// widgets/, but that directory is owned elsewhere.

import QtQuick
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Notifications
import qs.config
import qs.services
import qs.modules.quicksettings
import qs.modules.bar
import qs.widgets

Rectangle {
    id: root

    required property var notification

    // The center renders a denser card than a toast banner.
    property bool compact: false

    signal dismissed
    readonly property alias hovered: hover.containsMouse
    // Raised when the reply field takes focus: the toast window has to ask the
    // compositor for keyboard focus before anything can be typed.
    signal replyFocusChanged(bool focused)

    // The image sits outside `layout`, so the card has to be tall enough for
    // whichever of the two is bigger.
    implicitHeight: Math.max(layout.implicitHeight + 24, image.visible ? 96 : 0)
    radius: Theme.cardRadius
    border.width: 0
    // A real popover surface, the same one the date menu and clipboard panel
    // use, rather than a 6% foreground tint.
    //
    // A tint that faint has nothing behind it: the toast window is transparent,
    // so the card was sitting directly on whatever happened to be on screen and
    // the text competed with it. Notifications are the one surface someone reads
    // without having chosen to look at it, so it has to be legible over a
    // bright photo and a white document alike.
    color: hover.containsMouse
        ? Theme.alpha(Theme.mix(Theme.bgPopover, Theme.fg, 0.06), Theme.toastAlpha)
        : Theme.alpha(Theme.bgPopover, Theme.toastAlpha)

    Behavior on color {
        ColorAnimation {
            duration: Theme.durFast
        }
    }

    // Critical notifications get a red edge rather than a color wash, so the
    // text contrast never changes.
    Rectangle {
        anchors.left: parent.left
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        anchors.margins: 6
        width: 3
        radius: 1.5
        border.width: 0
        color: Theme.urgent
        visible: root.notification.urgency === NotificationUrgency.Critical
    }

    readonly property var defaultAction: {
        for (const action of root.notification.actions) {
            if (action.identifier === "default")
                return action;
        }
        return null;
    }

    // "default" is the card click, so it never becomes a button.
    readonly property bool hasButtons: {
        for (const action of root.notification.actions) {
            if (action.identifier !== "default")
                return true;
        }
        return false;
    }

    // Everything except the buttons: clicking the body runs the notification's
    // default action, which is what GNOME does.
    MouseArea {
        id: hover
        anchors.fill: parent
        hoverEnabled: true
        cursorShape: root.defaultAction ? Qt.PointingHandCursor : Qt.ArrowCursor
        onClicked: {
            if (root.defaultAction)
                root.defaultAction.invoke();
        }
    }

    IconImage {
        id: appIcon
        anchors.left: parent.left
        anchors.leftMargin: 14
        anchors.top: parent.top
        anchors.topMargin: 13
        implicitSize: 22
        asynchronous: true
        source: {
            if (root.notification.appIcon !== "")
                return Quickshell.iconPath(root.notification.appIcon, true);
            const entry = DesktopEntries.byId(root.notification.desktopEntry);
            return entry ? Quickshell.iconPath(entry.icon, true) : "";
        }
        visible: source !== ""
    }

    IconButton {
        id: closeButton
        anchors.right: parent.right
        anchors.rightMargin: 6
        anchors.top: parent.top
        anchors.topMargin: 6
        size: 24
        iconSize: 12
        tint: Theme.fgDim
        icon: "window-close-symbolic"
        onClicked: root.dismissed()
    }

    IconButton {
        id: settingsMenuButton
        anchors.right: closeButton.left
        anchors.rightMargin: 2
        anchors.top: parent.top
        anchors.topMargin: 6
        size: 24
        iconSize: 14
        tint: Theme.fgDim
        icon: "view-more-symbolic"
        iconFallback: "open-menu-symbolic"
        onClicked: settingsMenu.visible = !settingsMenu.visible
    }

    Popover {
        id: settingsMenu
        anchorItem: settingsMenuButton

        // A Column already measures itself from its children, and in Qt 6 both
        // implicit sizes are read-only on a positioner -- assigning them makes
        // the whole shell fail to load rather than just this menu.
        Column {
            TrayMenuRow {
                id: notificationSettings
                label: "Notification settings"
                onActivated: {
                    ShellState.openSettings("notifications");
                    settingsMenu.visible = false;
                }
            }
        }
    }

    Column {
        id: layout
        anchors.left: appIcon.visible ? appIcon.right : parent.left
        anchors.leftMargin: appIcon.visible ? 10 : 14
        anchors.right: image.visible ? image.left : settingsMenuButton.left
        anchors.rightMargin: 8
        anchors.top: parent.top
        anchors.topMargin: 12
        spacing: 3

        Text {
            width: parent.width
            text: {
                const app = root.notification.appName || "Notification";
                const time = Notifs.timeText(root.notification.id);
                return time === "" ? app : app + " · " + time;
            }
            color: Theme.fgMuted
            elide: Text.ElideRight
            font.family: Theme.fontFamily
            font.pixelSize: Theme.fontSizeSmall
        }

        Text {
            width: parent.width
            visible: text !== ""
            text: root.notification.summary
            color: Theme.fg
            elide: Text.ElideRight
            maximumLineCount: 2
            wrapMode: Text.Wrap
            font.family: Theme.fontFamily
            font.pixelSize: Theme.fontSize
            font.weight: Font.DemiBold
        }

        Text {
            width: parent.width
            visible: text !== ""
            text: root.notification.body
            color: Theme.fgDim
            // The server advertises bodyMarkupSupported, so bodies arrive with
            // the freedesktop subset of HTML in them.
            textFormat: Text.StyledText
            linkColor: Theme.accent
            elide: Text.ElideRight
            wrapMode: Text.Wrap
            maximumLineCount: root.compact ? 6 : 3
            font.family: Theme.fontFamily
            font.pixelSize: Theme.fontSize
        }

        Item {
            width: 1
            height: 4
            visible: actionRow.visible || replyBox.visible
        }

        Flow {
            id: actionRow
            width: parent.width
            spacing: 6
            visible: root.hasButtons

            Repeater {
                model: root.notification.actions

                Rectangle {
                    id: actionButton

                    required property var modelData

                    visible: actionButton.modelData.identifier !== "default"
                    width: visible ? actionLabel.implicitWidth + 22 : 0
                    height: visible ? 28 : 0
                    radius: Theme.pillRadius
                    border.width: 0
                    color: actionMouse.containsMouse ? Theme.alpha(Theme.accent, 0.28) : Theme.alpha(Theme.fg, 0.1)

                    Behavior on color {
                        ColorAnimation {
                            duration: Theme.durFast
                        }
                    }

                    Text {
                        id: actionLabel
                        anchors.centerIn: parent
                        text: actionButton.modelData.text
                        color: Theme.fg
                        font.family: Theme.fontFamily
                        font.pixelSize: Theme.fontSizeSmall
                        font.weight: Font.Medium
                    }

                    MouseArea {
                        id: actionMouse
                        anchors.fill: parent
                        hoverEnabled: true
                        cursorShape: Qt.PointingHandCursor
                        onClicked: actionButton.modelData.invoke()
                    }
                }
            }
        }

        Rectangle {
            id: replyBox
            width: parent.width
            height: 36
            visible: root.notification.hasInlineReply
            radius: Theme.cardRadius - 2
            border.width: 1
            border.color: replyInput.activeFocus ? Theme.alpha(Theme.accent, 0.5) : Theme.alpha(Theme.fg, 0.12)
            color: Theme.alpha(Theme.fg, 0.08)

            TextInput {
                id: replyInput
                anchors.left: parent.left
                anchors.leftMargin: 10
                anchors.right: replySend.left
                anchors.rightMargin: 4
                anchors.verticalCenter: parent.verticalCenter
                color: Theme.fg
                selectionColor: Theme.alpha(Theme.accent, 0.5)
                selectedTextColor: Theme.fg
                font.family: Theme.fontFamily
                font.pixelSize: Theme.fontSize
                clip: true

                onActiveFocusChanged: root.replyFocusChanged(replyInput.activeFocus)

                onAccepted: {
                    if (replyInput.text.length === 0)
                        return;
                    root.notification.sendInlineReply(replyInput.text);
                    replyInput.text = "";
                    root.dismissed();
                }

                Text {
                    anchors.fill: parent
                    verticalAlignment: Text.AlignVCenter
                    visible: replyInput.text === ""
                    text: root.notification.inlineReplyPlaceholder || "Reply"
                    color: Theme.fgMuted
                    font: replyInput.font
                }
            }

            IconButton {
                id: replySend
                anchors.right: parent.right
                anchors.rightMargin: 3
                anchors.verticalCenter: parent.verticalCenter
                size: 28
                iconSize: 14
                icon: "go-next-symbolic"
                onClicked: replyInput.accepted()
            }
        }
    }

    // Notification images (album art, avatars, screenshots) sit on the right.
    ClippingRectangle {
        id: image
        anchors.right: parent.right
        anchors.rightMargin: 14
        anchors.top: closeButton.bottom
        anchors.topMargin: 4
        implicitWidth: 52
        implicitHeight: 52
        radius: 8
        border.width: 0
        color: "transparent"
        visible: root.notification.image !== ""

        Image {
            anchors.fill: parent
            source: root.notification.image
            fillMode: Image.PreserveAspectCrop
            asynchronous: true
            sourceSize.width: 104
            sourceSize.height: 104
        }
    }
}
