import { RecipientCard, ItemSquareData, NoteDisplayData } from "card.slint";
import { TabStrip } from "tab-strip.slint";
import { SearchBar } from "search-bar.slint";
import { ChipBar, ChipData } from "chip-bar.slint";
import { RecipientGrid, ProductGrid, RecipientTileData, ProductTileData } from "option-grid.slint";
import { ProductDetailPanel, ProductUnitDisplayData } from "product-detail.slint";
import { RecipientDetailPanel } from "recipient-detail.slint";
import { AddProductForm, ShopifySuggestion } from "add-product-form.slint";
import { LookupModal, PickerProductData, PickerUnitData } from "lookup-modal.slint";
import { SettingsModal } from "settings-modal.slint";
import { RecipientPickerModal, RecipientPickerEntryData } from "recipient-picker.slint";
import { StateTransitionModal } from "state-transition-modal.slint";
import { Colors, Typography } from "tokens.slint";

// Inter font family (SC07 — D-04)
import "fonts/Inter-Regular.ttf";
import "fonts/Inter-Medium.ttf";
import "fonts/Inter-SemiBold.ttf";
import "fonts/Inter-Bold.ttf";

struct CardData {
    recipient-name: string,
    status-pill: string,
    status-date: string,
    item-squares: [ItemSquareData],
    item-display-label: string,
    notes: [NoteDisplayData],
    missing-label: string,
    stale-label: string,
    is-stale: bool,
    show-refresh: bool,
    refresh-disabled: bool,
    refresh-label: string,
    retry-label: string,
    show-error: bool,
    refresh-error: bool,
    // Archive state: 0=Active, 1=ToBeArchived, 2=Archived
    archive-state: int,
    discord-user-id: string,
    contact-secondary: string,
    recipient-initial: string,
    shopify-customer-url: string,
    shopify-order-url: string,
    github-issue-url: string,
    email: string,
    package-id: string,
    active-item-id: string,
    remove-confirming: bool,
    is-unassigned: bool,
    unassigned-customer-name: string,
    purpose-color: color,
    purpose-label: string,
    discord-username: string,
    avatar-image: image,
    recipient-id: string,
    partial-return-warning: bool,
    has-unassigned-units: bool,
}

