import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Notifications
import Quickshell.Wayland
import qs.Commons
import qs.Modules.MainScreen
import qs.Modules.Panels.Settings
import qs.Services.System
import qs.Services.UI
import qs.Widgets

// Notification History panel
SmartPanel {
  id: root

  preferredWidth: Math.round((Settings.data.notifications.enableMarkdown ? 540 : 440) * Style.uiScaleRatio)
  preferredHeight: Math.round((Settings.data.notifications.enableMarkdown ? 640 : 540) * Style.uiScaleRatio)

  onOpened: {
    NotificationService.updateLastSeenTs();
  }

  panelContent: Rectangle {
    id: panelContent
    color: "transparent"
    focus: true

    // Force focus when opened
    Connections {
      target: root
      function onOpened() {
        panelContent.forceActiveFocus();
      }
    }

    Keys.onPressed: event => {
                      // Tab navigation for categories
                      if (event.key === Qt.Key_Tab) {
                        currentRange = (currentRange + 1) % 4;
                        event.accepted = true;
                        return;
                      }

                      if (event.key === Qt.Key_Backtab) { // Shift+Tab
                        currentRange = (currentRange - 1 + 4) % 4;
                        event.accepted = true;
                        return;
                      }

                      // Navigation Up/Down
                      if (checkKey(event, 'up')) {
                        moveSelection(-1);
                        event.accepted = true;
                        return;
                      }
                      if (checkKey(event, 'down')) {
                        moveSelection(1);
                        event.accepted = true;
                        return;
                      }

                      // Action Navigation Left/Right
                      if (checkKey(event, 'left')) {
                        moveAction(-1);
                        event.accepted = true;
                        return;
                      }
                      if (checkKey(event, 'right')) {
                        moveAction(1);
                        event.accepted = true;
                        return;
                      }

                      // Activation (Enter)
                      if (checkKey(event, 'enter')) {
                        activateSelection();
                        event.accepted = true;
                        return;
                      }

                      // Removal (Delete/Remove)
                      if (checkKey(event, 'remove') || event.key === Qt.Key_Delete) {
                        removeSelection();
                        event.accepted = true;
                        return;
                      }
                    }

    function parseActions(actions) {
      try {
        return JSON.parse(actions || "[]");
      } catch (e) {
        return [];
      }
    }

    function moveSelection(dir) {
      var m = NotificationService.historyModel;
      if (!m || m.count === 0)
        return;

      var newIndex = focusIndex;
      var found = false;
      var count = m.count;

      // If no selection yet, start from beginning (or end if up)
      if (focusIndex === -1) {
        if (dir > 0)
          newIndex = -1;
        else
          newIndex = count;
      }

      // Loop to find next visible item
      var loopCount = 0;
      while (loopCount < count) {
        newIndex += dir;

        // Bounds check
        if (newIndex < 0 || newIndex >= count) {
          break; // Stop at edges
        }

        var item = m.get(newIndex);
        if (item && isInCurrentRange(item.timestamp)) {
          found = true;
          break;
        }
        loopCount++;
      }

      if (found) {
        focusIndex = newIndex;
        actionIndex = -1; // Reset action selection
        scrollToItem(focusIndex);
      }
    }

    function moveAction(dir) {
      if (focusIndex === -1)
        return;
      var item = NotificationService.historyModel.get(focusIndex);
      if (!item)
        return;

      var actions = parseActions(item.actionsJson);

      if (actions.length === 0)
        return;

      var newActionIndex = actionIndex + dir;

      // Clamp between -1 (body) and actions.length - 1
      if (newActionIndex < -1)
        newActionIndex = -1;
      if (newActionIndex >= actions.length)
        newActionIndex = actions.length - 1;

      actionIndex = newActionIndex;
    }

    function activateSelection() {
      if (focusIndex === -1)
        return;
      var item = NotificationService.historyModel.get(focusIndex);
      if (!item)
        return;

      if (actionIndex >= 0) {
        var actions = parseActions(item.actionsJson);
        if (actionIndex < actions.length) {
          if (NotificationService.invokeAction(item.id, actions[actionIndex].identifier))
            root.close();
        }
      } else {
        var delegate = notificationColumn.children[focusIndex];
        if (!delegate)
          return;
        if (!(delegate.canExpand || delegate.isExpanded))
          return;

        if (scrollView.expandedId === item.id) {
          scrollView.expandedId = "";
        } else {
          scrollView.expandedId = item.id;
        }
      }
    }

    function removeSelection() {
      if (focusIndex === -1)
        return;
      var item = NotificationService.historyModel.get(focusIndex);
      if (!item)
        return;

      NotificationService.removeFromHistory(item.id);
      // selection updates automatically?
      // If we remove item at index i, the next item becomes index i.
      // So focusIndex is still valid (unless it was last item).
      // But we should re-verify if it exists.
      // Actually NotificationService removal might be async or immediate.
      // If immediate, model count decreases.
      // We might need to clamp focusIndex.
      // Let's handle this in a helper or just let the user navigate again.
      // Better UX: select next available or previous if last.
    }

    function scrollToItem(index) {
      // Find the delegate item
      if (index < 0 || index >= notificationColumn.children.length)
        return;

      var item = notificationColumn.children[index];
      if (item && item.visible) {
        // Use the internal flickable from NScrollView for accurate scrolling
        var flickable = scrollView._internalFlickable;
        if (!flickable || !flickable.contentItem)
          return;

        var pos = flickable.contentItem.mapFromItem(item, 0, 0);
        var itemY = pos.y;
        var itemHeight = item.height;

        var currentContentY = flickable.contentY;
        var viewHeight = flickable.height;

        // Check if above visible area
        if (itemY < currentContentY) {
          flickable.contentY = Math.max(0, itemY - Style.marginM);
        } else
          // Check if below visible area
          if (itemY + itemHeight > currentContentY + viewHeight) {
            flickable.contentY = (itemY + itemHeight) - viewHeight + Style.marginM;
          }
      }
    }

    // Calculate content height based on header + tabs (if visible) + content
    property real calculatedHeight: {
      if (NotificationService.historyModel.count === 0) {
        return headerBox.implicitHeight + scrollView.implicitHeight + Style.margin2L + Style.marginM;
      }
      return headerBox.implicitHeight + scrollView.implicitHeight + Style.margin2L + Style.marginM;
    }
    property real contentPreferredHeight: Math.min(root.preferredHeight, Math.ceil(calculatedHeight))

    property real layoutWidth: Math.max(1, root.preferredWidth - Style.margin2L)

    // State (lazy-loaded with panelContent)
    property var rangeCounts: [0, 0, 0, 0]
    property var lastKnownDate: null  // Track the current date to detect day changes

    // UI state (lazy-loaded with panelContent)
    // 0 = All, 1 = Today, 2 = Yesterday, 3 = Earlier
    property int currentRange: 1  // start on Today by default
    property bool groupByDate: true
    onCurrentRangeChanged: resetFocus()

    // Keyboard navigation state
    property int focusIndex: -1
    property int actionIndex: -1  // For actions within a notification

    function resetFocus() {
      focusIndex = -1;
      actionIndex = -1;
    }

    function checkKey(event, settingName) {
      return Keybinds.checkKey(event, settingName, Settings);
    }

    // Helper functions (lazy-loaded with panelContent)
    function dateOnly(d) {
      return new Date(d.getFullYear(), d.getMonth(), d.getDate());
    }

    function getDateKey(d) {
      // Returns a string key for the date (YYYY-MM-DD) for comparison
      var date = dateOnly(d);
      return date.getFullYear() + "-" + date.getMonth() + "-" + date.getDate();
    }

    function rangeForTimestamp(ts) {
      var dt = new Date(ts);
      var today = dateOnly(new Date());
      var thatDay = dateOnly(dt);

      var diffMs = today - thatDay;
      var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

      if (diffDays === 0)
        return 0;
      if (diffDays === 1)
        return 1;
      return 2;
    }

    function recalcRangeCounts() {
      var m = NotificationService.historyModel;
      if (!m || typeof m.count === "undefined" || m.count <= 0) {
        panelContent.rangeCounts = [0, 0, 0, 0];
        return;
      }

      var counts = [0, 0, 0, 0];

      counts[0] = m.count;

      for (var i = 0; i < m.count; ++i) {
        var item = m.get(i);
        if (!item || typeof item.timestamp === "undefined")
          continue;
        var r = rangeForTimestamp(item.timestamp);
        counts[r + 1] = counts[r + 1] + 1;
      }

      panelContent.rangeCounts = counts;
    }

    function isInCurrentRange(ts) {
      if (currentRange === 0)
        return true;
      return rangeForTimestamp(ts) === (currentRange - 1);
    }

    function countForRange(range) {
      return rangeCounts[range] || 0;
    }

    function hasNotificationsInCurrentRange() {
      var m = NotificationService.historyModel;
      if (!m || m.count === 0) {
        return false;
      }
      for (var i = 0; i < m.count; ++i) {
        var item = m.get(i);
        if (item && isInCurrentRange(item.timestamp))
          return true;
      }
      return false;
    }

    Component.onCompleted: {
      recalcRangeCounts();
      // Initialize lastKnownDate
      lastKnownDate = getDateKey(new Date());
    }

    Connections {
      target: NotificationService.historyModel
      function onCountChanged() {
        panelContent.recalcRangeCounts();
      }
    }

    // Timer to check for day changes at midnight
    Timer {
      id: dayChangeTimer
      interval: 60000  // Check every minute
      repeat: true
      running: true  // Always runs when panelContent exists (panel is open)
      onTriggered: {
        var currentDateKey = panelContent.getDateKey(new Date());
        if (panelContent.lastKnownDate !== null && panelContent.lastKnownDate !== currentDateKey) {
          // Day has changed, recalculate counts
          panelContent.recalcRangeCounts();
        }
        panelContent.lastKnownDate = currentDateKey;
      }
    }

    ColumnLayout {
      id: mainColumn
      anchors.fill: parent
      anchors.margins: Style.marginL
      spacing: Style.marginM

      // Header section
      NBox {
        id: headerBox
        Layout.fillWidth: true
        implicitHeight: header.implicitHeight + Style.margin2M

        ColumnLayout {
          id: header
          anchors.fill: parent
          anchors.margins: Style.marginM
          spacing: Style.marginM

          RowLayout {
            id: headerRow
            NIcon {
              icon: "bell"
              pointSize: Style.fontSizeXXL
              color: Color.mPrimary
            }

            NText {
              text: I18n.tr("common.notifications")
              pointSize: Style.fontSizeL
              font.weight: Style.fontWeightBold
              color: Color.mOnSurface
              Layout.fillWidth: true
            }

            NIconButton {
              icon: NotificationService.doNotDisturb ? "bell-off" : "bell"
              tooltipText: NotificationService.doNotDisturb ? I18n.tr("tooltips.do-not-disturb-enabled") : I18n.tr("tooltips.do-not-disturb-enabled")
              baseSize: Style.baseWidgetSize * 0.8
              onClicked: NotificationService.doNotDisturb = !NotificationService.doNotDisturb
            }

            NIconButton {
              icon: "trash"
              tooltipText: I18n.tr("actions.clear-history")
              baseSize: Style.baseWidgetSize * 0.8
              onClicked: {
                NotificationService.clearHistory();
                // Close panel as there is nothing more to see.
                root.close();
              }
            }

            NIconButton {
              icon: "settings"
              tooltipText: I18n.tr("common.settings")
              baseSize: Style.baseWidgetSize * 0.8
              onClicked: {
                SettingsPanelService.openToTab(SettingsPanel.Tab.Notifications, 0, screen);
                root.close();
              }
            }

            NIconButton {
              icon: "close"
              tooltipText: I18n.tr("common.close")
              baseSize: Style.baseWidgetSize * 0.8
              onClicked: root.close()
            }
          }

          // Time range tabs ([All] / [Today] / [Yesterday] / [Earlier])
          NTabBar {
            id: tabsBox
            Layout.fillWidth: true
            visible: NotificationService.historyModel.count > 0 && panelContent.groupByDate
            currentIndex: panelContent.currentRange
            tabHeight: Style.toOdd(Style.baseWidgetSize * 0.8)
            spacing: Style.marginXS
            distributeEvenly: true

            NTabButton {
              tabIndex: 0
              text: I18n.tr("launcher.categories.all") + " (" + panelContent.countForRange(0) + ")"
              checked: tabsBox.currentIndex === 0
              onClicked: panelContent.currentRange = 0
              pointSize: Style.fontSizeXS
            }

            NTabButton {
              tabIndex: 1
              text: I18n.tr("notifications.range.today") + " (" + panelContent.countForRange(1) + ")"
              checked: tabsBox.currentIndex === 1
              onClicked: panelContent.currentRange = 1
              pointSize: Style.fontSizeXS
            }

            NTabButton {
              tabIndex: 2
              text: I18n.tr("notifications.range.yesterday") + " (" + panelContent.countForRange(2) + ")"
              checked: tabsBox.currentIndex === 2
              onClicked: panelContent.currentRange = 2
              pointSize: Style.fontSizeXS
            }

            NTabButton {
              tabIndex: 3
              text: I18n.tr("notifications.range.earlier") + " (" + panelContent.countForRange(3) + ")"
              checked: tabsBox.currentIndex === 3
              onClicked: panelContent.currentRange = 3
              pointSize: Style.fontSizeXS
            }
          }
        }
      }

      // Notification list container with gradient overlay
      Item {
        Layout.fillWidth: true
        Layout.fillHeight: true

        NScrollView {
          id: scrollView
          anchors.fill: parent
          horizontalPolicy: ScrollBar.AlwaysOff
          verticalPolicy: ScrollBar.AsNeeded
          reserveScrollbarSpace: false
          gradientColor: Color.mSurface

          // Track which notification is expanded
          property string expandedId: ""

          ColumnLayout {
            width: panelContent.layoutWidth
            spacing: Style.marginM

            // Empty state when no notifications
            NBox {
              visible: !panelContent.hasNotificationsInCurrentRange()
              Layout.fillWidth: true
              Layout.preferredHeight: emptyState.implicitHeight + Style.marginXL

              ColumnLayout {
                id: emptyState
                anchors.fill: parent
                anchors.margins: Style.marginM
                spacing: Style.marginM

                Item {
                  Layout.fillHeight: true
                }

                NIcon {
                  icon: "bell-off"
                  pointSize: (NotificationService.historyModel.count === 0) ? 48 : Style.baseWidgetSize
                  color: Color.mOnSurfaceVariant
                  Layout.alignment: Qt.AlignHCenter
                }

                NText {
                  text: I18n.tr("notifications.panel.no-notifications")
                  pointSize: (NotificationService.historyModel.count === 0) ? Style.fontSizeL : Style.fontSizeM
                  color: Color.mOnSurfaceVariant
                  Layout.alignment: Qt.AlignHCenter
                }

                NText {
                  visible: NotificationService.historyModel.count === 0
                  text: I18n.tr("notifications.panel.description")
                  pointSize: Style.fontSizeS
                  color: Color.mOnSurfaceVariant
                  horizontalAlignment: Text.AlignHCenter
                  Layout.fillWidth: true
                  wrapMode: Text.WordWrap
                }

                Item {
                  Layout.fillHeight: true
                }
              }
            }

            // Notification list container
            Item {
              visible: panelContent.hasNotificationsInCurrentRange()
              Layout.fillWidth: true
              Layout.preferredHeight: notificationColumn.implicitHeight

              Column {
                id: notificationColumn
                width: panelContent.layoutWidth
                spacing: Style.marginM

                Repeater {
                  model: NotificationService.historyModel

                  delegate: Item {
                    id: notificationDelegate
                    width: parent.width
                    visible: panelContent.isInCurrentRange(model.timestamp)
                    height: visible && !isRemoving ? contentColumn.height + Style.margin2M : 0

                    property int listIndex: index
                    property string notificationId: model.id
                    property string appName: model.appName || ""
                    property bool isExpanded: scrollView.expandedId === notificationId
                    property bool canExpand: summaryText.truncated || bodyText.truncated
                    property real swipeOffset: 0
                    property real pressGlobalX: 0
                    property real pressGlobalY: 0
                    property bool isSwiping: false
                    property bool isRemoving: false
                    property string pendingLink: ""
                    readonly property real swipeStartThreshold: Math.round(16 * Style.uiScaleRatio)
                    readonly property real swipeDismissThreshold: Math.max(110, width * 0.3)
                    readonly property int removeAnimationDuration: Style.animationNormal
                    readonly property int notificationTextFormat: (Settings.data.notifications.enableMarkdown && notificationDelegate.isExpanded) ? Text.MarkdownText : Text.StyledText
                    readonly property real actionButtonSize: Style.baseWidgetSize * 0.7
                    readonly property real buttonClusterWidth: notificationDelegate.actionButtonSize * 2 + Style.marginXS
                    readonly property real iconSize: Math.round(40 * Style.uiScaleRatio)

                    function isSafeLink(link) {
                      if (!link)
                        return false;
                      const lower = link.toLowerCase();
                      const schemes = ["http://", "https://", "mailto:"];
                      return schemes.some(scheme => lower.startsWith(scheme));
                    }

                    function linkAtPoint(x, y) {
                      if (!Settings.data.notifications.enableMarkdown || !notificationDelegate.isExpanded)
                        return "";

                      if (summaryText) {
                        const summaryPoint = summaryText.mapFromItem(historyInteractionArea, x, y);
                        if (summaryPoint.x >= 0 && summaryPoint.y >= 0 && summaryPoint.x <= summaryText.width && summaryPoint.y <= summaryText.height) {
                          const summaryLink = summaryText.linkAt ? summaryText.linkAt(summaryPoint.x, summaryPoint.y) : "";
                          if (isSafeLink(summaryLink))
                            return summaryLink;
                        }
                      }

                      if (bodyText) {
                        const bodyPoint = bodyText.mapFromItem(historyInteractionArea, x, y);
                        if (bodyPoint.x >= 0 && bodyPoint.y >= 0 && bodyPoint.x <= bodyText.width && bodyPoint.y <= bodyText.height) {
                          const bodyLink = bodyText.linkAt ? bodyText.linkAt(bodyPoint.x, bodyPoint.y) : "";
                          if (isSafeLink(bodyLink))
                            return bodyLink;
                        }
                      }

                      return "";
                    }

                    function updateCursorAt(x, y) {
                      if (notificationDelegate.isExpanded && notificationDelegate.linkAtPoint(x, y)) {
                        historyInteractionArea.cursorShape = Qt.PointingHandCursor;
                      } else {
                        historyInteractionArea.cursorShape = Qt.ArrowCursor;
                      }
                    }

                    transform: Translate {
                      x: notificationDelegate.swipeOffset
                    }

                    function dismissBySwipe() {
                      if (isRemoving)
                        return;
                      isRemoving = true;
                      isSwiping = false;

                      if (Settings.data.general.animationDisabled) {
                        NotificationService.removeFromHistory(notificationId);
                        return;
                      }

                      swipeOffset = swipeOffset >= 0 ? width + Style.marginL : -width - Style.marginL;
                      opacity = 0;
                      removeTimer.restart();
                    }

                    Timer {
                      id: removeTimer
                      interval: notificationDelegate.removeAnimationDuration
                      repeat: false
                      onTriggered: NotificationService.removeFromHistory(notificationId)
                    }

                    Behavior on swipeOffset {
                      enabled: !Settings.data.general.animationDisabled && !notificationDelegate.isSwiping
                      NumberAnimation {
                        duration: notificationDelegate.removeAnimationDuration
                        easing.type: Easing.OutCubic
                      }
                    }

                    Behavior on opacity {
                      enabled: !Settings.data.general.animationDisabled && notificationDelegate.isRemoving
                      NumberAnimation {
                        duration: notificationDelegate.removeAnimationDuration
                        easing.type: Easing.OutCubic
                      }
                    }

                    Behavior on height {
                      enabled: !Settings.data.general.animationDisabled && notificationDelegate.isRemoving
                      NumberAnimation {
                        duration: notificationDelegate.removeAnimationDuration
                        easing.type: Easing.OutCubic
                      }
                    }

                    Behavior on y {
                      enabled: !Settings.data.general.animationDisabled && notificationDelegate.isRemoving
                      NumberAnimation {
                        duration: notificationDelegate.removeAnimationDuration
                        easing.type: Easing.OutCubic
                      }
                    }

                    // Parse actions safely
                    property var actionsList: parseActions(model.actionsJson)

                    property bool isFocused: index === panelContent.focusIndex

                    Rectangle {
                      anchors.fill: parent
                      radius: Style.radiusM
                      color: Color.mSurfaceVariant
                      border.color: {
                        if (notificationDelegate.isFocused)
                          return Color.mPrimary;
                        if (Settings.data.ui.boxBorderEnabled)
                          return Qt.alpha(Color.mOutline, Style.opacityHeavy);
                        return "transparent";
                      }
                      border.width: notificationDelegate.isFocused ? Style.borderM : Style.borderS

                      Behavior on color {
                        enabled: !Settings.data.general.animationDisabled
                        ColorAnimation {
                          duration: Style.animationFast
                        }
                      }
                    }

                    // Click to expand/collapse
                    MouseArea {
                      id: historyInteractionArea
                      anchors.fill: parent
                      anchors.rightMargin: notificationDelegate.buttonClusterWidth + Style.marginM
                      enabled: !notificationDelegate.isRemoving
                      hoverEnabled: true
                      cursorShape: Qt.ArrowCursor
                      onPressed: mouse => {
                                   panelContent.focusIndex = index;
                                   panelContent.actionIndex = -1;

                                   if (notificationDelegate.isExpanded) {
                                     const link = notificationDelegate.linkAtPoint(mouse.x, mouse.y);
                                     if (link) {
                                       notificationDelegate.pendingLink = link;
                                     } else {
                                       notificationDelegate.pendingLink = "";
                                     }
                                   }

                                   if (mouse.button !== Qt.LeftButton)
                                   return;
                                   const globalPoint = historyInteractionArea.mapToGlobal(mouse.x, mouse.y);
                                   notificationDelegate.pressGlobalX = globalPoint.x;
                                   notificationDelegate.pressGlobalY = globalPoint.y;
                                   notificationDelegate.isSwiping = false;
                                 }
                      onPositionChanged: mouse => {
                                           if (!(mouse.buttons & Qt.LeftButton) || notificationDelegate.isRemoving)
                                           return;

                                           const globalPoint = historyInteractionArea.mapToGlobal(mouse.x, mouse.y);
                                           const deltaX = globalPoint.x - notificationDelegate.pressGlobalX;
                                           const deltaY = globalPoint.y - notificationDelegate.pressGlobalY;

                                           if (!notificationDelegate.isSwiping) {
                                             if (Math.abs(deltaX) < notificationDelegate.swipeStartThreshold)
                                             return;

                                             // Only start a swipe-dismiss when horizontal movement is dominant.
                                             if (Math.abs(deltaX) <= Math.abs(deltaY) * 1.15) {
                                               return;
                                             }
                                             notificationDelegate.isSwiping = true;
                                           }

                                           if (notificationDelegate.pendingLink && Math.abs(deltaX) >= notificationDelegate.swipeStartThreshold) {
                                             notificationDelegate.pendingLink = "";
                                           }

                                           notificationDelegate.swipeOffset = deltaX;
                                         }
                      onReleased: mouse => {
                                    if (mouse.button !== Qt.LeftButton)
                                    return;

                                    if (notificationDelegate.isSwiping) {
                                      if (Math.abs(notificationDelegate.swipeOffset) >= notificationDelegate.swipeDismissThreshold) {
                                        notificationDelegate.dismissBySwipe();
                                      } else {
                                        notificationDelegate.swipeOffset = 0;
                                      }
                                      notificationDelegate.isSwiping = false;
                                      notificationDelegate.pendingLink = "";
                                      return;
                                    }

                                    if (notificationDelegate.pendingLink) {
                                      Qt.openUrlExternally(notificationDelegate.pendingLink);
                                      notificationDelegate.pendingLink = "";
                                      return;
                                    }

                                    // Without a default action, or if invoking it fails,
                                    // fall back to focusing the sender window by app identity.
                                    var actions = notificationDelegate.actionsList;
                                    var hasDefault = actions.some(function (a) {
                                      return a.identifier === "default";
                                    });
                                    if (hasDefault && NotificationService.invokeAction(notificationDelegate.notificationId, "default")) {
                                      root.close();
                                    } else {
                                      NotificationService.focusSenderWindow(notificationDelegate.appName);
                                      root.close();
                                    }
                                  }
                      onCanceled: {
                        notificationDelegate.isSwiping = false;
                        notificationDelegate.swipeOffset = 0;
                        notificationDelegate.pendingLink = "";
                        historyInteractionArea.cursorShape = Qt.ArrowCursor;
                      }
                    }

                    HoverHandler {
                      target: historyInteractionArea
                      onPointChanged: notificationDelegate.updateCursorAt(point.position.x, point.position.y)
                      onActiveChanged: {
                        if (!active) {
                          historyInteractionArea.cursorShape = Qt.ArrowCursor;
                        }
                      }
                    }

                    onVisibleChanged: {
                      if (!visible) {
                        notificationDelegate.isSwiping = false;
                        notificationDelegate.swipeOffset = 0;
                        notificationDelegate.opacity = 1;
                        notificationDelegate.isRemoving = false;
                        removeTimer.stop();
                      }
                    }

                    Component.onDestruction: removeTimer.stop()

                    Column {
                      id: contentColumn
                      anchors.left: parent.left
                      anchors.right: parent.right
                      anchors.top: parent.top
                      anchors.margins: Style.marginM
                      spacing: Style.marginM

                      Row {
                        width: parent.width
                        spacing: Style.marginM

                        // Icon
                        NImageRounded {
                          anchors.verticalCenter: notificationDelegate.isExpanded ? undefined : parent.verticalCenter
                          width: notificationDelegate.iconSize
                          height: notificationDelegate.iconSize
                          radius: Math.min(Style.radiusL, width / 2)
                          imagePath: model.cachedImage || model.originalImage || ""
                          borderColor: "transparent"
                          borderWidth: 0
                          fallbackIcon: "bell"
                          fallbackIconSize: 24
                        }

                        // Content
                        Column {
                          width: parent.width - notificationDelegate.iconSize - notificationDelegate.buttonClusterWidth - Style.margin2M
                          spacing: Style.marginXS

                          // Header row with app name and timestamp
                          Row {
                            width: parent.width
                            spacing: Style.marginS

                            // Urgency indicator
                            Rectangle {
                              width: 6
                              height: 6
                              anchors.verticalCenter: parent.verticalCenter
                              radius: 3
                              visible: model.urgency !== 1
                              color: {
                                if (model.urgency === 2)
                                  return Color.mError;
                                else if (model.urgency === 0)
                                  return Color.mOnSurfaceVariant;
                                else
                                  return "transparent";
                              }
                            }

                            NText {
                              text: model.appName || "Unknown App"
                              pointSize: Style.fontSizeXS
                              font.weight: Style.fontWeightBold
                              color: Color.mSecondary
                            }

                            NText {
                              textFormat: Text.PlainText
                              text: " " + Time.formatRelativeTime(model.timestamp)
                              pointSize: Style.fontSizeXXS
                              color: Color.mOnSurfaceVariant
                              anchors.bottom: parent.bottom
                            }
                          }

                          // Summary
                          NText {
                            id: summaryText
                            width: parent.width
                            text: (Settings.data.notifications.enableMarkdown && notificationDelegate.isExpanded) ? (model.summaryMarkdown || I18n.tr("common.no-summary")) : (model.summary || I18n.tr("common.no-summary"))
                            pointSize: Style.fontSizeM
                            color: Color.mOnSurface
                            textFormat: notificationDelegate.notificationTextFormat
                            wrapMode: Text.Wrap
                            maximumLineCount: notificationDelegate.isExpanded ? 999 : 2
                            elide: Text.ElideRight
                          }

                          // Body
                          NText {
                            id: bodyText
                            width: parent.width
                            text: (Settings.data.notifications.enableMarkdown && notificationDelegate.isExpanded) ? (model.bodyMarkdown || "") : (model.body || "")
                            pointSize: Style.fontSizeS
                            color: Color.mOnSurfaceVariant
                            textFormat: notificationDelegate.notificationTextFormat
                            wrapMode: Text.Wrap
                            maximumLineCount: notificationDelegate.isExpanded ? 999 : 3
                            elide: Text.ElideRight
                            visible: text.length > 0
                          }

                          // Actions Flow
                          Flow {
                            width: parent.width
                            spacing: Style.marginS
                            visible: notificationDelegate.actionsList.length > 0

                            Repeater {
                              model: notificationDelegate.actionsList

                              delegate: NButton {
                                text: modelData.text
                                fontSize: Style.fontSizeS

                                readonly property bool actionNavActive: notificationDelegate.isFocused && panelContent.actionIndex !== -1
                                readonly property bool isSelected: actionNavActive && panelContent.actionIndex === index

                                backgroundColor: isSelected ? Color.mSecondary : Color.mPrimary
                                textColor: isSelected ? Color.mOnSecondary : Color.mOnPrimary

                                outlined: false
                                implicitHeight: 24

                                onHoveredChanged: {
                                  if (hovered) {
                                    panelContent.focusIndex = notificationDelegate.listIndex;
                                  }
                                }

                                // Capture modelData in a property to avoid reference errors
                                property var actionData: modelData
                                onClicked: {
                                  if (NotificationService.invokeAction(notificationDelegate.notificationId, actionData.identifier))
                                    root.close();
                                }
                              }
                            }
                          }
                        }

                        Item {
                          width: notificationDelegate.buttonClusterWidth
                          height: notificationDelegate.actionButtonSize

                          Row {
                            anchors.right: parent.right
                            spacing: Style.marginXS

                            NIconButton {
                              id: expandButton
                              icon: notificationDelegate.isExpanded ? "chevron-up" : "chevron-down"
                              tooltipText: notificationDelegate.isExpanded ? I18n.tr("notifications.panel.click-to-collapse") || "Click to collapse" : I18n.tr("notifications.panel.click-to-expand") || "Click to expand"
                              baseSize: notificationDelegate.actionButtonSize
                              opacity: (notificationDelegate.canExpand || notificationDelegate.isExpanded) ? 1.0 : 0.0
                              enabled: notificationDelegate.canExpand || notificationDelegate.isExpanded

                              onClicked: {
                                notificationDelegate.pendingLink = "";
                                historyInteractionArea.cursorShape = Qt.ArrowCursor;
                                if (scrollView.expandedId === notificationId) {
                                  scrollView.expandedId = "";
                                } else {
                                  scrollView.expandedId = notificationId;
                                }
                              }
                            }

                            // Delete button
                            NIconButton {
                              icon: "trash"
                              tooltipText: I18n.tr("tooltips.delete-notification")
                              baseSize: notificationDelegate.actionButtonSize

                              onClicked: {
                                NotificationService.removeFromHistory(notificationId);
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
