<?php
if (!defined('ABSPATH')) {
    exit;
}

$event_id = absint((int) ($event_id ?? 0));
$post_id = absint((int) ($post_id ?? $event_id));
$bootstrap_payload = isset($bootstrap_payload) && is_array($bootstrap_payload) ? (array) $bootstrap_payload : [];
$bootstrap_url = esc_url_raw((string) ($bootstrap_url ?? rest_url('eep/v1/event-webapp-bootstrap')));
$chat_url = esc_url_raw((string) ($chat_url ?? rest_url('eep/v1/event-webapp-chat')));
$dm_url = esc_url_raw((string) ($dm_url ?? rest_url('eep/v1/event-webapp-dm')));
$qa_url = esc_url_raw((string) ($qa_url ?? rest_url('eep/v1/event-webapp-qa')));
$polls_url = esc_url_raw((string) ($polls_url ?? rest_url('eep/v1/event-webapp-polls')));
$notifications_url = esc_url_raw((string) ($notifications_url ?? rest_url('eep/v1/event-webapp-notifications')));
$itinerary_url = esc_url_raw((string) ($itinerary_url ?? rest_url('eep/v1/event-webapp-itinerary')));
$rest_nonce = sanitize_text_field((string) ($rest_nonce ?? wp_create_nonce('wp_rest')));
$event_webapp_page_url = esc_url((string) ($event_webapp_page_url ?? ''));
$pwa_manifest_url = esc_url_raw((string) ($pwa_manifest_url ?? ''));
$pwa_service_worker_url = esc_url_raw((string) ($pwa_service_worker_url ?? ''));
$pwa_service_worker_scope = sanitize_text_field((string) ($pwa_service_worker_scope ?? '/'));
if ($pwa_service_worker_scope === '') {
    $pwa_service_worker_scope = '/';
}
$can_manage_event_webapp = !empty($can_manage_event_webapp);
$install_identity = isset($install_identity) && is_array($install_identity) ? (array) $install_identity : [];
$install_identity_id = sanitize_key((string) ($install_identity['install_id'] ?? ''));
$install_identity_token = preg_replace('/[^a-z0-9]/', '', strtolower((string) ($install_identity['install_token'] ?? '')));

$event_row = isset($bootstrap_payload['event']) && is_array($bootstrap_payload['event']) ? (array) $bootstrap_payload['event'] : [];
$event_title = sanitize_text_field((string) ($event_row['title'] ?? get_the_title($post_id)));
$event_date = sanitize_text_field((string) ($event_row['date_label'] ?? ''));
$event_subtitle = eep_sanitize_textarea((string) ($event_row['subtitle'] ?? ''));
$event_cover = esc_url((string) ($event_row['cover_image'] ?? 'https://images.unsplash.com/photo-1540575467063-178a50c2df87?auto=format&fit=crop&q=80&w=1000'));
$event_icon = esc_url((string) ($event_row['icon_url'] ?? ''));
$accent_color = sanitize_hex_color((string) ($event_row['accent_color'] ?? '#6366f1')) ?: '#6366f1';
$user_row = isset($bootstrap_payload['user']) && is_array($bootstrap_payload['user']) ? (array) $bootstrap_payload['user'] : [];
$current_user_name = sanitize_text_field((string) ($user_row['display_name'] ?? ''));
if ($current_user_name === '') {
    $current_user_name = is_user_logged_in() ? wp_get_current_user()->display_name : __('Attendee', 'nexdirectory');
}

$json_flags = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT;
if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
    $json_flags |= JSON_INVALID_UTF8_SUBSTITUTE;
}
$bootstrap_json = wp_json_encode($bootstrap_payload, $json_flags);
if (!is_string($bootstrap_json) || $bootstrap_json === '') {
    $bootstrap_json = '{}';
}

$bootstrap_script_id = 'eep-webapp-bootstrap-' . (string) max(1, $event_id > 0 ? $event_id : $post_id);
$root_id = 'eep-webapp-root-' . (string) max(1, $event_id > 0 ? $event_id : $post_id);

// Config array passed to Alpine
$app_config = [
    'eventId' => $event_id,
    'endpoints' => [
        'bootstrap' => $bootstrap_url,
        'chat' => $chat_url,
        'dm' => $dm_url,
        'qa' => $qa_url,
        'polls' => $polls_url,
        'notifications' => $notifications_url,
        'itinerary' => $itinerary_url,
    ],
    'nonce' => $rest_nonce,
    'pwa' => [
        'manifest' => $pwa_manifest_url,
        'sw' => $pwa_service_worker_url,
        'scope' => $pwa_service_worker_scope
    ],
    'install' => [
        'id' => $install_identity_id,
        'token' => is_string($install_identity_token) ? $install_identity_token : '',
    ],
    'currentUser' => $current_user_name,
    'canManage' => $can_manage_event_webapp,
];
$app_config_json = wp_json_encode($app_config, $json_flags);
?>