export component DashboardWindow inherits Window {
    title: "WITwhat";
    default-font-family: "Inter";
    background: Colors.background;
    in property <string> title-text: "";
    in property <int> min-column-width: 320;
    in property <string> refresh-all-label: "Refresh all";
    in property <bool> refresh-all-disabled: false;
    in property <[CardData]> cards;

    // Discovery navigation properties
    in property <int> active-mode-index: 0;
    in property <bool> tab-flash-active: false;
    in property <string> toast-message: "";
    in-out property <bool> toast-visible: false;
    in property <bool> toast-is-warning: false;

    // Search bar properties
    in-out property <string> search-text: "";
    in property <int> search-result-count: 0;
    in-out property <bool> search-focused: false;

    // Chip bar properties
    in property <[ChipData]> status-chips;

    // Option grid properties
    in property <bool> show-option-grid: false;
    in property <bool> show-recipient-grid: false;
    in property <[RecipientTileData]> recipient-tiles;
    in property <[ProductTileData]> product-tiles;

    // Filtered card view properties
    in property <bool> show-filtered-breadcrumb: false;
    in property <string> filtered-label: "";

    // Archive properties
    in property <int> hidden-archived-count: 0;
    in property <bool> current-show-archived: false;
    in property <bool> toast-has-undo: false;

    // Pending edit sync indicator
    in property <int> pending-edit-count: 0;

    // Connection status: 0=unconfigured, 1=connecting, 2=connected, 3=disconnected
    in property <int> connection-status: 0;
    in property <bool> show-settings-button: true;

    // Lookup modal properties
    in property <bool> lookup-modal-open: false;
    in property <int> lookup-target-card-index: -1;
    in property <string> lookup-target-card-name: "";
    in property <[PickerProductData]> lookup-products: [];
    in property <string> lookup-target-card-id: "";
    in property <length> lookup-tree-viewport-height: 40px;
    in property <bool> shopify-fetch-in-progress: false;
    in property <string> shopify-fetched-image-url: "";
    in-out property <string> lookup-search-text: "";
    in-out property <bool> lookup-show-create-form: false;

    // G-08: forwarding properties for Rust-side modal state reset
    in-out property <string> lookup-expanded-product-id <=> lookup-modal-inst.expanded-product-id;
    in-out property <string> lookup-creating-unit-product-id <=> lookup-modal-inst.creating-unit-product-id;
    in-out property <string> lookup-new-unit-serial-id <=> lookup-modal-inst.new-unit-serial-id;
    in-out property <bool> lookup-reassign-prompt-visible <=> lookup-modal-inst.reassign-prompt-visible;

    // Settings modal properties
    in-out property <bool> settings-modal-open: false;
    in-out property <string> settings-store-slug: "";
    in-out property <string> settings-github-project-url: "";
    in-out property <string> settings-error: "";
    in-out property <bool> settings-shopify-token-configured: false;
    in-out property <string> settings-shopify-token: "";
    in-out property <bool> settings-shopify-token-changing: false;
    in-out property <bool> settings-saving-in-progress: false;
    in-out property <bool> discord-token-configured: false;
    in-out property <string> discord-token-text: "";
    callback settings-save-clicked();
    callback settings-cancel-clicked();
    callback settings-shopify-clear-clicked();
    callback discord-save-token();
    callback discord-clear-token();

    // text-input-focused derived from search bar focus (out so Rust can read it)
    out property <bool> text-input-focused: search-bar-inst.input-has-focus;

    callback refresh-all-clicked;
    callback card-refresh(int);

    callback card-add-item(int);
    callback card-remove-item(int);
    callback card-confirm-remove-item(int);
    callback card-remove-single-item(int, int);  // card-index, item-square-index

    // Archive callbacks
    callback toast-undo();
    callback toggle-show-archived();
    callback card-archive(int);
    callback card-unarchive(int);
    callback card-archive-now(int);

    // Communication and external link callbacks
    callback card-open-discord-dm(int);
    callback card-copy-email(int);
    callback card-open-shopify-customer(int);
    callback card-open-shopify-order(int);
    callback card-open-github-issue(int);

    // Start Return and warning callbacks (Phase 19)
    callback card-start-return(int);
    callback card-view-warning(int);

    // Item square click — opens state change modal from card context (G-17)
    callback card-item-square-clicked(int, string);  // card-index, item-id

    // Notes popover callbacks (Phase 20.1.1 Plan 04)
    callback card-post-note(int, string);              // (card-index, note-text)
    callback card-notes-popover-opened(int);           // (card-index)

    // Discovery navigation callbacks
    callback mode-switch-up();
    callback mode-switch-down();
    callback esc-pressed();
    callback home-pressed();
    callback end-pressed();
    callback tab-clicked(int);

    // Settings callback
    callback settings-clicked();

    // Search and filter callbacks
    callback search-changed(string);
    callback chip-toggled(int);
    callback character-pressed(string);

    // Option grid properties
    in property <int> recipient-viewport-rows: 0;

    // Option grid callbacks
    callback tile-clicked(string);
    callback breadcrumb-back();

    // Internal: triggers card model rebuild (invoked from event loop after background image updates)
    callback refresh-cards-requested();

    // Lookup modal callbacks
    callback lookup-search-changed(string);
    callback lookup-item-selected(int, string, string, string);  // card-index, product-id, name, hint
    callback lookup-create-confirmed(int, string, string);       // card-index, display-name, shopify-url
    callback lookup-unit-selected(string, string, string);       // serial-id, product-id, card-id
    callback lookup-unit-force-reassign(string, string, string); // serial-id, product-id, card-id
    callback lookup-create-unit-confirmed(string, string);       // product-id, serial-number

    // Recipient picker callbacks
    callback pick-recipient-clicked(int);
    callback assign-recipient(int, string);
    callback create-and-assign-recipient(int, string);

    // Recipient picker modal properties
    in-out property <bool> show-recipient-picker: false;
    in property <string> picker-customer-name: "";
    in property <int> picker-card-index: -1;
    in property <[RecipientPickerEntryData]> picker-recipients;

    // Recipient picker search callback — Rust re-filters the list
    callback picker-search-changed(string);

    // Sync lifecycle callback — fired after sync updates the cards model
    callback sync-cards-updated();

    // Product detail panel properties
    in property <bool> product-detail-visible: false;
    in property <string> detail-product-name;
    in property <string> detail-product-id;
    in property <image> detail-product-image;
    in property <bool> detail-has-image;
    in property <string> detail-shopify-url;
    in property <string> detail-github-issue-ref;
    in property <[ProductUnitDisplayData]> detail-units;
    in-out property <bool> detail-creation-in-progress: false;
    in-out property <int> detail-selected-unit-index: -1;
    in-out property <string> detail-selected-unit-serial: "";

    // Recipient detail sidebar properties (Phase 20.1.1 Plan 08, D-16/D-17/D-18)
    in property <bool> recipient-detail-visible: false;
    in property <string> detail-recipient-id: "";
    in property <string> detail-recipient-name: "";
    in property <bool> detail-recipient-has-avatar: false;
    in property <image> detail-recipient-avatar;
    in property <string> detail-recipient-initial: "?";
    in property <color> detail-recipient-purpose-color: #ffffff;
    in property <string> detail-recipient-purpose: "";
    in property <string> detail-recipient-vision-rx-od: "";
    in property <string> detail-recipient-vision-rx-os: "";
    in property <string> detail-recipient-discord-username: "";
    in property <string> detail-recipient-email: "";
    in property <string> detail-recipient-shopify-customer-url: "";
    in property <string> detail-recipient-issue-url: "";
    in property <[string]> detail-recipient-purpose-options: [];

    // Sidebar editing state (in-out so edits survive re-mounts)
    in-out property <bool> detail-editing-purpose: false;
    in-out property <string> detail-purpose-draft: "";
    in-out property <bool> detail-editing-rx-od: false;
    in-out property <string> detail-rx-od-draft: "";
    in-out property <bool> detail-editing-rx-os: false;
    in-out property <string> detail-rx-os-draft: "";
    in-out property <bool> detail-editing-discord-username: false;
    in-out property <string> detail-discord-username-draft: "";

    // Sidebar action callbacks
    callback sidebar-save-purpose(string, string);            // (recipient-id, new-purpose)
    callback sidebar-save-rx-od(string, string);              // (recipient-id, new-rx)
    callback sidebar-save-rx-os(string, string);
    callback sidebar-save-discord-username(string, string);
    callback sidebar-copy-rx(string);                         // (recipient-id)
    callback sidebar-view-shopify-customer(string);
    callback sidebar-view-recipient-issue(string);
    callback sidebar-close();
    callback card-name-navigate(string);                      // (recipient-id) — Plan 09 completes

    // Add product form properties
    in property <bool> add-product-visible: false;
    in-out property <string> add-product-name;
    in-out property <string> add-product-shopify-url;
    in property <[ShopifySuggestion]> add-product-suggestions;
    in property <bool> add-product-show-suggestions: false;
    in property <bool> add-product-has-image;
    in property <image> add-product-image-preview;
    in property <string> add-product-validation-error;

    // Product detail panel callbacks
    callback on-product-create-unit-with-serial(string, string);  // (product_id, serial_id)
    callback on-product-unit-selection-changed(string, string);   // (product_id, serial_id)
    callback detail-unit-search-changed(string);                   // (query) — filter unit list in detail panel
    callback on-product-change-unit-state(string, string);         // (product_id, serial_id) — D-18.2

    // Product sidecar action callbacks
    callback on-product-sidecar-set-image(string);
    callback on-product-sidecar-view-shopify(string);
    callback on-product-sidecar-view-issue(string);
    callback on-product-sidecar-archive(string);

    // Add product form callbacks
    callback on-add-product-submit();
    callback on-add-product-discard();
    callback on-add-product-name-changed(string);
    callback on-add-product-shopify-url-changed(string);
    callback on-add-product-set-image();
    callback on-add-product-suggestion-selected(string);

    callback on-product-add-clicked();

    // State transition modal properties
    in-out property <bool> state-modal-visible: false;
    in-out property <string> state-modal-serial-id: "";
    in-out property <string> state-modal-current-state: "";
    in-out property <string> state-modal-product-name: "";
    in-out property <bool> state-modal-is-assigned: false;
    in-out property <bool> state-modal-show-unassign: false;
    callback state-modal-confirmed(string, string, bool); // serial_id, new_state, unassign

    // Delete item confirmation modal properties
    in-out property <bool> delete-item-modal-visible: false;
    in-out property <string> delete-item-product-name: "";
    in-out property <string> delete-item-serial-id: "";
    in-out property <string> delete-item-assigned-to: "";
    in-out property <string> delete-item-card-name: "";
    in-out property <int> delete-item-card-index: -1;
    in-out property <int> delete-item-sq-index: -1;
    callback delete-item-confirmed(int, int); // card_index, item_sq_index

    // Start Return confirmation modal properties
    in-out property <bool> start-return-modal-visible: false;
    in-out property <int> start-return-card-index: -1;
    in-out property <string> start-return-recipient-name: "";
    in-out property <string> start-return-item-label: "";

    // Lookup modal close callback
    callback lookup-close();

    // Public function to focus the search bar from Rust
    public function focus-search() {
        search-bar-inst.focus-input();
    }

    // Focus search bar and place cursor at end of text
    public function focus-search-end() {
        search-bar-inst.focus-and-set-cursor-end();
    }

    // Public function to defocus the search bar from Rust
    public function defocus-search() {
        search-bar-inst.remove-focus();
        global-keys.focus();
    }

    // Public function to focus the lookup modal search input from Rust (called after opening modal)
    public function focus-lookup-search() {
        lookup-modal-inst.focus-lookup-search();
    }

    width: 1200px;
    height: 760px;

    // Auto-dismiss toast after 5 seconds (only for non-undo toasts)
    toast-dismiss-timer := Timer {
        interval: 5000ms;
        running: false;
        triggered => {
            root.toast-visible = false;
            toast-dismiss-timer.running = false;
        }
    }
    // Start timer when toast becomes visible without undo
    changed toast-visible => {
        if (root.toast-visible && !root.toast-has-undo) {
            toast-dismiss-timer.running = true;
        } else if (!root.toast-visible) {
            toast-dismiss-timer.running = false;
        }
    }

    Rectangle {
        background: Colors.background;
        border-radius: 8px;

        // Global keyboard handler for discovery navigation
        // Always enabled so Esc and arrow keys work even when search input has focus
        global-keys := FocusScope {
            init => { self.focus(); }
            key-pressed(event) => {
                if event.text == Key.UpArrow {
                    root.mode-switch-up();
                    return accept;
                }
                if event.text == Key.DownArrow {
                    root.mode-switch-down();
                    return accept;
                }
                if event.text == Key.Escape {
                    root.esc-pressed();
                    return accept;
                }
                if event.text == Key.Home {
                    card-flickable.viewport-y = 0px;
                    root.home-pressed();
                    return accept;
                }
                if event.text == Key.End {
                    card-flickable.viewport-y = -(card-flickable.viewport-height - card-flickable.height);
                    root.end-pressed();
                    return accept;
                }
                if event.text == Key.F5 {
                    root.refresh-all-clicked();
                    return accept;
                }
                // Forward printable character keys to search bar only when not already focused
                // Filter out modifier and special keys; let Rust-side callback handle validation
                if !root.text-input-focused
                    && event.text != Key.Tab && event.text != Key.Return
                    && event.text != Key.Backspace && event.text != Key.Delete
                    && event.text != Key.Shift && event.text != Key.Control
                    && event.text != Key.Alt && event.text != Key.Meta
                    && event.text.character-count > 0
                {
                    root.character-pressed(event.text);
                    return accept;
                }
                return reject;
            }

            // Title bar area: search bar + refresh-all button
            search-bar-inst := SearchBar {
                x: 60px;
                y: 22px;
                width: parent.width - 60px - 154px - 12px;
                search-text <=> root.search-text;
                result-count: root.search-result-count;
                text-changed(text) => {
                    root.search-changed(text);
                }
            }

            // D-05: Pending edit text removed — breathing dot is the only indicator

            // Connection status + settings button — top-left corner, above tab strip
            HorizontalLayout {
                x: 4px;
                y: 2px;
                height: 18px;
                spacing: 8px;
                alignment: start;

                // Settings button — shown when settings button is enabled
                if root.show-settings-button : Rectangle {
                    width: 52px;
                    height: 18px;
                    border-radius: 9px;
                    background: settings-touch.has-hover ? Colors.surface-popup : #1e2235;

                    Text {
                        text: "Settings";
                        font-size: Typography.size-xs;
                        color: settings-touch.has-hover ? Colors.text-secondary : Colors.text-dim;
                        horizontal-alignment: center;
                        vertical-alignment: center;
                    }

                    settings-touch := TouchArea {
                        mouse-cursor: pointer;
                        clicked => {
                            root.settings-clicked();
                        }
                    }
                }

                // Colored circle: gray=unconfigured, yellow=connecting, green=connected (breathing when pending edits), red=disconnected
                Rectangle {
                    width: 8px;
                    height: 8px;
                    border-radius: 4px;
                    y: 5px;
                    background: root.connection-status == 0 ? #666666
                              : root.connection-status == 1 ? #f0c040
                              : root.connection-status == 2 ? Colors.success
                              : root.connection-status == 4 ? #f0c040
                              : Colors.error;
                    // D-05: Breathing animation when pending edits exist (1.5s period, opacity 0.4-1.0)
                    opacity: (root.connection-status == 2 && root.pending-edit-count > 0)
                        ? 0.7 + 0.3 * Math.sin(animation-tick() / 1500ms * 360deg)
                        : 1.0;
                }
                Text {
                    text: root.connection-status == 0 ? "Not configured"
                        : root.connection-status == 1 ? "Connecting..."
                        : root.connection-status == 2 ? "Connected"
                        : root.connection-status == 4 ? "Connected (no Shopify)"
                        : "Disconnected";
                    font-size: Typography.size-sm;
                    color: Colors.text-dim;
                    vertical-alignment: center;
                }
            }

            // Refresh-all button
            Rectangle {
                x: parent.width - 154px;
                y: 22px;
                width: 126px;
                height: 32px;
                border-radius: 16px;
                background: root.refresh-all-disabled ? Colors.accent-dim : Colors.accent;
                Text {
                    text: root.refresh-all-label;
                    width: parent.width;
                    height: parent.height;
                    horizontal-alignment: center;
                    vertical-alignment: center;
                    color: #ffffff;
                    font-size: Typography.size-md;
                }
                TouchArea {
                    mouse-cursor: root.refresh-all-disabled ? default : pointer;
                    clicked => {
                        if !root.refresh-all-disabled {
                            root.refresh-all-clicked();
                        }
                    }
                }
            }

            // Click-outside handler: defocus search when clicking on the content area
            TouchArea {
                x: 0px;
                y: 60px;
                width: parent.width;
                height: parent.height - 60px;
                clicked => {
                    search-bar-inst.remove-focus();
                    global-keys.focus();
                }
            }

            // Breadcrumb bar (shown in filtered card view) — sits above card grid
            // Same color as window background so it blends seamlessly;
            // card grid's rounded top corners provide the visual border below.
            if root.show-filtered-breadcrumb : Rectangle {
                x: 60px;
                y: 72px;
                width: parent.width - 72px;
                height: 32px;
                background: Colors.background;

                HorizontalLayout {
                    padding-left: 12px;
                    spacing: 12px;
                    alignment: start;

                    Rectangle {
                        width: 28px;
                        height: 28px;
                        border-radius: 4px;
                        background: back-touch.has-hover ? Colors.surface-popup : transparent;

                        Text {
                            text: "\u{2190}";
                            font-size: Typography.size-md;
                            color: Colors.text-muted;
                            horizontal-alignment: center;
                            vertical-alignment: center;
                        }

                        back-touch := TouchArea {
                            mouse-cursor: pointer;
                            clicked => {
                                root.breadcrumb-back();
                            }
                        }
                    }

                    Text {
                        text: root.filtered-label;
                        font-size: Typography.size-md;
                        color: Colors.text-primary;
                        vertical-alignment: center;
                    }

                    // ">" separator + unit serial when a unit is selected
                    if root.detail-selected-unit-serial != "" : Text {
                        text: ">";
                        font-size: Typography.size-md;
                        color: Colors.text-muted;
                        vertical-alignment: center;
                    }
                    if root.detail-selected-unit-serial != "" : Text {
                        text: root.detail-selected-unit-serial;
                        font-size: Typography.size-md;
                        color: Colors.text-primary;
                        vertical-alignment: center;
                    }
                }
            }

            // Card grid region - offset right by tab strip width, with right margin
            // Bottom reduced by 36px to make room for chip bar
            // Lowered by 32px when breadcrumb is shown so rounded top corners are visible
            Rectangle {
                x: 60px;
                y: root.show-filtered-breadcrumb ? 72px + 32px : 72px;
                width: parent.width - 72px;
                height: root.show-filtered-breadcrumb ? parent.height - 96px - 36px - 32px : parent.height - 96px - 36px;
                background: #12151f;
                border-radius: 8px;

                // Recipient option grid + detail sidebar (D-18: sidebar in Recipients tab only)
                if root.show-option-grid && root.show-recipient-grid : RecipientGrid {
                    x: 0px;
                    y: 0px;
                    width: root.recipient-detail-visible ? parent.width - 330px : parent.width;
                    height: parent.height;
                    tiles: root.recipient-tiles;
                    selected-tile-name: root.detail-recipient-id;
                    viewport-row-count: root.recipient-viewport-rows;
                    tile-clicked(name) => {
                        root.tile-clicked(name);
                    }
                }
                if root.recipient-detail-visible : RecipientDetailPanel {
                    x: parent.width - 326px;
                    y: 6px;
                    width: 320px;
                    height: parent.height - 12px;

                    recipient-id: root.detail-recipient-id;
                    recipient-name: root.detail-recipient-name;
                    has-avatar-image: root.detail-recipient-has-avatar;
                    avatar-image: root.detail-recipient-avatar;
                    recipient-initial: root.detail-recipient-initial;
                    purpose-color: root.detail-recipient-purpose-color;
                    purpose: root.detail-recipient-purpose;
                    vision-rx-od: root.detail-recipient-vision-rx-od;
                    vision-rx-os: root.detail-recipient-vision-rx-os;
                    discord-username: root.detail-recipient-discord-username;
                    shopify-email: root.detail-recipient-email;
                    shopify-customer-url: root.detail-recipient-shopify-customer-url;
                    recipient-issue-url: root.detail-recipient-issue-url;
                    purpose-options: root.detail-recipient-purpose-options;

                    editing-purpose <=> root.detail-editing-purpose;
                    purpose-draft <=> root.detail-purpose-draft;
                    editing-rx-od <=> root.detail-editing-rx-od;
                    rx-od-draft <=> root.detail-rx-od-draft;
                    editing-rx-os <=> root.detail-editing-rx-os;
                    rx-os-draft <=> root.detail-rx-os-draft;
                    editing-discord-username <=> root.detail-editing-discord-username;
                    discord-username-draft <=> root.detail-discord-username-draft;

                    save-purpose(v) => { root.sidebar-save-purpose(root.detail-recipient-id, v); }
                    save-rx-od(v) => { root.sidebar-save-rx-od(root.detail-recipient-id, v); }
                    save-rx-os(v) => { root.sidebar-save-rx-os(root.detail-recipient-id, v); }
                    save-discord-username(v) => { root.sidebar-save-discord-username(root.detail-recipient-id, v); }
                    copy-rx() => { root.sidebar-copy-rx(root.detail-recipient-id); }
                    view-shopify-customer-clicked() => { root.sidebar-view-shopify-customer(root.detail-recipient-id); }
                    view-recipient-issue-clicked() => { root.sidebar-view-recipient-issue(root.detail-recipient-id); }
                    close-clicked() => { root.sidebar-close(); }
                    navigate-back() => { root.breadcrumb-back(); }
                }

                // Product option grid
                if root.show-option-grid && !root.show-recipient-grid : ProductGrid {
                    x: 0px;
                    y: 0px;
                    width: parent.width;
                    height: parent.height;
                    tiles: root.product-tiles;
                    tile-clicked(name) => {
                        root.tile-clicked(name);
                    }
                    add-product-clicked() => { root.on-product-add-clicked(); }
                }

                // Product detail sidecar — right sidebar in filtered card view when a product tile was clicked
                if !root.show-option-grid && root.product-detail-visible : ProductDetailPanel {
                    x: parent.width - 326px;
                    y: 6px;
                    width: 320px;
                    height: parent.height - 12px;
                    product-name: root.detail-product-name;
                    product-id: root.detail-product-id;
                    product-image: root.detail-product-image;
                    has-image: root.detail-has-image;
                    shopify-url: root.detail-shopify-url;
                    github-issue-ref: root.detail-github-issue-ref;
                    units: root.detail-units;
                    creation-in-progress <=> root.detail-creation-in-progress;
                    selected-unit-index <=> root.detail-selected-unit-index;
                    selected-unit-serial <=> root.detail-selected-unit-serial;
                    set-image-clicked(id) => { root.on-product-sidecar-set-image(id); }
                    view-shopify-clicked(id) => { root.on-product-sidecar-view-shopify(id); }
                    view-issue-clicked(id) => { root.on-product-sidecar-view-issue(id); }
                    archive-clicked(id) => { root.on-product-sidecar-archive(id); }
                    create-unit-with-serial(pid, serial) => { root.on-product-create-unit-with-serial(pid, serial); }
                    unit-selection-changed(pid, serial) => { root.on-product-unit-selection-changed(pid, serial); }
                    unit-search-changed(query) => { root.detail-unit-search-changed(query); }
                    change-unit-state-clicked(pid, serial) => { root.on-product-change-unit-state(pid, serial); }
                }

                card-flickable := Flickable {
                    x: 0px;
                    y: 0px;
                    width: (root.product-detail-visible && !root.show-option-grid) || (root.recipient-detail-visible && !root.show-option-grid)
                        ? parent.width - 330px
                        : parent.width;
                    height: parent.height;
                    visible: !root.show-option-grid;
                    // Data-driven card grid: 2 columns when sidecar is open, 3 otherwise
                    property <int> card-cols: (root.product-detail-visible && !root.show-option-grid) || (root.recipient-detail-visible && !root.show-option-grid) ? 2 : 3;
                    property <length> card-gap: 12px;
                    // gaps: (cols+1) * gap
                    property <length> card-total-gap: (card-cols + 1) * card-gap;
                    viewport-height: ((root.cards.length + card-cols - 1) / card-cols) * (196px + 12px) + 12px;

                    // Card grid empty state — shown when no cards, no search active, and not in option grid
                    if root.cards.length == 0 && root.search-text == "" && !root.show-option-grid : Rectangle {
                        width: parent.width;
                        height: parent.height;
                        background: transparent;
                        Text {
                            width: parent.width;
                            height: parent.height;
                            horizontal-alignment: center;
                            vertical-alignment: center;
                            font-size: Typography.size-md;
                            color: Colors.text-dim;
                            wrap: word-wrap;
                            text: root.connection-status == 0
                                ? "Open Settings to connect your GitHub project"
                                : root.connection-status == 1
                                ? "Syncing..."
                                : root.connection-status == 4
                                ? "Connect Shopify to see orders"
                                : "No orders to show";
                        }
                    }

                    // Search zero-results empty state
                    if root.cards.length == 0 && root.search-text != "" && !root.show-option-grid : Rectangle {
                        width: parent.width;
                        height: 60px;
                        background: transparent;
                        Text {
                            width: parent.width;
                            horizontal-alignment: center;
                            vertical-alignment: center;
                            font-size: Typography.size-md;
                            color: Colors.text-dim;
                            text: "No recipients match your search";
                        }
                    }

                    for card-data[card-index] in root.cards : RecipientCard {
                        width: (parent.width - card-total-gap) / card-cols;
                        height: 196px;
                        x: mod(card-index, card-cols) * (self.width + card-gap) + card-gap;
                        y: floor(card-index / card-cols) * (self.height + card-gap) + card-gap;

                        recipient-name: card-data.recipient-name;
                        status-pill: card-data.status-pill;
                        status-date: card-data.status-date;
                        missing-label: card-data.missing-label;
                        stale-label: card-data.stale-label;
                        is-stale: card-data.is-stale;
                        show-refresh: card-data.show-refresh;
                        refresh-disabled: card-data.refresh-disabled;
                        refresh-label: card-data.refresh-label;
                        retry-label: card-data.retry-label;
                        show-error: card-data.show-error;
                        refresh-error: card-data.refresh-error;
                        archive-state: card-data.archive-state;
                        discord-user-id: card-data.discord-user-id;
                        contact-secondary: card-data.contact-secondary;
                        recipient-initial: card-data.recipient-initial;
                        shopify-customer-url: card-data.shopify-customer-url;
                        shopify-order-url: card-data.shopify-order-url;
                        github-issue-url: card-data.github-issue-url;
                        email: card-data.email;
                        remove-confirming: card-data.remove-confirming;
                        item-squares: card-data.item-squares;
                        item-display-label: card-data.item-display-label;
                        is-unassigned: card-data.is-unassigned;
                        unassigned-customer-name: card-data.unassigned-customer-name;
                        purpose-color: card-data.purpose-color;
                        purpose-label: card-data.purpose-label;
                        discord-username: card-data.discord-username;
                        avatar-image: card-data.avatar-image;
                        has-avatar-image: card-data.avatar-image.width > 0;
                        recipient-id: card-data.recipient-id;
                        active-mode-index: root.active-mode-index;

                        pick-recipient-clicked => {
                            root.pick-recipient-clicked(card-index);
                        }
                        add-item-clicked => {
                            root.card-add-item(card-index);
                        }
                        remove-single-item(item-sq-index) => {
                            root.card-remove-single-item(card-index, item-sq-index);
                        }
                        archive-clicked => {
                            root.card-archive(card-index);
                        }
                        unarchive-clicked => {
                            root.card-unarchive(card-index);
                        }
                        archive-now-clicked => {
                            root.card-archive-now(card-index);
                        }
                        refresh-clicked => {
                            root.card-refresh(card-index);
                        }
                        open-discord-dm => {
                            root.card-open-discord-dm(card-index);
                        }
                        copy-email => {
                            root.card-copy-email(card-index);
                        }
                        open-shopify-customer => {
                            root.card-open-shopify-customer(card-index);
                        }
                        open-shopify-order => {
                            root.card-open-shopify-order(card-index);
                        }
                        open-github-issue => {
                            root.card-open-github-issue(card-index);
                        }
                        start-return-confirmed => {
                            root.start-return-card-index = card-index;
                            root.start-return-recipient-name = card-data.recipient-name;
                            root.start-return-item-label = card-data.item-display-label;
                            root.start-return-modal-visible = true;
                        }
                        view-warning-clicked => {
                            root.card-view-warning(card-index);
                        }
                        card-name-clicked(rid) => { root.card-name-navigate(rid); }
                        card-index: card-index;
                        item-square-clicked(cidx, item-id) => {
                            root.card-item-square-clicked(cidx, item-id);
                        }
                        notes: card-data.notes;
                        on-post-note(note-text) => { root.card-post-note(card-index, note-text); }
                        on-notes-popover-opened => { root.card-notes-popover-opened(card-index); }
                    }
                }
            }

            // "N archived results hidden" message -- above chip bar, below card grid
            if root.hidden-archived-count > 0 && !root.current-show-archived : Rectangle {
                x: 60px;
                y: parent.height - 96px;
                width: parent.width - 72px;
                height: 28px;
                background: transparent;

                HorizontalLayout {
                    alignment: center;
                    spacing: 8px;
                    padding-top: 6px;

                    Text {
                        text: root.hidden-archived-count + " archived results hidden";
                        font-size: Typography.size-sm;
                        color: Colors.text-dim;
                        vertical-alignment: center;
                    }

                    Rectangle {
                        width: 44px;
                        height: 20px;
                        border-radius: 10px;
                        background: show-archived-touch.has-hover ? #3d5bcc : Colors.surface-popup;

                        Text {
                            text: "Show";
                            width: parent.width;
                            height: parent.height;
                            horizontal-alignment: center;
                            vertical-alignment: center;
                            color: Colors.accent;
                            font-size: Typography.size-xs;
                        }

                        show-archived-touch := TouchArea {
                            mouse-cursor: pointer;
                            clicked => {
                                root.toggle-show-archived();
                            }
                        }
                    }
                }
            }

            // Chip bar at bottom of card region
            ChipBar {
                x: 60px;
                y: parent.height - 60px;
                width: parent.width - 72px;
                chips: root.status-chips;
                chip-toggled(idx) => {
                    root.chip-toggled(idx);
                }
            }

            // Tab strip - declared AFTER card grid so tooltips render on top
            TabStrip {
                x: 0px;
                y: 72px;
                width: 48px;
                height: parent.height - 72px;
                active-index: root.active-mode-index;
                flash-active: root.tab-flash-active;
                tab-clicked(index) => { root.tab-clicked(index); }
            }

            // Toast overlay at bottom-center (above chip bar)
            // Widens to 300px when undo button is present
            if root.toast-visible : Rectangle {
                x: root.toast-has-undo ? (parent.width - 360px) / 2 : (root.toast-is-warning ? (parent.width - 400px) / 2 : (parent.width - 260px) / 2);
                y: parent.height - 92px;
                width: root.toast-has-undo ? 360px : (root.toast-is-warning ? 400px : 260px);
                height: 32px;
                border-radius: 16px;
                background: root.toast-is-warning ? Colors.toast-warning-bg : Colors.toast-info-bg;
                opacity: root.toast-visible ? 1.0 : 0.0;
                animate opacity { duration: 200ms; easing: ease-out; }

                if !root.toast-has-undo : Text {
                    text: root.toast-message;
                    width: parent.width;
                    height: parent.height;
                    horizontal-alignment: center;
                    vertical-alignment: center;
                    color: #ffffff;
                    font-size: Typography.size-sm;
                }

                if root.toast-has-undo : HorizontalLayout {
                    padding-left: 12px;
                    padding-right: 12px;
                    padding-top: 4px;
                    padding-bottom: 4px;
                    spacing: 8px;
                    alignment: center;

                    Text {
                        text: root.toast-message;
                        color: #ffffff;
                        font-size: Typography.size-sm;
                        vertical-alignment: center;
                        horizontal-stretch: 1;
                    }

                    Rectangle {
                        width: 46px;
                        height: 22px;
                        border-radius: 11px;
                        background: #ffffff30;

                        Text {
                            text: "Undo";
                            width: parent.width;
                            height: parent.height;
                            horizontal-alignment: center;
                            vertical-alignment: center;
                            color: #ffffff;
                            font-size: Typography.size-xs;
                        }

                        TouchArea {
                            mouse-cursor: pointer;
                            clicked => {
                                root.toast-undo();
                            }
                        }
                    }
                }
            }

            // Settings modal — declared before lookup modal so lookup modal can appear on top if both open
            // In practice only one modal is open at a time; settings has lower z-order than lookup
            settings-modal-inst := SettingsModal {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                modal-open <=> root.settings-modal-open;
                store-slug-text <=> root.settings-store-slug;
                github-project-url-text <=> root.settings-github-project-url;
                error-message <=> root.settings-error;
                shopify-token-configured <=> root.settings-shopify-token-configured;
                shopify-token-text <=> root.settings-shopify-token;
                shopify-token-changing <=> root.settings-shopify-token-changing;
                saving-in-progress <=> root.settings-saving-in-progress;
                discord-token-configured <=> root.discord-token-configured;
                discord-token-text <=> root.discord-token-text;

                save-clicked() => {
                    root.settings-save-clicked();
                }
                cancel-clicked() => {
                    root.settings-cancel-clicked();
                }
                shopify-clear-clicked() => {
                    root.settings-shopify-clear-clicked();
                }
                discord-save-token-clicked() => {
                    root.discord-save-token();
                }
                discord-clear-clicked() => {
                    root.discord-clear-token();
                }
            }

            // Lookup modal — declared before recipient picker so picker has higher z-order
            lookup-modal-inst := LookupModal {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                visible: root.lookup-modal-open;
                target-card-name: root.lookup-target-card-name;
                target-card-id: root.lookup-target-card-id;
                products: root.lookup-products;
                tree-viewport-height: root.lookup-tree-viewport-height;
                shopify-fetch-in-progress: root.shopify-fetch-in-progress;
                shopify-fetched-image-url: root.shopify-fetched-image-url;
                search-text <=> root.lookup-search-text;
                show-create-form <=> root.lookup-show-create-form;

                search-changed(query) => {
                    root.lookup-search-changed(query);
                }
                item-selected(product-id, name, hint) => {
                    root.lookup-item-selected(root.lookup-target-card-index, product-id, name, hint);
                    global-keys.focus();
                }
                unit-selected(sid, pid, cid) => {
                    root.lookup-unit-selected(sid, pid, cid);
                    global-keys.focus();
                }
                unit-force-reassign(sid, pid, cid) => {
                    root.lookup-unit-force-reassign(sid, pid, cid);
                }
                toggle-product-expanded(pid) => {
                    if lookup-modal-inst.expanded-product-id == pid {
                        lookup-modal-inst.expanded-product-id = "";
                    } else {
                        lookup-modal-inst.expanded-product-id = pid;
                    }
                }
                create-unit-confirmed(pid, sn) => {
                    root.lookup-create-unit-confirmed(pid, sn);
                }
                create-confirmed(display-name, shopify-url) => {
                    root.lookup-create-confirmed(root.lookup-target-card-index, display-name, shopify-url);
                }
                close-requested() => {
                    root.lookup-close();
                }
            }

            // Add product form — centered overlay
            if root.add-product-visible : Rectangle {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                background: #00000080;

                // Backdrop click to dismiss (declared before form so form intercepts its own clicks)
                TouchArea {
                    clicked => { root.on-add-product-discard(); }
                }

                AddProductForm {
                    x: (parent.width - 400px) / 2;
                    y: (parent.height - 380px) / 2;
                    width: 400px;
                    height: 380px;
                    name-value <=> root.add-product-name;
                    shopify-url-value <=> root.add-product-shopify-url;
                    suggestions: root.add-product-suggestions;
                    show-suggestions: root.add-product-show-suggestions;
                    has-image-preview: root.add-product-has-image;
                    image-preview: root.add-product-image-preview;
                    validation-error: root.add-product-validation-error;
                    submit-clicked => { root.on-add-product-submit(); }
                    discard-clicked => { root.on-add-product-discard(); }
                    name-changed(val) => { root.on-add-product-name-changed(val); }
                    shopify-url-changed(val) => { root.on-add-product-shopify-url-changed(val); }
                    set-image-clicked => { root.on-add-product-set-image(); }
                    suggestion-selected(url) => { root.on-add-product-suggestion-selected(url); }
                }
            }

            // State transition modal overlay — for manual unit state changes (D-18, D-19)
            if root.state-modal-visible : Rectangle {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                background: #00000080;

                // Backdrop dismiss
                TouchArea {
                    clicked => {
                        root.state-modal-visible = false;
                    }
                }

                StateTransitionModal {
                    x: (parent.width - 400px) / 2;
                    y: (parent.height - 380px) / 2;
                    width: 400px;
                    height: 380px;
                    serial-id: root.state-modal-serial-id;
                    current-state: root.state-modal-current-state;
                    product-name: root.state-modal-product-name;
                    is-assigned: root.state-modal-is-assigned;
                    show-unassign-option: root.state-modal-show-unassign;
                    state-selected(sid, new-state, unassign) => {
                        root.state-modal-visible = false;
                        root.state-modal-confirmed(sid, new-state, unassign);
                    }
                    close-requested => {
                        root.state-modal-visible = false;
                    }
                }
            }

            // Delete item confirmation modal
            if root.delete-item-modal-visible : Rectangle {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                background: #00000080;

                TouchArea {
                    clicked => { root.delete-item-modal-visible = false; }
                }

                Rectangle {
                    x: (parent.width - 340px) / 2;
                    y: (parent.height - 200px) / 2;
                    width: 340px;
                    height: 200px;
                    background: Colors.surface;
                    border-radius: 8px;
                    border-width: 1px;
                    border-color: Colors.border-default;

                    VerticalLayout {
                        padding: 16px;
                        spacing: 8px;
                        alignment: center;

                        // Product name (bold)
                        Text {
                            text: root.delete-item-product-name;
                            font-size: Typography.size-md;
                            font-weight: 700;
                            color: Colors.text-primary;
                            horizontal-alignment: center;
                        }
                        // Serial number (subtext, only for units)
                        if root.delete-item-serial-id != "" : Text {
                            text: root.delete-item-serial-id;
                            font-size: Typography.size-sm;
                            color: Colors.text-muted;
                            horizontal-alignment: center;
                        }
                        // Assigned to (only if assigned elsewhere)
                        if root.delete-item-assigned-to != "" : Text {
                            text: "Assigned to: " + root.delete-item-assigned-to;
                            font-size: Typography.size-sm;
                            color: Colors.text-secondary;
                            horizontal-alignment: center;
                        }
                        // Spacer
                        Rectangle { height: 4px; }
                        // Action text
                        Text {
                            text: root.delete-item-serial-id != ""
                                ? "Currently not assigned to " + root.delete-item-card-name + ".\n\nDelete from this card?"
                                : "Delete from " + root.delete-item-card-name + "?";
                            font-size: Typography.size-sm;
                            color: Colors.text-primary;
                            wrap: word-wrap;
                            horizontal-alignment: center;
                        }

                        HorizontalLayout {
                            spacing: 12px;
                            alignment: center;

                            Rectangle {
                                width: 80px;
                                height: 32px;
                                border-radius: 4px;
                                background: ok-touch.has-hover ? #ef5350 : #c62828;

                                ok-touch := TouchArea {
                                    mouse-cursor: pointer;
                                    clicked => {
                                        root.delete-item-modal-visible = false;
                                        root.delete-item-confirmed(root.delete-item-card-index, root.delete-item-sq-index);
                                    }
                                }
                                Text {
                                    text: "OK";
                                    width: parent.width;
                                    height: parent.height;
                                    horizontal-alignment: center;
                                    vertical-alignment: center;
                                    color: #ffffff;
                                    font-size: Typography.size-sm;
                                }
                            }

                            Rectangle {
                                width: 80px;
                                height: 32px;
                                border-radius: 4px;
                                border-width: 1px;
                                border-color: Colors.border-default;
                                background: cancel-touch.has-hover ? Colors.surface-popup : transparent;

                                cancel-touch := TouchArea {
                                    mouse-cursor: pointer;
                                    clicked => {
                                        root.delete-item-modal-visible = false;
                                    }
                                }
                                Text {
                                    text: "Cancel";
                                    width: parent.width;
                                    height: parent.height;
                                    horizontal-alignment: center;
                                    vertical-alignment: center;
                                    color: Colors.text-primary;
                                    font-size: Typography.size-sm;
                                }
                            }
                        }
                    }
                }
            }

            // Start Return confirmation modal — centered with dimming
            if root.start-return-modal-visible : Rectangle {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                background: #00000080;

                // Backdrop dismiss
                TouchArea {
                    clicked => {
                        root.start-return-modal-visible = false;
                    }
                }

                Rectangle {
                    x: (parent.width - 360px) / 2;
                    y: (parent.height - 200px) / 2;
                    width: 360px;
                    height: 200px;
                    background: Colors.surface-popup;
                    border-radius: 8px;
                    border-width: 1px;
                    border-color: Colors.border-muted;

                    // Absorb clicks on modal body
                    TouchArea {}

                    VerticalLayout {
                        padding: 20px;
                        spacing: 12px;

                        Text {
                            text: "Start Return";
                            font-size: Typography.size-lg;
                            font-weight: 600;
                            color: Colors.text-primary;
                        }

                        Text {
                            text: "Open Shopify return page for " + root.start-return-recipient-name + "?";
                            font-size: Typography.size-sm;
                            color: Colors.text-secondary;
                            wrap: word-wrap;
                        }

                        if root.start-return-item-label != "" : Text {
                            text: "Products: " + root.start-return-item-label;
                            font-size: Typography.size-xs;
                            color: Colors.text-muted;
                            wrap: word-wrap;
                        }

                        HorizontalLayout {
                            spacing: 8px;
                            alignment: end;

                            Rectangle {
                                width: 70px;
                                height: 32px;
                                border-radius: 4px;
                                border-width: 1px;
                                border-color: Colors.border-muted;
                                background: sr-cancel-touch.has-hover ? Colors.border-default : transparent;
                                Text {
                                    text: "Cancel";
                                    font-size: Typography.size-sm;
                                    color: Colors.text-primary;
                                    horizontal-alignment: center;
                                    vertical-alignment: center;
                                }
                                sr-cancel-touch := TouchArea {
                                    mouse-cursor: pointer;
                                    clicked => { root.start-return-modal-visible = false; }
                                }
                            }

                            Rectangle {
                                width: 140px;
                                height: 32px;
                                border-radius: 4px;
                                background: sr-confirm-touch.has-hover ? Colors.warning : #c07820;
                                Text {
                                    text: "Open Return Page";
                                    font-size: Typography.size-sm;
                                    color: #ffffff;
                                    horizontal-alignment: center;
                                    vertical-alignment: center;
                                }
                                sr-confirm-touch := TouchArea {
                                    mouse-cursor: pointer;
                                    clicked => {
                                        root.start-return-modal-visible = false;
                                        root.card-start-return(root.start-return-card-index);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Recipient picker modal — last declaration = highest z-order (rendered on top of all other content)
            RecipientPickerModal {
                x: 0px;
                y: 0px;
                width: parent.width;
                height: parent.height;
                is-visible: root.show-recipient-picker;
                customer-name: root.picker-customer-name;
                recipients: root.picker-recipients;
                card-index: root.picker-card-index;

                close-picker => {
                    root.show-recipient-picker = false;
                }
                assign-recipient(idx, key) => {
                    root.show-recipient-picker = false;
                    root.assign-recipient(idx, key);
                }
                create-and-assign(idx, name) => {
                    root.show-recipient-picker = false;
                    root.create-and-assign-recipient(idx, name);
                }
                picker-search-changed(query) => {
                    root.picker-search-changed(query);
                }
            }
        }
    }
}
