// ═══════════════════════════════════════════════════════════════════
//  NeonContextMenu — right-click / on-demand contextual menu
// ═══════════════════════════════════════════════════════════════════
//
//  A floating menu that opens at an arbitrary anchor (typically
//  the cursor position from a pointer-event), shows a dynamic list
//  of items, dismisses on click-outside or Escape.
//
//  Items support:
//   • Plain action rows (label + optional icon).
//   • Disabled state (greyed, non-clickable).
//   • Separator lines (set `separator: true` on the entry).
//   • Danger emphasis (red text + hover, e.g. for "Eliminar").
//   • Trailing keyboard hint (e.g. "Ctrl+C").
//
//  Mounting pattern:
//
//    in-out property <bool> ctx-visible: false;
//    in-out property <length> ctx-x: 0px;
//    in-out property <length> ctx-y: 0px;
//    in property <[ContextMenuItem]> ctx-items: [];
//    callback ctx-pick(int);
//
//    NeonContextMenu {
//        items: root.ctx-items;
//        visible-flag <=> root.ctx-visible;
//        anchor-x: root.ctx-x;
//        anchor-y: root.ctx-y;
//        bound-w: root.width;
//        bound-h: root.height;
//        item-selected(idx) => { root.ctx-pick(idx); }
//    }
//
//  Open on right-click:
//    pointer-event(e) => {
//        if (e.kind == PointerEventKind.down
//            && e.button == PointerEventButton.right) {
//            root.ctx-x = self.mouse-x;
//            root.ctx-y = self.mouse-y;
//            root.ctx-visible = true;
//        }
//    }
//
// ═══════════════════════════════════════════════════════════════════

import { Theme } from "../theme.slint";

/// One menu row. The renderer dispatches based on `separator`:
/// when true, the row collapses to a 1px divider and ignores
/// label/icon/disabled/danger.
export struct ContextMenuItem {
    label: string,
    /// Optional unicode glyph (no SVG support inline). Empty hides
    /// the icon column.
    icon: string,
    /// Optional trailing hint, typically a keyboard shortcut. Empty
    /// hides it.
    shortcut: string,
    disabled: bool,
    /// True → red text + red hover. Use for destructive actions
    /// (delete, remove, reset).
    danger: bool,
    /// True → renders as a divider; label/icon ignored.
    separator: bool,
}