<div id="<?php echo esc_attr($root_id); ?>" class="fixed inset-0 z-[999999] bg-gray-100 text-slate-800 font-sans antialiased selection:bg-brand-200 selection:text-brand-900 flex justify-center sm:p-4 md:p-8 overflow-hidden" data-eep-webapp-root data-event-id="<?php echo esc_attr((string) max(1, $event_id > 0 ? $event_id : $post_id)); ?>">
    
    <script src="https://cdn.tailwindcss.com"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
    <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
    <script type="application/json" id="<?php echo esc_attr($bootstrap_script_id); ?>"><?php echo $bootstrap_json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></script>

    <script>
        tailwind.config = {
            theme: {
                extend: {
                    fontFamily: { sans: ['"Plus Jakarta Sans"', 'sans-serif'] },
                    colors: {
                        brand: {
                            50: '#f0f5ff',
                            100: '#e5edff',
                            200: '#cddbfe',
                            300: '#b4c6fc',
                            400: '#8da2fb',
                            500: '<?php echo esc_js($accent_color); ?>',
                            600: 'color-mix(in srgb, <?php echo esc_js($accent_color); ?> 85%, black)',
                            700: 'color-mix(in srgb, <?php echo esc_js($accent_color); ?> 70%, black)',
                            800: 'color-mix(in srgb, <?php echo esc_js($accent_color); ?> 55%, black)',
                            900: 'color-mix(in srgb, <?php echo esc_js($accent_color); ?> 40%, black)',
                        }
                    },
                    boxShadow: {
                        'app': '0 0 0 1px rgba(0,0,0,0.05), 0 30px 60px -12px rgba(0, 0, 0, 0.25)',
                        'glass': '0 8px 32px 0 rgba(31, 38, 135, 0.07)',
                        'float': '0 20px 40px -10px rgba(0,0,0,0.1)',
                    }
                }
            }
        }
    </script>
    <style>
        .hide-scrollbar::-webkit-scrollbar { display: none; }
        .hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
        [x-cloak] { display: none !important; }
        .pb-safe { padding-bottom: env(safe-area-inset-bottom, 24px); }
        .pt-safe { padding-top: env(safe-area-inset-top, 24px); }
        
        /* Modern Glassmorphism Utilities */
        .glass-panel {
            background: rgba(255, 255, 255, 0.75);
            backdrop-filter: blur(16px);
            -webkit-backdrop-filter: blur(16px);
            border: 1px solid rgba(255, 255, 255, 0.4);
        }
        .glass-dark {
            background: rgba(15, 23, 42, 0.6);
            backdrop-filter: blur(20px);
            -webkit-backdrop-filter: blur(20px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        #wpadminbar, html body .eep-platform-nav, html body .eep-nav-mobile-shell,
        html body [data-eep-nav-mobile-shell], html body .eep-front-sticky-nav,
        html body [data-eep-sticky-nav], html body [data-eep-mobile-nav],
        html body [data-eep-bottom-nav], html body .eep-front-mobile-dock,
        html body [data-eep-nav-root], html body .eep-platform-footer,
        html body .eep-app-page-footer, html body #colophon, html body footer.site-footer {
            display: none !important;
        }
        html, body { margin-top: 0 !important; padding-top: 0 !important; }
        html.eep-webapp-open, body.eep-webapp-open { overflow: hidden !important; height: 100% !important; background: #f1f5f9; }
    </style>

    <div x-data='appData(<?php echo $app_config_json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>)' x-init="initApp()" class="w-full max-w-[400px] bg-[#f8fafc] h-full sm:rounded-[40px] shadow-app overflow-hidden flex flex-col relative sm:border-[8px] border-slate-800 mx-auto transform transition-all">

        <div x-show="loading" x-cloak class="absolute inset-0 z-[120] flex items-center justify-center transition-opacity duration-500" :style="'background:' + (event.loadingBg || '#ffffff')">
            <div class="w-full flex flex-col items-center justify-center px-8 animate-pulse">
                <img x-show="event.loadingLogo" :src="event.loadingLogo" alt="" class="w-[40%] max-w-[160px] h-auto object-contain drop-shadow-2xl">
                <div x-show="!event.loadingLogo" class="w-[80%] text-center text-slate-800 font-extrabold tracking-tight text-3xl" x-text="event.name"></div>
                <div class="mt-8 flex gap-1">
                    <span class="w-2 h-2 rounded-full bg-brand-500 animate-bounce" style="animation-delay: 0s"></span>
                    <span class="w-2 h-2 rounded-full bg-brand-500 animate-bounce" style="animation-delay: 0.15s"></span>
                    <span class="w-2 h-2 rounded-full bg-brand-500 animate-bounce" style="animation-delay: 0.3s"></span>
                </div>
            </div>
        </div>

        <header class="z-30 shrink-0 relative transition-all duration-700 pb-4 pt-safe"
                :class="activeTab === 'home' ? 'h-52' : 'h-32'">
            <div class="absolute inset-0 bg-cover bg-center transition-all duration-700" 
                 :style="'background-image: url(' + event.cover + ');'"
                 :class="activeTab === 'home' ? 'opacity-100 scale-100' : 'opacity-20 scale-105 blur-sm'"></div>
            <div class="absolute inset-0 bg-gradient-to-b from-slate-900/60 via-slate-900/40 to-[#f8fafc]"></div>
            
            <div class="relative z-10 px-6 pt-4 h-full flex flex-col justify-between">
                <div class="flex justify-between items-center gap-2">
                    <div class="flex items-center gap-3 min-w-0">
                        <template x-if="event.headerLogo">
                            <div class="glass-panel p-1.5 rounded-xl shadow-sm">
                                <img :src="event.headerLogo" alt="" class="h-8 w-auto max-w-[120px] object-contain object-center">
                            </div>
                        </template>
                        <span x-show="event.isLiveNow" x-cloak class="glass-dark px-3 py-1.5 rounded-full text-white text-[10px] font-bold uppercase tracking-wider flex items-center gap-2 shadow-lg">
                            <span class="relative flex h-2 w-2">
                              <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
                              <span class="relative inline-flex rounded-full h-2 w-2 bg-red-500"></span>
                            </span>
                            Live
                        </span>
                    </div>
                    
                    <div class="flex items-center gap-2 shrink-0">
                        <button x-show="canInstall" @click="promptInstall()" class="glass-panel p-2.5 text-slate-800 rounded-xl hover:bg-white active:scale-90 transition-all shadow-sm" x-cloak aria-label="<?php echo esc_attr__('Install app', 'nexdirectory'); ?>">
                            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path></svg>
                        </button>
                        <button x-show="featureFlags.notifications" @click="openNotifications()" class="glass-panel relative p-2.5 text-slate-800 rounded-xl hover:bg-white active:scale-90 transition-all shadow-sm" x-cloak>
                            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg>
                            <span x-show="notifications.some(n => n.unread)" class="absolute top-1.5 right-1.5 w-2.5 h-2.5 bg-red-500 rounded-full border-2 border-white"></span>
                        </button>
                    </div>
                </div>

                <div x-show="activeTab === 'home'" x-transition:enter="transition ease-out duration-500 delay-100" x-transition:enter-start="opacity-0 translate-y-4" x-transition:enter-end="opacity-100 translate-y-0" class="pb-2">
                    <p class="text-brand-300 text-[10px] font-bold uppercase tracking-widest drop-shadow-md mb-1" x-text="event.date"></p>
                    <h1 class="text-3xl font-extrabold tracking-tight text-white drop-shadow-lg leading-tight line-clamp-2" x-text="event.name"></h1>
                </div>
            </div>
        </header>

        <div x-show="featureFlags.notifications && showNotifications" @click="showNotifications = false" class="absolute inset-0 z-40 bg-slate-900/40 backdrop-blur-sm transition-opacity" x-cloak></div>
        <div x-show="featureFlags.notifications && showNotifications" x-cloak
             x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 scale-95 translate-y-4 sm:translate-y-0 sm:translate-x-4" x-transition:enter-end="opacity-100 scale-100 translate-y-0 sm:translate-x-0"
             x-transition:leave="transition ease-in duration-200" x-transition:leave-start="opacity-100 scale-100 translate-y-0" x-transition:leave-end="opacity-0 scale-95 translate-y-4"
             class="absolute bottom-6 left-4 right-4 sm:top-[100px] sm:bottom-auto sm:left-auto sm:right-6 sm:w-[320px] bg-white rounded-3xl shadow-2xl border border-slate-100 overflow-hidden z-50 flex flex-col">
            <div class="p-5 bg-slate-50 border-b border-slate-100 flex justify-between items-center shrink-0">
                <span class="text-xs font-bold uppercase tracking-widest text-slate-800">Alerts & Updates</span>
                <button @click="showNotifications = false" class="text-slate-400 hover:text-slate-800 bg-white p-2 rounded-full shadow-sm transition-all"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M6 18L18 6M6 6l12 12"></path></svg></button>
            </div>
            <div class="max-h-[350px] overflow-y-auto p-4 hide-scrollbar space-y-3 bg-white">
                <template x-if="notifications.length === 0">
                    <div class="text-center py-8">
                        <div class="w-12 h-12 bg-slate-50 rounded-full flex items-center justify-center mx-auto mb-3 text-slate-300">
                            <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg>
                        </div>
                        <p class="text-sm font-medium text-slate-400">All caught up!</p>
                    </div>
                </template>
                <template x-for="notif in notifications" :key="notif.id">
                    <button type="button" @click="openNotification(notif)" class="w-full text-left p-4 rounded-2xl cursor-pointer transition-all duration-200" :class="notif.unread ? 'bg-brand-50 border border-brand-100 shadow-sm' : 'bg-slate-50 hover:bg-slate-100 border border-transparent'">
                        <p class="font-semibold text-sm text-slate-800 leading-snug" x-text="notif.text"></p>
                        <p class="text-[10px] font-bold uppercase tracking-widest text-slate-400 mt-2" x-text="notif.time"></p>
                    </button>
                </template>
            </div>
        </div>

        <div x-show="activeNotification" x-cloak class="absolute inset-0 z-[80] flex items-center justify-center p-6">
            <div class="absolute inset-0 bg-slate-900/60 backdrop-blur-md transition-opacity" @click="activeNotification = null"></div>
            <div class="relative w-full max-w-sm rounded-[32px] bg-white p-6 shadow-2xl transform transition-all">
                <div class="flex items-start justify-between gap-3 border-b border-slate-100 pb-4 mb-4">
                    <div class="flex items-center gap-3">
                        <div class="w-10 h-10 bg-brand-100 text-brand-600 rounded-full flex items-center justify-center"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg></div>
                        <h3 class="text-sm font-bold text-slate-800 tracking-tight" x-text="activeNotification?.time || 'Update'"></h3>
                    </div>
                    <button type="button" @click="activeNotification = null" class="p-2 rounded-full bg-slate-50 text-slate-400 hover:text-slate-800 hover:bg-slate-100 transition-colors">
                        <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M6 18L18 6M6 6l12 12"></path></svg>
                    </button>
                </div>
                <p class="text-[15px] text-slate-600 font-medium leading-relaxed whitespace-pre-wrap" x-text="activeNotification?.text || ''"></p>
                <a x-show="activeNotification?.url" :href="activeNotification?.url || '#'" target="_blank" rel="noopener noreferrer" class="mt-6 flex w-full justify-center items-center rounded-2xl bg-brand-600 px-4 py-3.5 text-xs font-bold uppercase tracking-widest text-white shadow-lg shadow-brand-500/30 hover:bg-brand-700 transition-all active:scale-95">
                    View Details
                </a>
            </div>
        </div>

        <div x-show="toast" x-cloak
             x-transition:enter="transition ease-out duration-400" x-transition:enter-start="opacity-0 -translate-y-12 scale-90" x-transition:enter-end="opacity-100 translate-y-0 scale-100"
             x-transition:leave="transition ease-in duration-300" x-transition:leave-start="opacity-100 translate-y-0 scale-100" x-transition:leave-end="opacity-0 -translate-y-8 scale-90"
             class="absolute top-8 left-4 right-4 bg-slate-800/95 backdrop-blur-xl border border-slate-700 text-white p-4 rounded-2xl shadow-float z-[150] flex items-start gap-4">
            <div class="p-2.5 rounded-xl shrink-0" :class="toast?.type === 'error' ? 'bg-red-500/20 text-red-400' : 'bg-brand-500/20 text-brand-300'">
                <template x-if="toast?.type === 'error'"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></template>
                <template x-if="toast?.type !== 'error'"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></template>
            </div>
            <div class="flex-1 pt-1">
                <h4 class="font-bold text-[13px] tracking-tight text-slate-100" x-text="toast?.title || 'Notice'"></h4>
                <p class="text-xs text-slate-300 mt-1 font-medium leading-relaxed" x-text="toast?.message"></p>
            </div>
            <button @click="toast = null" class="text-slate-400 hover:text-white p-2 rounded-xl transition-colors"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg></button>
        </div><main class="flex-1 overflow-hidden relative z-0">
            
<?php include __DIR__ . '/mobile-app/tab-home.php'; ?>
<?php include __DIR__ . '/mobile-app/tab-schedule.php'; ?>
<?php include __DIR__ . '/mobile-app/tab-pass.php'; ?>
<?php include __DIR__ . '/mobile-app/tab-settings.php'; ?>
<?php include __DIR__ . '/mobile-app/tab-chat.php'; ?>
<?php include __DIR__ . '/mobile-app/tab-map.php'; ?>
        </main><nav data-eep-webapp-bottom-nav class="absolute bottom-4 left-4 right-4 sm:bottom-6 sm:left-6 sm:right-6 bg-white/85 backdrop-blur-xl border border-white shadow-glass rounded-[28px] px-2 py-2 flex justify-between items-center z-[100] h-[72px]">
            <template x-for="item in navItems()" :key="item.id">
                <button type="button" @click="setTab(item.id)" class="flex-1 flex flex-col items-center justify-center h-full transition-all duration-300 active:scale-90 rounded-[20px] relative overflow-hidden group" :class="activeTab === item.id ? 'bg-brand-50 text-brand-600' : 'text-slate-400 hover:text-slate-600 hover:bg-slate-50'">
                    
                    <svg class="w-6 h-6 mb-1 transition-all duration-300" :class="activeTab === item.id ? '-translate-y-1 scale-110 drop-shadow-sm' : ''" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" :d="item.icon"></path>
                    </svg>
                    
                    <span class="text-[9px] font-extrabold uppercase tracking-widest transition-all duration-300 absolute bottom-1.5" :class="activeTab === item.id ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-3'" x-text="item.label"></span>
                    
                    <span class="absolute top-1.5 right-1.5 w-1.5 h-1.5 bg-brand-500 rounded-full transition-all duration-300" :class="activeTab === item.id ? 'opacity-100 scale-100' : 'opacity-0 scale-0'"></span>
                </button>
            </template>
        </nav>

<?php include __DIR__ . '/mobile-app/subviews.php'; ?>
    </div>

    <script>
        function appData(config) {
            const initialData = JSON.parse(document.getElementById('<?php echo esc_js($bootstrap_script_id); ?>').textContent || '{}');
            const dayRows = Array.isArray(initialData.days) ? initialData.days : [];
            const flatSessions = dayRows.flatMap((day) => Array.isArray(day?.items) ? day.items : []);
            const savedKeys = Array.isArray(initialData.itinerary?.saved_session_keys) ? initialData.itinerary.saved_session_keys : [];
            const savedNotes = (initialData.itinerary && typeof initialData.itinerary.notes === 'object' && initialData.itinerary.notes) ? initialData.itinerary.notes : {};
            const safeMessages = Array.isArray(initialData.chat?.messages) ? initialData.chat.messages.map((messageRow) => ({
                id: messageRow.id,
                user: messageRow.user_name,
                text: messageRow.body,
                time: messageRow.created_label,
                isSystem: messageRow.user_id === 0,
                isMe: messageRow.is_mine
            })) : [];
            const settings = (initialData && typeof initialData.settings === 'object' && initialData.settings) ? initialData.settings : {};
            const featureFlags = {
                chat: settings.enable_chat !== false,
                qa: settings.enable_qa !== false,
                polls: settings.enable_polls !== false,
                map: settings.enable_map !== false,
                notifications: settings.enable_notifications !== false,
                attendees: settings.enable_attendees !== false,
                customItinerary: settings.enable_custom_itinerary !== false,
            };

            return {
                config,
                loading: true,
                activeTab: 'home',
                activeSubView: null,
                selectedSession: null,
                showNotifications: false,
                activeNotification: null,
                scheduleFilter: 'all',
                chatInput: '',
                qaInput: '',
                pollInputQuestion: '',
                pollInputOptions: '',
                pollInputMultiple: false,
                sessionTab: 'details',
                feedbackStars: [1,2,3,4,5],
                rating: 0,
                feedbackSubmitted: false,
                toast: null,
                activeUser: null,
                dmInput: '',
                isSubmitting: false,
                deferredInstallPrompt: null,
                canInstall: false,
                popStateHandler: null,
                upNextLabel: 'Up Next',
                featureFlags,
                installIdentity: {
                    id: config?.install?.id ? String(config.install.id) : '',
                    token: config?.install?.token ? String(config.install.token).replace(/[^a-z0-9]/gi, '').toLowerCase() : '',
                },

                event: {
                    id: Number(initialData.event?.id || config?.eventId || 0),
                    name: initialData.event?.title || 'Event',
                    date: initialData.event?.date_label || '',
                    startRaw: initialData.event?.start_raw || '',
                    endRaw: initialData.event?.end_raw || '',
                    isLiveNow: !!initialData.event?.is_live_now,
                    cover: initialData.event?.cover_image || '',
                    user: initialData.user?.display_name || config.currentUser || 'Attendee',
                    subtitle: initialData.event?.subtitle || '',
                    permalink: initialData.event?.permalink || '',
                    headerLogo: initialData.event?.header_logo_url || initialData.event?.icon_url || '',
                    loadingLogo: initialData.event?.splash_url || initialData.event?.icon_url || '',
                    loadingBg: initialData.event?.loading_bg_color || '#ffffff',
                },
                user: {
                    id: Number(initialData.user?.id || 0),
                    name: initialData.user?.display_name || config.currentUser || 'Attendee',
                    loggedIn: !!initialData.user?.logged_in,
                    canManage: !!initialData.user?.can_manage,
                },
                notifications: Array.isArray(initialData.notifications) ? initialData.notifications.map((notificationRow) => ({
                    ...notificationRow,
                    unread: typeof notificationRow?.unread === 'boolean' ? notificationRow.unread : true
                })) : [],
                sessions: flatSessions.map((sessionRow, index) => ({
                    id: String(sessionRow?.id || index + 1),
                    time: sessionRow?.time || '',
                    start: sessionRow?.start || '',
                    end: sessionRow?.end || '',
                    title: sessionRow?.title || `Session ${index + 1}`,
                    speaker: sessionRow?.speaker || '',
                    location: sessionRow?.location || '',
                    desc: ''
                })),
                savedSessions: savedKeys.map((savedKey) => String(savedKey || '')).filter((savedKey) => savedKey !== ''),
                itineraryNotes: (savedNotes && typeof savedNotes === 'object') ? { ...savedNotes } : {},
                attendees: [],
                attendeeSearch: '',
                networkingMatches: [],
                messages: safeMessages,
                dmMessages: [],
                activeDmThreadId: 0,
                qaList: Array.isArray(initialData.qa?.items) ? initialData.qa.items : [],
                pollsList: Array.isArray(initialData.polls?.items) ? initialData.polls.items : [],

                normalizeAttendees(rawRows) {
                    const rows = Array.isArray(rawRows) ? rawRows : [];
                    const seen = new Set();
                    return rows
                        .map((attendeeRow) => {
                            const numericId = Number(attendeeRow?.id || 0);
                            if (!Number.isFinite(numericId) || numericId <= 0) {
                                return null;
                            }
                            const id = String(numericId);
                            if (seen.has(id)) {
                                return null;
                            }
                            seen.add(id);
                            const name = String(attendeeRow?.name || '').trim() || 'Member';
                            const statusRaw = String(attendeeRow?.status || '').trim();
                            const statusLabel = String(attendeeRow?.status_label || '').trim() || (statusRaw ? statusRaw : 'Active');
                            return {
                                id,
                                name,
                                avatar: String(attendeeRow?.avatar || '').trim(),
                                status: statusRaw || 'active',
                                status_label: statusLabel,
                                online: !!attendeeRow?.online || statusRaw === 'online'
                            };
                        })
                        .filter(Boolean);
                },

                filteredAttendees() {
                    const query = String(this.attendeeSearch || '').trim().toLowerCase();
                    let rows = Array.isArray(this.attendees) ? [...this.attendees] : [];
                    if (this.user?.id) {
                        rows = rows.filter((attendeeRow) => Number(attendeeRow?.id || 0) !== Number(this.user.id));
                    }
                    if (!query) {
                        return rows;
                    }
                    return rows.filter((attendeeRow) => {
                        const haystack = `${attendeeRow?.name || ''} ${attendeeRow?.status_label || ''} ${attendeeRow?.status || ''}`.toLowerCase();
                        return haystack.includes(query);
                    });
                },

                syncNetworkingMatches() {
                    const existingState = {};
                    (Array.isArray(this.networkingMatches) ? this.networkingMatches : []).forEach((entry) => {
                        const entryId = String(entry?.id || '');
                        if (entryId) {
                            existingState[entryId] = !!entry?.connected;
                        }
                    });
                    const sourceRows = (Array.isArray(this.attendees) ? this.attendees : [])
                        .filter((attendeeRow) => !this.user?.id || Number(attendeeRow?.id || 0) !== Number(this.user.id));
                    const nextMatches = sourceRows
                        .slice(0, 6)
                        .map((attendeeRow) => ({
                            id: String(attendeeRow.id || ''),
                            name: String(attendeeRow.name || 'Member'),
                            avatar: String(attendeeRow.avatar || ''),
                            role: String(attendeeRow.status_label || attendeeRow.status || 'Event attendee'),
                            status: String(attendeeRow.status || 'active'),
                            status_label: String(attendeeRow.status_label || attendeeRow.status || 'Active'),
                            online: !!attendeeRow.online,
                            connected: !!existingState[String(attendeeRow.id || '')]
                        }))
                        .filter((matchRow) => matchRow.id !== '');
                    this.networkingMatches = nextMatches;
                },

                normalizeDmMessages(rawRows) {
                    const rows = Array.isArray(rawRows) ? rawRows : [];
                    return rows
                        .map((messageRow) => {
                            const messageId = String(messageRow?.id || '');
                            if (!messageId) {
                                return null;
                            }
                            return {
                                id: messageId,
                                sender_id: Number(messageRow?.sender_id || 0),
                                recipient_id: Number(messageRow?.recipient_id || 0),
                                text: String(messageRow?.text || ''),
                                time: String(messageRow?.time || ''),
                                created_label: String(messageRow?.created_label || ''),
                                is_mine: !!messageRow?.is_mine
                            };
                        })
                        .filter(Boolean);
                },

                navItems() {
                    const items = [
                        { id: 'home', label: 'Home', icon: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6' },
                        { id: 'schedule', label: 'Agenda', icon: 'M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z' },
                        { id: 'pass', label: 'Pass', icon: 'M4 7h16a2 2 0 012 2v2h-2a2 2 0 100 4h2v2a2 2 0 01-2 2H4a2 2 0 01-2-2v-2h2a2 2 0 100-4H2V9a2 2 0 012-2z' },
                    ];
                    if (this.featureFlags.chat) {
                        items.push({ id: 'chat', label: 'Chat', icon: 'M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.214-3.64A7.952 7.952 0 013 12c0-4.418 4.03-8 9-8s9 3.582 9 8z' });
                    }
                    if (this.featureFlags.map) {
                        items.push({ id: 'map', label: 'Map', icon: 'M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0zM15 11a3 3 0 11-6 0 3 3 0 016 0z' });
                    }
                    items.push({ id: 'settings', label: 'Settings', icon: 'M10.325 4.317a1 1 0 011.35-.936l.647.282a1 1 0 00.856 0l.647-.282a1 1 0 011.35.936v.695a1 1 0 00.466.84l.59.37a1 1 0 01.207 1.53l-.454.545a1 1 0 000 1.28l.454.545a1 1 0 01-.207 1.53l-.59.37a1 1 0 00-.466.84v.695a1 1 0 01-1.35.936l-.647-.282a1 1 0 00-.856 0l-.647.282a1 1 0 01-1.35-.936v-.695a1 1 0 00-.466-.84l-.59-.37a1 1 0 01-.207-1.53l.454-.545a1 1 0 000-1.28l-.454-.545a1 1 0 01.207-1.53l.59-.37a1 1 0 00.466-.84v-.695zM12 15a3 3 0 100-6 3 3 0 000 6z' });
                    return items;
                },

                ensureActiveTab() {
                    const availableTabs = this.navItems().map((item) => item.id);
                    if (!availableTabs.includes(this.activeTab)) {
                        this.activeTab = availableTabs[0] || 'home';
                    }
                },

                setTab(tabId) {
                    const targetTab = String(tabId || '').trim();
                    if (!targetTab) {
                        return;
                    }
                    const availableTabs = this.navItems().map((item) => item.id);
                    if (!availableTabs.includes(targetTab)) {
                        return;
                    }
                    this.activeSubView = null;
                    this.selectedSession = null;
                    this.activeUser = null;
                    this.showNotifications = false;
                    this.activeNotification = null;
                    this.activeTab = targetTab;
                    if (targetTab === 'home') {
                        this.$nextTick(() => this.updateUpNextLabelFromScroll());
                    }
                },

                openSubView(subViewId) {
                    const viewId = String(subViewId || '').trim();
                    if (!viewId) {
                        return;
                    }
                    if (viewId === 'attendees' || viewId === 'networking') {
                        this.syncNetworkingMatches();
                    }
                    this.activeSubView = viewId;
                },

                openNotifications() {
                    if (!this.featureFlags.notifications) {
                        return;
                    }
                    this.showNotifications = true;
                },

                openNotification(notificationRow) {
                    if (!notificationRow || typeof notificationRow !== 'object') {
                        return;
                    }
                    const targetId = String(notificationRow.id || '');
                    this.notifications = this.notifications.map((row) => {
                        if (String(row.id || '') === targetId) {
                            return { ...row, unread: false };
                        }
                        return row;
                    });
                    this.activeNotification = { ...notificationRow, unread: false };
                    this.showNotifications = false;
                },

                closeNotification() {
                    this.activeNotification = null;
                },

                passPayload() {
                    const payload = {
                        event_id: this.event.id || Number(config.eventId || 0),
                        event_name: this.event.name || '',
                        user_id: this.user.id || 0,
                        attendee: this.user.name || '',
                        issued_at: new Date().toISOString()
                    };
                    return JSON.stringify(payload);
                },

                passQrUrl() {
                    const encodedPayload = encodeURIComponent(this.passPayload());
                    return 'https://api.qrserver.com/v1/create-qr-code/?size=320x320&data=' + encodedPayload;
                },

                sessionStartValue(sessionRow) {
                    if (!sessionRow || typeof sessionRow !== 'object') {
                        return Number.MAX_SAFE_INTEGER;
                    }
                    if (sessionRow.start) {
                        const parsedStart = Date.parse(String(sessionRow.start));
                        if (Number.isFinite(parsedStart)) {
                            return parsedStart;
                        }
                    }
                    const fromTimeText = Date.parse(String(sessionRow.time || ''));
                    if (Number.isFinite(fromTimeText)) {
                        return fromTimeText;
                    }
                    return Number.MAX_SAFE_INTEGER;
                },

                upNextSessions() {
                    if (!Array.isArray(this.sessions) || this.sessions.length === 0) {
                        return [];
                    }
                    const now = Date.now();
                    const sorted = [...this.sessions].sort((left, right) => {
                        const leftStart = this.sessionStartValue(left);
                        const rightStart = this.sessionStartValue(right);
                        if (leftStart !== rightStart) {
                            return leftStart - rightStart;
                        }
                        return String(left.id || '').localeCompare(String(right.id || ''));
                    });
                    let startIndex = sorted.findIndex((sessionRow) => this.sessionStartValue(sessionRow) >= (now - (5 * 60 * 1000)));
                    if (startIndex < 0) {
                        startIndex = 0;
                    }
                    return sorted.slice(startIndex, startIndex + 6);
                },

                updateUpNextLabelFromScroll() {
                    const scroller = this.$refs && this.$refs.upNextScroller ? this.$refs.upNextScroller : null;
                    if (!scroller) {
                        this.upNextLabel = 'Up Next';
                        return;
                    }
                    const left = Number(scroller.scrollLeft || 0);
                    this.upNextLabel = left > 28 ? 'Up Later' : 'Up Next';
                },

                displayedSessions() {
                    if (!this.featureFlags.customItinerary) {
                        return this.sessions;
                    }
                    return this.scheduleFilter === 'all'
                        ? this.sessions
                        : this.sessions.filter((sessionRow) => this.savedSessions.includes(String(sessionRow.id || '')));
                },

                hideHostChrome() {
                    try {
                        document.documentElement.classList.add('eep-webapp-open');
                        document.body.classList.add('eep-webapp-open');
                    } catch (_err) {}
                    const chromeSelectors = [
                        '#wpadminbar',
                        '.eep-platform-nav',
                        '.eep-nav-mobile-shell',
                        '[data-eep-nav-mobile-shell]',
                        '.eep-front-sticky-nav',
                        '[data-eep-sticky-nav]',
                        '[data-eep-mobile-nav]',
                        '[data-eep-bottom-nav]',
                        '[data-eep-nav-root]',
                        '.eep-platform-footer',
                        '.eep-app-page-footer',
                        '.eep-front-mobile-dock',
                        '#colophon',
                        'footer.site-footer'
                    ];
                    chromeSelectors.forEach((selector) => {
                        document.querySelectorAll(selector).forEach((node) => {
                            if (node && node.style) {
                                node.style.setProperty('display', 'none', 'important');
                            }
                        });
                    });
                },

                promptInstall() {
                    if (this.deferredInstallPrompt) {
                        const promptEvent = this.deferredInstallPrompt;
                        this.deferredInstallPrompt = null;
                        promptEvent.prompt();
                        promptEvent.userChoice
                            .then(() => {
                                this.canInstall = false;
                            })
                            .catch(() => {
                                this.showToast('Install prompt could not be completed.', 'error');
                            });
                        return;
                    }
                    this.showToast('Use your browser menu and tap Add to Home Screen.', 'info');
                },

                handleBackNavigation() {
                    if (this.activeNotification) {
                        this.activeNotification = null;
                        return true;
                    }
                    if (this.showNotifications) {
                        this.showNotifications = false;
                        return true;
                    }
                    if (this.activeSubView === 'dm') {
                        this.activeSubView = 'attendees';
                        this.activeUser = null;
                        return true;
                    }
                    if (this.activeSubView !== null) {
                        this.closeSubView();
                        return true;
                    }
                    if (this.activeTab !== 'home') {
                        this.setTab('home');
                        return true;
                    }
                    return false;
                },

                handleBrowserBack() {
                    if (this.handleBackNavigation()) {
                        window.history.pushState({ eepWebappGuard: true, eventId: config.eventId }, document.title, window.location.href);
                        return;
                    }
                    const shouldExit = window.confirm('Are you sure you want to exit this app?');
                    if (!shouldExit) {
                        window.history.pushState({ eepWebappGuard: true, eventId: config.eventId }, document.title, window.location.href);
                        return;
                    }
                    if (this.popStateHandler) {
                        window.removeEventListener('popstate', this.popStateHandler);
                        this.popStateHandler = null;
                    }
                    window.history.back();
                },

                initApp() {
                    const rootEl = this.$root ? this.$root.closest('[data-eep-webapp-root]') : null;
                    const eventId = String(config.eventId || '');
                    if (eventId !== '') {
                        const roots = Array.from(document.querySelectorAll('[data-eep-webapp-root]'))
                            .filter((node) => String(node.getAttribute('data-event-id') || '') === eventId);
                        if (roots.length > 1) {
                            const primaryRoot = roots[0];
                            if (rootEl && rootEl !== primaryRoot) {
                                rootEl.remove();
                                return;
                            }
                            roots.slice(1).forEach((node) => {
                                if (node !== primaryRoot) {
                                    node.remove();
                                }
                            });
                        }
                    }

                    const storageKey = eventId ? ('eep-webapp-install-' + eventId) : '';
                    if (storageKey) {
                        if (this.installIdentity.id && this.installIdentity.token) {
                            try {
                                window.localStorage.setItem(storageKey, JSON.stringify({
                                    id: this.installIdentity.id,
                                    token: this.installIdentity.token
                                }));
                            } catch (_err) {}
                        } else {
                            try {
                                const storedRaw = window.localStorage.getItem(storageKey);
                                const stored = storedRaw ? JSON.parse(storedRaw) : null;
                                const storedId = stored && stored.id ? String(stored.id).replace(/[^a-z0-9_-]/gi, '').toLowerCase() : '';
                                const storedToken = stored && stored.token ? String(stored.token).replace(/[^a-z0-9]/gi, '').toLowerCase() : '';
                                if (storedId && storedToken) {
                                    this.installIdentity = { id: storedId, token: storedToken };
                                }
                            } catch (_err) {}
                        }
                    }

                    if (config.pwa && config.pwa.manifest) {
                        const manifestEvent = String(config.eventId || '');
                        const existingManifest = Array.from(document.querySelectorAll('link[rel="manifest"]'))
                            .find((node) => String(node.getAttribute('data-eep-manifest-event') || '') === manifestEvent);
                        if (!existingManifest) {
                            const manifestLink = document.createElement('link');
                            manifestLink.rel = 'manifest';
                            manifestLink.href = config.pwa.manifest;
                            manifestLink.setAttribute('data-eep-manifest-event', manifestEvent);
                            document.head.appendChild(manifestLink);
                        }
                    }

                    if ('serviceWorker' in navigator && config.pwa.sw) {
                        navigator.serviceWorker.register(config.pwa.sw, { scope: config.pwa.scope || '/' })
                            .catch((error) => console.warn('SW Register failed', error));
                    }

                    window.addEventListener('beforeinstallprompt', (event) => {
                        event.preventDefault();
                        this.deferredInstallPrompt = event;
                        this.canInstall = true;
                    });

                    if ((window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone) {
                        this.canInstall = false;
                    }

                    this.attendees = this.normalizeAttendees(initialData.attendees);
                    this.syncNetworkingMatches();

                    if (!this.featureFlags.customItinerary) {
                        this.scheduleFilter = 'all';
                    }

                    this.ensureActiveTab();
                    this.hideHostChrome();

                    this.$nextTick(() => {
                        const navNodes = this.$root ? this.$root.querySelectorAll('[data-eep-webapp-bottom-nav]') : [];
                        if (navNodes && navNodes.length > 1) {
                            navNodes.forEach((node, index) => {
                                if (index > 0) {
                                    node.remove();
                                }
                            });
                        }
                    });

                    if (window.history && typeof window.history.pushState === 'function') {
                        this.popStateHandler = () => this.handleBrowserBack();
                        window.history.pushState({ eepWebappGuard: true, eventId: config.eventId }, document.title, window.location.href);
                        window.addEventListener('popstate', this.popStateHandler);
                    }

                    window.setTimeout(() => {
                        this.loading = false;
                    }, 500);

                    setTimeout(() => {
                        if (this.$refs.chatEnd) {
                            this.$refs.chatEnd.scrollIntoView();
                        }
                    }, 100);
                    this.$nextTick(() => this.updateUpNextLabelFromScroll());
                },

                apiCall(url, payload) {
                    const requestUrl = String(url || '');

                    const requestHeaders = {
                        'Content-Type': 'application/json',
                        'X-WP-Nonce': config.nonce
                    };
                    if (this.installIdentity.id && this.installIdentity.token) {
                        requestHeaders['X-Eep-Webapp-Install-Id'] = this.installIdentity.id;
                        requestHeaders['X-Eep-Webapp-Install-Token'] = this.installIdentity.token;
                    }

                    return fetch(requestUrl, {
                        method: 'POST',
                        headers: requestHeaders,
                        body: JSON.stringify({ event_id: config.eventId, ...payload })
                    }).then((response) => response.json()).then((data) => {
                        if (data && data.code && data.message) {
                            throw new Error(data.message);
                        }
                        return data;
                    });
                },

                toggleSave(sessionId) {
                    if (!this.featureFlags.customItinerary) {
                        this.showToast('Custom itinerary is disabled for this event.');
                        return;
                    }
                    if (!this.user?.loggedIn) {
                        this.showToast('Please log in to save your custom itinerary.', 'error');
                        return;
                    }
                    const sessionKey = String(sessionId || '').trim();
                    if (!sessionKey) {
                        return;
                    }
                    const previousSaved = Array.isArray(this.savedSessions) ? [...this.savedSessions] : [];
                    if (this.savedSessions.includes(sessionKey)) {
                        this.savedSessions = this.savedSessions.filter((rowId) => String(rowId || '') !== sessionKey);
                    } else {
                        this.savedSessions = [...this.savedSessions, sessionKey];
                    }
                    this.savedSessions = Array.from(new Set(this.savedSessions.map((rowId) => String(rowId || '')).filter((rowId) => rowId !== '')));

                    this.apiCall(config.endpoints.itinerary, {
                        action: 'save',
                        saved_session_keys: this.savedSessions,
                        notes: this.itineraryNotes || {}
                    })
                        .then((response) => {
                            if (Array.isArray(response?.saved_session_keys)) {
                                this.savedSessions = response.saved_session_keys.map((rowId) => String(rowId || '')).filter((rowId) => rowId !== '');
                            }
                            if (response?.notes && typeof response.notes === 'object') {
                                this.itineraryNotes = { ...response.notes };
                            }
                        })
                        .catch((error) => {
                            this.savedSessions = previousSaved;
                            this.showToast(error?.message || 'Could not update your itinerary.', 'error');
                        });
                },

                openSession(sessionRow) {
                    if (!sessionRow) {
                        return;
                    }
                    this.selectedSession = sessionRow;
                    this.sessionTab = 'details';
                    this.rating = 0;
                    this.feedbackSubmitted = false;
                    this.activeSubView = 'session';
                },

                openDM(userRow) {
                    if (!this.user?.id) {
                        this.showToast('Please log in to message attendees.', 'error');
                        return;
                    }
                    if (!config?.endpoints?.dm) {
                        this.showToast('Direct messaging is not available right now.', 'error');
                        return;
                    }
                    const targetId = String(userRow?.id || '').trim();
                    if (!targetId) {
                        return;
                    }
                    if (Number(targetId) === Number(this.user.id || 0)) {
                        this.showToast('You cannot message yourself.', 'error');
                        return;
                    }

                    const attendeeRow = (Array.isArray(this.attendees) ? this.attendees : []).find((entry) => String(entry?.id || '') === targetId);
                    this.activeUser = attendeeRow ? { ...attendeeRow } : {
                        id: targetId,
                        name: String(userRow?.name || 'Member'),
                        avatar: String(userRow?.avatar || ''),
                        status: String(userRow?.status || 'active'),
                        status_label: String(userRow?.status_label || userRow?.role || 'Active'),
                        online: !!userRow?.online
                    };
                    this.activeDmThreadId = 0;
                    this.dmMessages = [];
                    this.activeSubView = 'dm';
                    this.isSubmitting = true;
                    this.apiCall(config.endpoints.dm, { action: 'thread', recipient_id: targetId })
                        .then((response) => {
                            this.activeDmThreadId = Number(response?.thread_id || 0);
                            if (response?.recipient && typeof response.recipient === 'object') {
                                this.activeUser = {
                                    ...this.activeUser,
                                    id: String(response.recipient.id || targetId),
                                    name: String(response.recipient.name || this.activeUser.name || 'Member'),
                                    avatar: String(response.recipient.avatar || this.activeUser.avatar || '')
                                };
                            }
                            this.dmMessages = this.normalizeDmMessages(response?.messages);
                            setTimeout(() => {
                                if (this.$refs.dmEnd) {
                                    this.$refs.dmEnd.scrollIntoView({ behavior: 'smooth' });
                                }
                            }, 50);
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not load conversation.', 'error'))
                        .finally(() => {
                            this.isSubmitting = false;
                        });
                },

                closeSubView() {
                    this.activeSubView = null;
                    setTimeout(() => {
                        this.selectedSession = null;
                        this.activeUser = null;
                    }, 200);
                },

                sendMessage() {
                    if (!this.featureFlags.chat) {
                        return;
                    }
                    const messageText = String(this.chatInput || '').trim();
                    if (!messageText) {
                        return;
                    }
                    this.isSubmitting = true;
                    this.apiCall(config.endpoints.chat, { message: messageText })
                        .then((response) => {
                            if (Array.isArray(response?.messages)) {
                                this.messages = response.messages.map((messageRow) => ({
                                    id: messageRow.id,
                                    user: messageRow.user_name,
                                    text: messageRow.body,
                                    time: messageRow.created_label,
                                    isSystem: messageRow.user_id === 0,
                                    isMe: messageRow.is_mine
                                }));
                            }
                            this.chatInput = '';
                            setTimeout(() => {
                                if (this.$refs.chatEnd) {
                                    this.$refs.chatEnd.scrollIntoView({ behavior: 'smooth' });
                                }
                            }, 50);
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not send message.', 'error'))
                        .finally(() => {
                            this.isSubmitting = false;
                        });
                },

                sendDM() {
                    const messageText = String(this.dmInput || '').trim();
                    if (!messageText || !this.activeUser) {
                        return;
                    }
                    if (!config?.endpoints?.dm) {
                        this.showToast('Direct messaging is not available right now.', 'error');
                        return;
                    }
                    this.isSubmitting = true;
                    this.apiCall(config.endpoints.dm, {
                        action: 'send',
                        recipient_id: this.activeUser.id,
                        message: messageText
                    })
                        .then((response) => {
                            this.activeDmThreadId = Number(response?.thread_id || this.activeDmThreadId || 0);
                            if (response?.recipient && typeof response.recipient === 'object') {
                                this.activeUser = {
                                    ...this.activeUser,
                                    id: String(response.recipient.id || this.activeUser.id),
                                    name: String(response.recipient.name || this.activeUser.name || 'Member'),
                                    avatar: String(response.recipient.avatar || this.activeUser.avatar || '')
                                };
                            }
                            this.dmMessages = this.normalizeDmMessages(response?.messages);
                            this.dmInput = '';
                            setTimeout(() => {
                                if (this.$refs.dmEnd) {
                                    this.$refs.dmEnd.scrollIntoView({ behavior: 'smooth' });
                                }
                            }, 50);
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not send message.', 'error'))
                        .finally(() => {
                            this.isSubmitting = false;
                        });
                },

                submitQA() {
                    if (!this.featureFlags.qa) {
                        return;
                    }
                    const questionText = String(this.qaInput || '').trim();
                    if (!questionText) {
                        return;
                    }
                    this.isSubmitting = true;
                    this.apiCall(config.endpoints.qa, { action: 'ask', question: questionText })
                        .then((response) => {
                            if (Array.isArray(response?.items)) {
                                this.qaList = response.items;
                            }
                            this.qaInput = '';
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not post question.', 'error'))
                        .finally(() => {
                            this.isSubmitting = false;
                        });
                },

                toggleUpvote(questionId) {
                    if (!this.featureFlags.qa) {
                        return;
                    }
                    this.apiCall(config.endpoints.qa, { action: 'vote', question_id: questionId })
                        .then((response) => {
                            if (Array.isArray(response?.items)) {
                                this.qaList = response.items;
                            }
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not update vote.', 'error'));
                },

                submitPollVote(pollId, optionId) {
                    if (!this.featureFlags.polls) {
                        return;
                    }
                    if (!this.user.id) {
                        this.showToast('Please log in to vote in polls.', 'error');
                        return;
                    }
                    const poll = Array.isArray(this.pollsList)
                        ? this.pollsList.find((entry) => String(entry?.id || '') === String(pollId || ''))
                        : null;
                    if (!poll || String(optionId || '') === '') {
                        return;
                    }

                    const selected = Array.isArray(poll.selected_option_ids)
                        ? poll.selected_option_ids.map((entry) => String(entry || '')).filter((entry) => entry !== '')
                        : [];
                    const targetOptionId = String(optionId || '');
                    let nextSelected = [];
                    if (poll.multiple) {
                        const hasOption = selected.includes(targetOptionId);
                        if (hasOption && selected.length > 1) {
                            nextSelected = selected.filter((entry) => entry !== targetOptionId);
                        } else if (hasOption) {
                            nextSelected = selected;
                        } else {
                            nextSelected = [...selected, targetOptionId];
                        }
                    } else {
                        nextSelected = [targetOptionId];
                    }

                    this.apiCall(config.endpoints.polls, { action: 'vote', poll_id: pollId, option_ids: nextSelected })
                        .then((response) => {
                            if (Array.isArray(response?.items)) {
                                this.pollsList = response.items;
                            }
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not submit poll vote.', 'error'));
                },

                createPollFromApp() {
                    if (!this.featureFlags.polls || !this.user.canManage) {
                        return;
                    }
                    const question = String(this.pollInputQuestion || '').trim();
                    const options = String(this.pollInputOptions || '')
                        .split(/\r\n|\r|\n|,/)
                        .map((entry) => String(entry || '').trim())
                        .filter((entry) => entry !== '');
                    if (!question || options.length < 2) {
                        this.showToast('Add a question and at least two options.', 'error');
                        return;
                    }
                    this.isSubmitting = true;
                    this.apiCall(config.endpoints.polls, {
                        action: 'create',
                        question,
                        options,
                        multiple: this.pollInputMultiple ? 1 : 0
                    })
                        .then((response) => {
                            if (Array.isArray(response?.items)) {
                                this.pollsList = response.items;
                            }
                            this.pollInputQuestion = '';
                            this.pollInputOptions = '';
                            this.pollInputMultiple = false;
                            this.showToast('Poll created.');
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not create poll.', 'error'))
                        .finally(() => {
                            this.isSubmitting = false;
                        });
                },

                managePoll(action, pollId) {
                    if (!this.user.canManage || !this.featureFlags.polls) {
                        return;
                    }
                    const normalizedAction = String(action || '').trim();
                    const normalizedPollId = String(pollId || '').trim();
                    if (!normalizedAction || !normalizedPollId) {
                        return;
                    }
                    this.apiCall(config.endpoints.polls, { action: normalizedAction, poll_id: normalizedPollId })
                        .then((response) => {
                            if (Array.isArray(response?.items)) {
                                this.pollsList = response.items;
                            }
                        })
                        .catch((error) => this.showToast(error?.message || 'Could not update poll.', 'error'));
                },

                connectMatch(matchId) {
                    const match = this.networkingMatches.find((entry) => entry.id === matchId);
                    if (match) {
                        match.connected = true;
                    }
                },

                showToast(message, type = 'info') {
                    this.toast = {
                        title: type === 'error' ? 'Error' : 'Notice',
                        message: String(message || ''),
                        type
                    };
                    setTimeout(() => {
                        this.toast = null;
                    }, 4000);
                }
            };
        }
    </script>
</div>