export component NeonContextMenu inherits Rectangle {
    // ── State ────────────────────────────────────────────────────
    in property <[ContextMenuItem]> items;
    in-out property <bool> visible-flag: false;
    /// Anchor in PARENT coordinates. Caller typically sets this
    /// from a pointer-event's `(self.mouse-x, self.mouse-y)`.
    in property <length> anchor-x: 0px;
    in property <length> anchor-y: 0px;
    /// Bounding box (parent's width/height). The menu auto-flips
    /// or clamps so it stays inside.
    in property <length> bound-w: 9999px;
    in property <length> bound-h: 9999px;
    /// Fixed menu width. Caller can override for wide-label menus.
    in property <length> menu-width: 220px;
    /// Approximate row height — used only for clamp math, not for
    /// per-row styling (each row owns its own min-height).
    property <length> row-height: 28px;
    property <length> separator-height: 9px;

    // Total content height — derived from item kinds.
    property <length> content-height: {
        // Slint can't sum a `for` so we use a manual count: one row
        // per item, separators are shorter. Fold via a recursive
        // expression isn't possible either — fall back to a worst-
        // case estimate used only for bound clamping.
        return root.items.length * root.row-height + 16px;  // padding cushion
    }

    // Auto-flip when the anchor would push the menu off bounds.
    property <length> resolved-x: anchor-x + menu-width > bound-w
                                   ? max(0px, bound-w - menu-width - 4px)
                                   : anchor-x;
    property <length> resolved-y: anchor-y + content-height > bound-h
                                   ? max(0px, bound-h - content-height - 4px)
                                   : anchor-y;

    callback item-selected(int);
    callback dismissed;

    visible: visible-flag;

    // ── Click-outside scrim ─────────────────────────────────────
    if (visible-flag): scrim := TouchArea {
        x: 0px;
        y: 0px;
        width: bound-w;
        height: bound-h;
        clicked => {
            visible-flag = false;
            root.dismissed();
        }
        // Right-click on the scrim also dismisses (don't open a
        // second menu underneath).
        pointer-event(e) => {
            if (e.kind == PointerEventKind.down
                && e.button == PointerEventButton.right)
            {
                visible-flag = false;
                root.dismissed();
            }
        }
    }

    // ── Menu card ────────────────────────────────────────────────
    if (visible-flag): Rectangle {
        x: root.resolved-x;
        y: root.resolved-y;
        width: root.menu-width;
        background: Theme.bg-elevated;
        border-radius: Theme.radius-md;
        border-width: 1px;
        border-color: Theme.border;
        drop-shadow-blur: Theme.shadow-blur-lg;
        drop-shadow-color: #00000080;

        // Eat clicks so the scrim doesn't dismiss when clicking
        // inside the menu card itself.
        TouchArea {
            width: parent.width;
            height: parent.height;
        }

        VerticalLayout {
            padding: 4px;
            spacing: 0px;

            for item[idx] in root.items: Rectangle {
                // Separator rows render as a 1px divider with
                // padding above/below; everything else is a
                // clickable row.
                height: item.separator ? root.separator-height : root.row-height;
                background: !item.separator && row-touch.has-hover
                    ? (item.danger ? Theme.bg-danger-hover : Theme.bg-hover)
                    : transparent;
                border-radius: Theme.radius-sm;

                if (item.separator): Rectangle {
                    height: 1px;
                    width: parent.width - 8px;
                    x: 4px;
                    y: (parent.height - 1px) / 2;
                    background: Theme.border;
                }

                if (!item.separator): HorizontalLayout {
                    padding-left: Theme.sp-2;
                    padding-right: Theme.sp-2;
                    spacing: Theme.sp-2;
                    alignment: start;

                    if (item.icon != ""): Text {
                        text: item.icon;
                        font-size: Theme.font-base;
                        color: item.disabled
                            ? Theme.text-dim
                            : (item.danger ? Theme.neon-red : Theme.text-muted);
                        min-width: 16px;
                        vertical-alignment: center;
                    }
                    Text {
                        text: item.label;
                        font-size: Theme.font-sm;
                        color: item.disabled
                            ? Theme.text-dim
                            : (item.danger
                                ? (row-touch.has-hover ? Theme.neon-red : Theme.text-primary)
                                : Theme.text-primary);
                        opacity: item.disabled ? 0.5 : 1.0;
                        horizontal-stretch: 1;
                        vertical-alignment: center;
                        overflow: elide;
                    }
                    if (item.shortcut != ""): Text {
                        text: item.shortcut;
                        font-size: Theme.font-xs;
                        color: Theme.text-dim;
                        vertical-alignment: center;
                    }
                }

                row-touch := TouchArea {
                    enabled: !item.separator && !item.disabled;
                    clicked => {
                        root.item-selected(idx);
                        root.visible-flag = false;
                    }
                    mouse-cursor: !item.separator && !item.disabled
                        ? MouseCursor.pointer
                        : MouseCursor.default;
                }
            }
        }
    }

    // ── Escape-to-dismiss ────────────────────────────────────────
    // FocusScope grabs Escape so the host doesn't have to wire it
    // separately. `forward-focus` to scope so it auto-receives
    // keyboard when the menu opens.
    if (visible-flag): scope := FocusScope {
        x: -1px; y: -1px; width: 1px; height: 1px;
        key-pressed(event) => {
            if (event.text == Key.Escape) {
                root.visible-flag = false;
                root.dismissed();
                return accept;
            }
            return reject;
        }
        init => { self.focus(); }
    }
}
