/* ===========================================================================
 * SaleOs AI assistant — frontend layer ("دستیار SaleOs", Phase 1)
 * ---------------------------------------------------------------------------
 * Attaches to the existing global `app` object (same pattern as the inline
 * sheet/filter helpers) so app.js stays untouched. Owns:
 *   - the global AI drawer (ask / suggest / draft)
 *   - the in-context panels: customer brief, deal risk, activity draft,
 *     KPI explain, operation-rule draft
 *   - feedback, and the admin enable/disable control
 *   - feature-flag + RBAC aware visibility for every AI surface
 *
 * Security: this layer only ever sends IDs / filters / user-typed text to the
 * backend. It never sends raw DB rows to the model — the backend builds the
 * scoped, minimised context.
 *
 * Feature flag: the backend strips every `ai.*` / `menu.ai_assistant`
 * capability from rbac_current_permissions when the feature is off — for every
 * role, admin included. Because admins bypass client capability checks, the
 * presence of any AI capability in the payload is the only trustworthy "is the
 * feature on" signal, so that is what `isAiFeatureAvailable()` reads. Every
 * surface requires it, which is how "off => nobody, not even admin, sees it"
 * holds on the client.
 * ========================================================================= */
(function () {
    'use strict';

    function whenReady(cb) {
        if (typeof app === 'undefined') { return window.setTimeout(function () { whenReady(cb); }, 30); }
        cb(app);
    }

    whenReady(function (app) {
        if (app.__aiInstalled) { return; }
        app.__aiInstalled = true;

        /* ---- small utilities --------------------------------------------- */
        function esc(s) {
            return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
                return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
            });
        }
        function fmt(t) { return esc(t).replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>'); }
        function byId(id) { return document.getElementById(id); }
        function visible(id) { var e = byId(id); return !!(e && !e.classList.contains('hidden')); }
        function toast(msg, icon) {
            if (typeof Swal === 'undefined') { return; }
            Swal.fire({ toast: true, position: 'top-end', timer: 2200, timerProgressBar: true, showConfirmButton: false, icon: icon || 'success', title: msg });
        }
        function copyText(text) {
            text = String(text || '');
            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(text).then(function () { toast('کپی شد'); }, function () { legacyCopy(text); });
            } else { legacyCopy(text); }
        }
        function legacyCopy(text) {
            try {
                var ta = document.createElement('textarea');
                ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
                document.body.appendChild(ta); ta.focus(); ta.select();
                document.execCommand('copy'); document.body.removeChild(ta);
                toast('کپی شد');
            } catch (e) { toast('کپی نشد', 'error'); }
        }
        // first defined value across top-level payload, backend `result`, and optional wrappers
        function field(res, names, dflt) {
            if (!res) { return dflt; }
            var scopes = [res];
            if (res.result && typeof res.result === 'object') { scopes.push(res.result); }
            if (res.data && typeof res.data === 'object') {
                scopes.push(res.data);
                if (res.data.result && typeof res.data.result === 'object') { scopes.push(res.data.result); }
            }
            for (var s = 0; s < scopes.length; s++) {
                for (var i = 0; i < names.length; i++) {
                    if (scopes[s][names[i]] !== undefined && scopes[s][names[i]] !== null) { return scopes[s][names[i]]; }
                }
            }
            return dflt;
        }
        function arr(v) { return Array.isArray(v) ? v : (v ? [v] : []); }
        function setBusy(btn, busy, busyHtml) {
            if (!btn) { return; }
            if (busy) {
                if (!btn.dataset.aiIdle) { btn.dataset.aiIdle = btn.innerHTML; }
                btn.disabled = true;
                btn.innerHTML = busyHtml || '<i class="fa-solid fa-spinner fa-spin"></i> در حال پردازش…';
            } else {
                btn.disabled = false;
                if (btn.dataset.aiIdle) { btn.innerHTML = btn.dataset.aiIdle; delete btn.dataset.aiIdle; }
            }
        }

        /* ---- shared copy ------------------------------------------------- */
        var DISCLAIMER = '<div class="ai-disclaimer"><i class="fa-solid fa-triangle-exclamation"></i><span>هوش مصنوعی ممکن است خطا کند؛ پیش از ثبت، نتیجه را بررسی کنید.</span></div>';
        var STATUS_FA = { new: 'جدید', followup: 'در حال پیگیری', active_deal: 'معامله فعال', payment: 'پرداخت', support: 'پشتیبانی', lost: 'از دست رفته', won: 'موفق', unknown: 'نامشخص' };
        var RISK_FA = { low: 'کم', medium: 'متوسط', high: 'زیاد' };
        var ACTION_ICON = { call: 'fa-phone', message: 'fa-comment', activity: 'fa-list-check', reminder: 'fa-bell', review_deal: 'fa-handshake', none: 'fa-circle-info' };
        var ACTION_FA = { call: 'تماس', message: 'پیام', activity: 'فعالیت', reminder: 'یادآوری', review_deal: 'بررسی معامله', none: 'بدون اقدام' };

        /* ---- shared render fragments ------------------------------------- */
        function riskBadge(level) {
            var k = String(level || '').toLowerCase();
            var cls = (k === 'low' || k === 'medium' || k === 'high') ? k : 'neutral';
            return '<span class="ai-badge ai-badge--' + cls + '">ریسک ' + esc(RISK_FA[k] || 'نامشخص') + '</span>';
        }
        function statusBadge(status) {
            var k = String(status || 'unknown').toLowerCase();
            return '<span class="ai-badge ai-badge--neutral">' + esc(STATUS_FA[k] || k) + '</span>';
        }
        function sourceChips(refs) {
            refs = arr(refs);
            if (!refs.length) { return ''; }
            var inner = refs.map(function (r) {
                var label = (r && typeof r === 'object') ? (r.label || r.title || r.type || r.ref || r.id || '') : r;
                if (label === '' || label == null) { return ''; }
                return '<span class="ai-source-chip"><i class="fa-solid fa-link"></i>' + esc(label) + '</span>';
            }).join('');
            return inner ? '<div class="ai-sources">' + inner + '</div>' : '';
        }
        function listBlock(label, items, warn) {
            items = arr(items);
            if (!items.length) { return ''; }
            var li = items.map(function (it) {
                var t = (it && typeof it === 'object') ? (it.text || it.title || it.reason || it.step || it.label || JSON.stringify(it)) : it;
                return '<div class="ai-list__item">' + esc(t) + '</div>';
            }).join('');
            return '<div><div class="ai-result__section-label">' + esc(label) + '</div><div class="ai-list' + (warn ? ' ai-list--warn' : '') + '">' + li + '</div></div>';
        }
        function nextActionBlock(na) {
            if (!na || (!na.title && !na.type)) { return ''; }
            var k = String(na.type || 'none').toLowerCase();
            var icon = ACTION_ICON[k] || 'fa-circle-info';
            return '<div><div class="ai-result__section-label">گام بعدی پیشنهادی</div>' +
                '<div class="ai-next"><span class="ai-next__icon"><i class="fa-solid ' + icon + '"></i></span>' +
                '<div><div class="ai-next__title">' + esc(na.title || ACTION_FA[k] || '') + '</div>' +
                (na.reason ? '<div class="ai-next__reason">' + esc(na.reason) + '</div>' : '') + '</div></div></div>';
        }
        function feedbackRow(requestId) {
            if (!requestId) { return ''; }
            return '<div class="ai-feedback"><span>مفید بود؟</span><div class="ai-feedback__btns">' +
                '<button type="button" class="ai-feedback__btn is-up" data-ai-fb="' + esc(requestId) + '" data-ai-rating="1" aria-label="مفید بود"><i class="fa-solid fa-thumbs-up"></i></button>' +
                '<button type="button" class="ai-feedback__btn is-down" data-ai-fb="' + esc(requestId) + '" data-ai-rating="0" aria-label="مفید نبود"><i class="fa-solid fa-thumbs-down"></i></button>' +
                '</div></div>';
        }
        function errorBlock(message, retryMethod) {
            return '<div class="ai-error"><i class="fa-solid fa-circle-exclamation"></i><div>' +
                '<div>' + esc(message || 'خطا در دریافت پاسخ هوش مصنوعی.') + '</div>' +
                (retryMethod ? '<button type="button" onclick="' + retryMethod + '">تلاش دوباره</button>' : '') +
                '</div></div>';
        }
        function skeleton(lines) {
            var n = lines || 3, out = '<div class="ai-skel ai-skel--block"></div>';
            for (var i = 0; i < n; i++) {
                out += '<div class="ai-skel ai-skel--line ai-skel--w' + (i % 3 === 0 ? '90' : (i % 3 === 1 ? '70' : '50')) + '"></div>';
            }
            return out;
        }

        /* =================================================================
         * Feature-flag + capability gating
         * ================================================================= */
        function hasExplicitAiCapability(capabilityKey) {
            var perms = app.state.rbacCurrentPermissions;
            var caps = perms && perms.capabilities;
            if (!Array.isArray(caps) || app.state.rbacCurrentPermissionsLoaded !== true) { return false; }
            return caps.indexOf(capabilityKey) !== -1;
        }
        app.isAiFeatureAvailable = function () {
            var perms = app.state.rbacCurrentPermissions;
            var caps = perms && perms.capabilities;
            if (!Array.isArray(caps) || app.state.rbacCurrentPermissionsLoaded !== true) { return false; }
            if (perms.ai_feature_enabled !== undefined && String(perms.ai_feature_enabled) !== '1') { return false; }
            if (app.state.ai && app.state.ai.settingsLoaded === true && app.state.ai.featureEnabled === false) { return false; }
            for (var i = 0; i < caps.length; i++) {
                var c = String(caps[i] || '');
                if (c === 'menu.ai_assistant' || c.indexOf('ai.') === 0) { return true; }
            }
            return false;
        };
        app.canAi = function (capabilityKey) {
            return app.isAiFeatureAvailable() && hasExplicitAiCapability(capabilityKey);
        };
        app.applyAiVisibility = function () {
            var available = app.isAiFeatureAvailable();
            var nodes = document.querySelectorAll('[data-ai-surface]');
            for (var i = 0; i < nodes.length; i++) {
                var el = nodes[i];
                var cap = el.getAttribute('data-ai-cap') || '';
                var ok = available && (cap === '' || hasExplicitAiCapability(cap));
                el.classList.toggle('hidden', !ok);
                if ('disabled' in el) { el.disabled = !ok; }
            }
            if (!available && app.state.ai && app.state.ai.drawerOpen) { app.closeAiDrawer(); }
        };

        /* =================================================================
         * State
         * ================================================================= */
        app.state.ai = Object.assign({
            drawerOpen: false,
            activeMode: 'ask',
            activeContext: null,
            loading: false,
            lastRequestId: null,
            lastResponse: null,
            error: null,
            messages: [],
            brief: null,
            risk: null,
            activityDraft: null,
            opRule: null,
            featureEnabled: false,
            settingsLoaded: false,
            _opener: null
        }, app.state.ai || {});

        /* =================================================================
         * Context detection
         * ================================================================= */
        function leadName() {
            var n = byId('prof-name');
            if (n && n.textContent && n.textContent.trim() && n.textContent.trim() !== 'نام مشتری') { return n.textContent.trim(); }
            var l = app.state.currentLead;
            if (l) { return ((l.first_name || '') + ' ' + (l.last_name || '')).trim() || (l.mobile_phone || ''); }
            return '';
        }
        function dealId() {
            var d = app.state.currentDeal || {};
            return String(d.id || d.didar_deal_id || d.deal_id || '');
        }
        app.computeAiContext = function () {
            if (visible('modalProfile')) {
                return { key: 'lead', label: 'پروفایل مشتری', entityType: 'contact', entityId: String(window.currentContactId || ''), entityLabel: leadName() };
            }
            if (visible('modalDeal')) {
                var d = app.state.currentDeal || {};
                return { key: 'deal', label: 'معامله', entityType: 'deal', entityId: dealId(), entityLabel: d.title || d.deal_title || '' };
            }
            var view = (typeof app.getCurrentView === 'function') ? app.getCurrentView() : 'dashboard';
            var map = {
                'performance-dashboard': { key: 'kpi', label: 'داشبورد عملکرد' },
                'kpi-dashboard': { key: 'kpi', label: 'داشبورد KPI' },
                'operation-manager': { key: 'operation_manager', label: 'مدیریت عملیات' },
                'leads': { key: 'leads', label: 'لیدها' },
                'deals': { key: 'deals', label: 'معاملات' },
                'dashboard': { key: 'dashboard', label: 'داشبورد' }
            };
            var m = map[view] || { key: view || 'dashboard', label: 'داشبورد' };
            return { key: m.key, label: m.label, entityType: '', entityId: '', entityLabel: '' };
        };
        function refreshContextChip() {
            var ctx = app.state.ai.activeContext || app.computeAiContext();
            var label = byId('ai-context-label'); var ent = byId('ai-context-entity');
            if (label) { label.textContent = ctx.label || 'داشبورد'; }
            if (ent) { ent.textContent = ctx.entityLabel ? ('· ' + ctx.entityLabel) : ''; }
        }

        /* =================================================================
         * Drawer
         * ================================================================= */
        var PLACEHOLDER = { ask: 'سؤال خود را بنویسید…', suggest: 'بپرسید برای این صفحه چه پیشنهادی دارید…', draft: 'بنویسید چه متنی لازم دارید…' };

        app.openAiDrawer = function (source) {
            if (!app.isAiFeatureAvailable()) { return; }
            app.state.ai._opener = document.activeElement;
            app.state.ai.activeContext = app.computeAiContext();
            refreshContextChip();
            var overlay = byId('ai-drawer-overlay'); var drawer = byId('ai-drawer');
            if (!drawer) { return; }
            overlay.classList.add('active');
            // reflow so the transform transition runs from the off-screen state
            void drawer.offsetHeight;
            drawer.classList.add('active');
            drawer.setAttribute('aria-hidden', 'false');
            app.state.ai.drawerOpen = true;
            renderDrawer();
            var input = byId('ai-prompt-input');
            if (input) { input.placeholder = PLACEHOLDER[app.state.ai.activeMode] || PLACEHOLDER.ask; }
            window.setTimeout(function () { if (input && app.canAi('ai.chat')) { input.focus(); } }, 320);
            if (app.haptic) { try { app.haptic(); } catch (e) { } }
        };
        app.closeAiDrawer = function () {
            var overlay = byId('ai-drawer-overlay'); var drawer = byId('ai-drawer');
            if (overlay) { overlay.classList.remove('active'); }
            if (drawer) { drawer.classList.remove('active'); drawer.setAttribute('aria-hidden', 'true'); drawer.style.transform = ''; }
            app.state.ai.drawerOpen = false;
            var opener = app.state.ai._opener;
            if (opener && typeof opener.focus === 'function') { try { opener.focus(); } catch (e) { } }
        };
        app.setAiMode = function (mode) {
            app.state.ai.activeMode = mode;
            var tabs = document.querySelectorAll('.ai-mode-tab');
            for (var i = 0; i < tabs.length; i++) { tabs[i].classList.toggle('is-active', tabs[i].getAttribute('data-ai-mode') === mode); }
            var input = byId('ai-prompt-input');
            if (input) { input.placeholder = PLACEHOLDER[mode] || PLACEHOLDER.ask; }
            renderDrawer();
        };

        function suggestionChips(mode, ctx) {
            var chips = [];
            if (ctx.key === 'lead') { chips.push({ t: 'برای این مشتری خلاصه بده', k: 'chat' }); }
            if (ctx.key === 'deal') { chips.push({ t: 'برای این معامله پیگیری بنویس', k: 'chat' }); }
            if (ctx.key === 'kpi') { chips.push({ t: 'فروش این ماه را توضیح بده', k: 'kpi' }); }
            chips.push({ t: 'امروز کدام لیدها نیاز به پیگیری دارند؟', k: 'chat' });
            chips.push({ t: 'معاملات پرریسک من را نشان بده', k: 'chat' });
            if (mode === 'draft') {
                chips = [];
                if (ctx.key === 'deal') { chips.push({ t: 'برای این معامله پیگیری بنویس', k: 'chat' }); }
                chips.push({ t: 'یک پیام پیگیری کوتاه بنویس', k: 'chat' });
                chips.push({ t: 'یک یادداشت فعالیت بنویس', k: 'chat' });
            }
            return chips.map(function (c) {
                return '<button type="button" class="ai-chip" data-ai-chip data-ai-chip-kind="' + c.k + '"><i class="fa-solid fa-arrow-turn-up"></i>' + esc(c.t) + '</button>';
            }).join('');
        }
        function emptyState(mode, ctx) {
            var titles = { ask: 'چطور می‌توانم کمک کنم؟', suggest: 'پیشنهادهای هوشمند', draft: 'کمک در نوشتن' };
            var texts = {
                ask: 'هر سؤالی درباره لیدها، معاملات یا عملکرد دارید بپرسید. پاسخ‌ها فقط بر اساس داده‌های مجاز شما ساخته می‌شوند.',
                suggest: 'بر اساس صفحه‌ای که در آن هستید، چند پیشنهاد آماده داریم.',
                draft: 'متن یادداشت فعالیت یا پیام پیگیری را با کمک دستیار بنویسید.'
            };
            return '<div class="ai-empty">' +
                '<span class="ai-mark ai-mark--lg"><i class="fa-solid fa-comment-dots"></i></span>' +
                '<div class="ai-empty__title">' + esc(titles[mode] || titles.ask) + '</div>' +
                '<div class="ai-empty__text">' + esc(texts[mode] || texts.ask) + '</div>' +
                '<div class="ai-chips" style="margin-top:16px;justify-content:center;">' + suggestionChips(mode, ctx) + '</div>' +
                '</div>';
        }
        function renderMessage(m) {
            if (m.role === 'user') { return '<div class="ai-msg ai-msg--user">' + esc(m.text) + '</div>'; }
            var inner = m.html ? ('<div class="ai-msg ai-msg--ai" style="max-width:100%;">' + m.html + '</div>') : ('<div class="ai-msg ai-msg--ai">' + fmt(m.text) + '</div>');
            if (m.sources) { inner += sourceChips(m.sources); }
            if (m.requestId) { inner += feedbackRow(m.requestId); }
            return '<div>' + inner + '</div>';
        }
        function typingBubble() { return '<div class="ai-msg ai-msg--ai" style="padding:0;"><div class="ai-typing"><span></span><span></span><span></span></div></div>'; }

        function renderDrawer() {
            var body = byId('ai-drawer-body'); if (!body) { return; }
            var ai = app.state.ai;
            var ctx = ai.activeContext || app.computeAiContext();
            var html = '';
            if (!app.canAi('ai.chat') && ai.messages.length === 0) {
                html = '<div class="ai-empty"><span class="ai-mark ai-mark--lg"><i class="fa-solid fa-lock"></i></span>' +
                    '<div class="ai-empty__title">گفتگو در دسترس شما نیست</div>' +
                    '<div class="ai-empty__text">برای استفاده از پرسش و پاسخ، به مجوز «گفتگو با هوش مصنوعی» نیاز دارید. برای دریافت دسترسی با مدیر سیستم هماهنگ کنید.</div></div>';
                body.innerHTML = html;
                return;
            }
            if (ai.messages.length === 0) {
                html = emptyState(ai.activeMode, ctx);
            } else {
                ai.messages.forEach(function (m) { html += renderMessage(m); });
                if (ai.loading) { html += typingBubble(); }
                html += '<div style="text-align:center;margin-top:4px;"><button type="button" class="ai-action" onclick="app.clearAiChat()"><i class="fa-solid fa-eraser"></i> پاک‌کردن گفتگو</button></div>';
            }
            body.innerHTML = html;
            body.scrollTop = body.scrollHeight;
        }
        app.clearAiChat = function () { app.state.ai.messages = []; renderDrawer(); };

        app.autoGrowAiInput = function (el) {
            if (!el) { return; }
            el.style.height = 'auto';
            el.style.height = Math.min(el.scrollHeight, 120) + 'px';
        };
        app.onAiInputKey = function (e) {
            if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); app.sendAiChat(); }
        };

        app.sendAiChat = function () {
            var input = byId('ai-prompt-input');
            var text = input ? input.value.trim() : '';
            if (!text) { return; }
            if (!app.canAi('ai.chat')) { toast('دسترسی به گفتگو ندارید', 'error'); return; }
            var ai = app.state.ai;
            var ctx = ai.activeContext || app.computeAiContext();
            ai.messages.push({ role: 'user', text: text });
            if (input) { input.value = ''; app.autoGrowAiInput(input); }
            ai.loading = true;
            var sendBtn = byId('ai-send-btn');
            if (sendBtn) { sendBtn.disabled = true; sendBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>'; }
            renderDrawer();
            app.req('ai_chat', { message: text, screen: ctx.key, entity_type: ctx.entityType || '', entity_id: ctx.entityId || '' }).then(function (res) {
                ai.loading = false;
                if (sendBtn) { sendBtn.disabled = false; sendBtn.innerHTML = '<i class="fa-solid fa-arrow-up"></i>'; }
                if (res && res.status === 'success') {
                    var reply = field(res, ['reply', 'answer', 'text', 'output', 'content', 'response'], res.message || '');
                    var rid = field(res, ['ai_request_id', 'request_id', 'id'], null);
                    ai.lastRequestId = rid;
                    ai.messages.push({ role: 'ai', text: reply || 'پاسخی دریافت نشد.', sources: field(res, ['source_refs', 'sources'], null), requestId: rid });
                } else {
                    ai.messages.push({ role: 'ai', html: errorBlock((res && res.message) || 'خطا در دریافت پاسخ.', null) });
                }
                renderDrawer();
            });
        };

        /* =================================================================
         * KPI explain (surfaced through the drawer)
         * ================================================================= */
        function renderKpiResult(res) {
            var summary = field(res, ['headline', 'executive_summary', 'summary'], '');
            var drivers = field(res, ['drivers'], []);
            var anomalies = field(res, ['anomalies'], []);
            var recs = field(res, ['recommendations', 'recommended_actions'], []);
            var sources = field(res, ['source_refs', 'sources'], ['KPI', 'payments', 'deals', 'activities']);
            var html = '<div class="ai-result">';
            html += '<div class="ai-result__summary"><strong>توضیح هوشمند</strong><br>' + fmt(summary || 'توضیحی در دسترس نیست.') + '</div>';
            html += listBlock('محرک‌های اصلی', drivers);
            html += listBlock('نکات غیرعادی', anomalies, true);
            html += listBlock('اقدامات پیشنهادی', recs);
            html += '</div>';
            return { html: html, sources: sources, requestId: field(res, ['ai_request_id', 'request_id', 'id'], null) };
        }
        app.explainKpiWithAi = function () {
            if (!app.canAi('ai.kpi_explain')) { toast('دسترسی به توضیح هوشمند ندارید', 'error'); return; }
            if (!app.state.ai.drawerOpen) { app.openAiDrawer('kpi'); }
            var ai = app.state.ai;
            var ctx = ai.activeContext || app.computeAiContext();
            ai.activeContext = ctx;
            ai.messages.push({ role: 'user', text: 'توضیح هوشمند: ' + (ctx.label || 'داشبورد') });
            ai.loading = true; renderDrawer();
            app.req('ai_kpi_explain', { section: ctx.key, view: (typeof app.getCurrentView === 'function' ? app.getCurrentView() : '') }).then(function (res) {
                ai.loading = false;
                if (res && res.status === 'success') {
                    var r = renderKpiResult(res);
                    ai.messages.push({ role: 'ai', html: r.html, sources: r.sources, requestId: r.requestId });
                } else {
                    ai.messages.push({ role: 'ai', html: errorBlock((res && res.message) || 'توضیح هوشمند در دسترس نیست.', null) });
                }
                renderDrawer();
            });
        };

        /* =================================================================
         * Customer brief (modalProfile)
         * ================================================================= */
        function resetBriefPanel() {
            app.state.ai.brief = null;
            var body = byId('ai-brief-body'); if (body) { body.classList.add('hidden'); body.innerHTML = ''; }
            var cta = byId('ai-brief-cta'); if (cta) { setBusy(cta, false); cta.style.display = ''; }
        }
        app.loadAiCustomerBrief = function () {
            if (!app.canAi('ai.customer_brief')) { return; }
            var contactId = window.currentContactId;
            if (!contactId) { toast('مشتری انتخاب نشده است', 'error'); return; }
            var body = byId('ai-brief-body'); var cta = byId('ai-brief-cta');
            if (!body) { return; }
            body.classList.remove('hidden');
            body.innerHTML = skeleton(3);
            setBusy(cta, true, '<i class="fa-solid fa-spinner fa-spin"></i> در حال تحلیل…');
            app.req('ai_customer_brief', { contact_id: contactId }).then(function (res) {
                setBusy(cta, false);
                if (res && res.status === 'success') {
                    if (cta) { cta.style.display = 'none'; }
                    renderBrief(res, String(contactId));
                } else {
                    body.innerHTML = errorBlock((res && res.message) || 'خلاصه هوشمند در دسترس نیست.', 'app.loadAiCustomerBrief()');
                }
            });
        };
        function renderBrief(res, contactId) {
            var summary = field(res, ['summary'], '');
            var status = field(res, ['status'], 'unknown');
            var risk = field(res, ['risk_level'], '');
            var reasons = field(res, ['risk_reasons', 'risks', 'signals'], []);
            var actions = field(res, ['next_actions'], []);
            var na = field(res, ['next_action'], null) || (Array.isArray(actions) && actions.length ? actions[0] : null);
            var draft = field(res, ['draft_activity_note', 'draft_note', 'draft_message'], '');
            var sources = field(res, ['source_refs', 'sources'], null);
            var rid = field(res, ['ai_request_id', 'request_id', 'id'], null);
            app.state.ai.brief = { note: draft, summary: summary, requestId: rid, contactId: contactId };
            var html = '<div class="ai-result">';
            html += '<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">' + statusBadge(status) + (risk ? riskBadge(risk) : '') + '</div>';
            if (summary) { html += '<div class="ai-result__summary">' + fmt(summary) + '</div>'; }
            html += listBlock('دلایل ریسک', reasons, true);
            html += nextActionBlock(na);
            if (draft) {
                html += '<div><div class="ai-result__section-label">پیش‌نویس یادداشت فعالیت</div><div class="ai-draft">' + esc(draft) + '</div></div>';
            }
            html += '<div class="ai-actions">' +
                '<button type="button" class="ai-action" onclick="app.aiBriefCopy()"><i class="fa-solid fa-copy"></i> کپی خلاصه</button>' +
                (draft ? '<button type="button" class="ai-action ai-action--primary" onclick="app.aiBriefUseInActivity()"><i class="fa-solid fa-list-check"></i> استفاده در فعالیت</button>' : '') +
                '<button type="button" class="ai-action" onclick="app.aiBriefCreateReminder()"><i class="fa-solid fa-bell"></i> ساخت یادآوری</button>' +
                '</div>';
            html += sourceChips(sources);
            html += DISCLAIMER;
            html += feedbackRow(rid);
            html += '</div>';
            var body = byId('ai-brief-body');
            body.classList.remove('hidden');
            body.innerHTML = html;
        }
        app.aiBriefCopy = function () { var b = app.state.ai.brief; if (b) { copyText(b.summary || ''); } };
        app.aiBriefUseInActivity = function () {
            var b = app.state.ai.brief; if (!b) { return; }
            app.openModal('modalActivity');
            window.setTimeout(function () {
                var note = byId('activity-result-note');
                if (note) { note.value = b.note || ''; note.dispatchEvent(new Event('input', { bubbles: true })); }
            }, 140);
            if (b.requestId) { app.sendAiFeedback(b.requestId, 1, null, true); }
        };
        app.aiBriefCreateReminder = function () {
            var b = app.state.ai.brief; if (!b) { return; }
            app.openModal('modalActivity');
            window.setTimeout(function () {
                var note = byId('activity-result-note');
                if (note && b.note) { note.value = b.note; note.dispatchEvent(new Event('input', { bubbles: true })); }
                var sched = byId('activity-is-scheduled');
                if (sched && !sched.checked) { sched.checked = true; if (typeof app.toggleActivityScheduleFields === 'function') { app.toggleActivityScheduleFields(); } }
                var rem = byId('activity-reminder-enabled');
                if (rem && !rem.checked) { rem.checked = true; if (typeof app.toggleActivityReminderFields === 'function') { app.toggleActivityReminderFields(); } }
            }, 160);
        };

        /* =================================================================
         * Deal risk (modalDeal)
         * ================================================================= */
        function resetRiskPanel() {
            app.state.ai.risk = null;
            var body = byId('ai-risk-body'); if (body) { body.classList.add('hidden'); body.innerHTML = ''; }
            var cta = byId('ai-risk-cta'); if (cta) { setBusy(cta, false); cta.style.display = ''; }
        }
        app.loadAiDealRisk = function () {
            if (!app.canAi('ai.deal_risk')) { return; }
            var id = dealId();
            if (!id) { toast('این معامله هنوز ذخیره نشده است', 'error'); return; }
            var body = byId('ai-risk-body'); var cta = byId('ai-risk-cta');
            if (!body) { return; }
            body.classList.remove('hidden');
            body.innerHTML = skeleton(2);
            setBusy(cta, true, '<i class="fa-solid fa-spinner fa-spin"></i> در حال تحلیل…');
            app.req('ai_deal_risk', { deal_id: id }).then(function (res) {
                setBusy(cta, false);
                if (res && res.status === 'success') {
                    if (cta) { cta.style.display = 'none'; }
                    renderRisk(res, id);
                } else {
                    body.innerHTML = errorBlock((res && res.message) || 'تحلیل ریسک در دسترس نیست.', 'app.loadAiDealRisk()');
                }
            });
        };
        function renderRisk(res, id) {
            var risk = field(res, ['risk_level'], '');
            var score = field(res, ['risk_score'], null);
            var reasons = field(res, ['reasons'], []);
            var missing = field(res, ['missing_data', 'blockers'], []);
            var steps = field(res, ['recommended_next_steps', 'next_steps', 'recovery_plan'], []);
            var draft = field(res, ['draft_followup_note', 'draft_note', 'next_activity'], '');
            var sources = field(res, ['source_refs', 'sources'], null);
            var rid = field(res, ['ai_request_id', 'request_id', 'id'], null);
            app.state.ai.risk = { note: draft, requestId: rid, dealId: id };
            var html = '<div class="ai-result">';
            html += '<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">' + riskBadge(risk) +
                (score !== null && score !== '' ? '<span class="ai-badge ai-badge--score">امتیاز ریسک: ' + esc(score) + '</span>' : '') + '</div>';
            html += listBlock('دلایل', reasons, true);
            html += listBlock('داده‌های ناقص', missing, true);
            html += listBlock('گام‌های بعدی پیشنهادی', steps);
            if (draft) { html += '<div><div class="ai-result__section-label">پیش‌نویس پیگیری</div><div class="ai-draft">' + esc(draft) + '</div></div>'; }
            html += '<div class="ai-actions">' +
                (draft ? '<button type="button" class="ai-action ai-action--primary" onclick="app.aiRiskWriteFollowup()"><i class="fa-solid fa-pen"></i> نوشتن پیگیری</button>' : '') +
                '<button type="button" class="ai-action" onclick="app.aiRiskCreateReminder()"><i class="fa-solid fa-bell"></i> ساخت یادآوری</button>' +
                '</div>';
            html += sourceChips(sources);
            html += DISCLAIMER;
            html += feedbackRow(rid);
            html += '</div>';
            var body = byId('ai-risk-body');
            body.classList.remove('hidden');
            body.innerHTML = html;
        }
        function openActivityForCurrentDeal(noteText, withReminder) {
            if (typeof app.startActivityForDeal === 'function') { app.startActivityForDeal(); }
            else { app.openModal('modalActivity'); }
            window.setTimeout(function () {
                var note = byId('activity-result-note');
                if (note && noteText) { note.value = noteText; note.dispatchEvent(new Event('input', { bubbles: true })); }
                if (withReminder) {
                    var sched = byId('activity-is-scheduled');
                    if (sched && !sched.checked) { sched.checked = true; if (typeof app.toggleActivityScheduleFields === 'function') { app.toggleActivityScheduleFields(); } }
                    var rem = byId('activity-reminder-enabled');
                    if (rem && !rem.checked) { rem.checked = true; if (typeof app.toggleActivityReminderFields === 'function') { app.toggleActivityReminderFields(); } }
                }
            }, 180);
        }
        app.aiRiskWriteFollowup = function () {
            var r = app.state.ai.risk; if (!r) { return; }
            openActivityForCurrentDeal(r.note || '', false);
            if (r.requestId) { app.sendAiFeedback(r.requestId, 1, null, true); }
        };
        app.aiRiskCreateReminder = function () {
            var r = app.state.ai.risk; if (!r) { return; }
            openActivityForCurrentDeal(r.note || '', true);
        };

        /* =================================================================
         * Activity draft (modalActivity)
         * ================================================================= */
        function resetActivityDraft() {
            app.state.ai.activityDraft = null;
            var res = byId('ai-activity-draft-result'); if (res) { res.classList.add('hidden'); res.innerHTML = ''; }
            var cta = byId('ai-activity-draft-cta'); if (cta) { setBusy(cta, false); }
        }
        app.draftAiActivity = function () {
            if (!app.canAi('ai.activity_draft')) { return; }
            var contactId = window.currentContactId || (byId('activity-contact-id') ? byId('activity-contact-id').value : '');
            if (!contactId) { toast('مشتری انتخاب نشده است', 'error'); return; }
            var dealSel = byId('activity-deal-select');
            var dId = (dealSel && dealSel.value) ? dealSel.value : (byId('activity-deal-id') ? byId('activity-deal-id').value : '');
            var typeSel = byId('activity-type-select');
            var activityType = typeSel ? typeSel.value : '';
            var noteEl = byId('activity-result-note');
            var intent = noteEl ? noteEl.value.trim() : '';
            var box = byId('ai-activity-draft-result'); var cta = byId('ai-activity-draft-cta');
            if (box) { box.classList.remove('hidden'); box.innerHTML = '<div class="ai-skel ai-skel--block"></div>'; }
            setBusy(cta, true, '<i class="fa-solid fa-spinner fa-spin"></i> در حال نوشتن…');
            app.req('ai_activity_draft', { contact_id: contactId, deal_id: dId, activity_type: activityType, intent: intent }).then(function (res) {
                setBusy(cta, false);
                if (res && res.status === 'success') { renderActivityDraft(res); }
                else if (box) { box.innerHTML = errorBlock((res && res.message) || 'پیشنهاد متن در دسترس نیست.', 'app.draftAiActivity()'); }
            });
        };
        function renderActivityDraft(res) {
            var note = field(res, ['activity_note', 'draft_note', 'note', 'text'], '');
            var followup = field(res, ['followup_title', 'follow_up_title', 'followup', 'title', 'follow_up_question'], '');
            var reminder = field(res, ['reminder_suggestion', 'reminder', 'due_suggestion'], '');
            var rid = field(res, ['ai_request_id', 'request_id', 'id'], null);
            app.state.ai.activityDraft = { note: note, requestId: rid };
            var html = '<div class="ai-panel" style="border-radius:12px;">';
            html += '<div class="ai-panel__body" style="padding:12px;">';
            html += '<div class="ai-draft">' + esc(note || 'متنی تولید نشد.') + '</div>';
            if (followup) { html += '<div style="margin-top:8px;font-size:11.5px;color:var(--ai-fg-muted);"><strong style="color:var(--ai-fg);">عنوان پیگیری:</strong> ' + esc(followup) + '</div>'; }
            if (reminder) { html += '<div style="margin-top:4px;font-size:11.5px;color:var(--ai-fg-muted);"><strong style="color:var(--ai-fg);">یادآوری پیشنهادی:</strong> ' + esc(reminder) + '</div>'; }
            html += '<div class="ai-actions" style="margin-top:10px;">' +
                '<button type="button" class="ai-action ai-action--primary" onclick="app.aiActivityUseText()"><i class="fa-solid fa-check"></i> استفاده از متن</button>' +
                '<button type="button" class="ai-action" onclick="app.draftAiActivity()"><i class="fa-solid fa-rotate"></i> پیشنهاد دیگر</button>' +
                '</div>';
            html += DISCLAIMER;
            html += feedbackRow(rid);
            html += '</div></div>';
            var box = byId('ai-activity-draft-result');
            box.classList.remove('hidden');
            box.innerHTML = html;
        }
        app.aiActivityUseText = function () {
            var d = app.state.ai.activityDraft; if (!d) { return; }
            var note = byId('activity-result-note');
            if (note) { note.value = d.note || ''; note.dispatchEvent(new Event('input', { bubbles: true })); note.focus(); }
            var box = byId('ai-activity-draft-result');
            if (box) { box.innerHTML = '<div class="ai-feedback__done" style="font-size:11.5px;"><i class="fa-solid fa-check"></i> متن در یادداشت فعالیت قرار گرفت.</div>'; }
            if (d.requestId) { app.sendAiFeedback(d.requestId, 1, null, true); }
            toast('متن وارد شد');
        };

        /* =================================================================
         * Operation-rule draft (view-operation-manager)
         * ================================================================= */
        app.draftOperationRuleWithAi = function () {
            if (!app.canAi('ai.operation_rule_draft')) { return; }
            var input = byId('ai-oprule-input');
            var instruction = input ? input.value.trim() : '';
            if (!instruction) { toast('لطفاً خواسته خود را بنویسید', 'warning'); return; }
            var box = byId('ai-oprule-result'); var cta = byId('ai-oprule-cta');
            if (box) { box.classList.remove('hidden'); box.innerHTML = skeleton(2); }
            setBusy(cta, true, '<i class="fa-solid fa-spinner fa-spin"></i> در حال ساخت…');
            app.req('ai_operation_rule_draft', { instruction: instruction }).then(function (res) {
                setBusy(cta, false);
                if (res && res.status === 'success') { renderOpRule(res); }
                else if (box) { box.innerHTML = errorBlock((res && res.message) || 'ساخت پیش‌نویس قانون در دسترس نیست.', 'app.draftOperationRuleWithAi()'); }
            });
        };
        function jsonBlock(label, obj) {
            if (obj === undefined || obj === null || (typeof obj === 'object' && !Object.keys(obj).length)) { return ''; }
            var txt;
            try { txt = JSON.stringify(obj, null, 2); } catch (e) { txt = String(obj); }
            return '<div style="margin-top:8px;"><div class="ai-result__section-label">' + esc(label) + '</div><pre class="ai-json">' + esc(txt) + '</pre></div>';
        }
        function renderOpRule(res) {
            var filter = field(res, ['filter', 'filter_json'], {});
            var condition = field(res, ['condition', 'condition_json'], {});
            var operation = field(res, ['operation', 'operation_json'], {});
            var explanation = field(res, ['explanation', 'description', 'rule_name'], '');
            var warnings = field(res, ['warnings'], []);
            var canPreview = field(res, ['can_preview'], true);
            var rid = field(res, ['ai_request_id', 'request_id', 'id'], null);
            app.state.ai.opRule = { filter: filter, condition: condition, operation: operation, requestId: rid };
            var html = '<div class="ai-result">';
            if (explanation) { html += '<div class="ai-result__summary"><strong>توضیح</strong><br>' + fmt(explanation) + '</div>'; }
            html += listBlock('هشدارها', warnings, true);
            html += jsonBlock('فیلتر (filter)', filter);
            html += jsonBlock('شرط (condition)', condition);
            html += jsonBlock('عملیات (operation)', operation);
            html += '<div class="ai-actions">' +
                '<button type="button" class="ai-action" onclick="app.aiOpRuleCopy()"><i class="fa-solid fa-copy"></i> کپی JSON</button>' +
                (canPreview ? '<button type="button" class="ai-action ai-action--primary" onclick="app.aiOpRulePreview()"><i class="fa-solid fa-magnifying-glass"></i> پیش‌نمایش</button>' : '') +
                '</div>';
            html += '<div class="ai-disclaimer"><i class="fa-solid fa-triangle-exclamation"></i><span>این خروجی فقط پیش‌نویس است؛ اجرای واقعی عملیات همیشه به‌صورت دستی و پس از پیش‌نمایش انجام می‌شود.</span></div>';
            html += feedbackRow(rid);
            html += '</div>';
            var box = byId('ai-oprule-result');
            box.classList.remove('hidden');
            box.innerHTML = html;
        }
        app.aiOpRuleCopy = function () {
            var r = app.state.ai.opRule; if (!r) { return; }
            try { copyText(JSON.stringify({ filter: r.filter, condition: r.condition, operation: r.operation }, null, 2)); } catch (e) { }
        };
        app.aiOpRulePreview = function () {
            var r = app.state.ai.opRule; if (!r) { return; }
            if (r.requestId) { app.sendAiFeedback(r.requestId, 1, null, true); }
            if (typeof app.previewOperationManager === 'function') {
                app.previewOperationManager(1);
                toast('پیش‌نمایش با فیلترهای فعلی فرم اجرا شد؛ نتیجه را بررسی کنید');
            }
        };

        /* =================================================================
         * Feedback
         * ================================================================= */
        app.sendAiFeedback = function (requestId, rating, sourceBtn, accepted) {
            if (!requestId) { return; }
            if (!app.canAi('ai.feedback')) { return; }
            var payload = { ai_request_id: requestId, rating: (rating === 1 || rating === '1') ? 1 : 0 };
            if (accepted === true) { payload.accepted = 1; }
            app.req('ai_feedback', payload).then(function () { });
            if (sourceBtn) {
                var row = sourceBtn.closest('.ai-feedback');
                if (row) {
                    var btns = row.querySelectorAll('.ai-feedback__btn');
                    for (var i = 0; i < btns.length; i++) { btns[i].classList.remove('is-active'); btns[i].disabled = true; }
                    sourceBtn.classList.add('is-active');
                    var thanks = document.createElement('span');
                    thanks.className = 'ai-feedback__done';
                    thanks.innerHTML = '<i class="fa-solid fa-check"></i> ممنون';
                    row.appendChild(thanks);
                }
            }
        };

        /* =================================================================
         * Admin settings (enable / disable) — system settings section
         * ================================================================= */
        function aiBadge(state) {
            var el = byId('settings-badge-ai'); if (!el) { return; }
            var map = {
                on: ['فعال', '#022c22', '#34d399', 'rgba(16,185,129,.5)'],
                off: ['غیرفعال', '#2c0b0b', '#f87171', 'rgba(239,68,68,.5)'],
                loading: ['در حال بارگذاری…', '', '#cbd5e1', '']
            };
            var m = map[state] || map.loading;
            el.textContent = m[0];
            el.style.background = m[1] || 'rgba(31,41,55,.7)';
            el.style.color = m[2];
            el.style.borderColor = m[3] || '#374151';
        }
        app.loadAiSettings = function () {
            var chk = byId('settings-ai-enabled'); if (!chk) { return; }
            aiBadge('loading');
            app.req('get_ai_settings').then(function (res) {
                if (res && res.status === 'success') {
                    app.state.ai.settingsLoaded = true;
                    var enabled = String(field(res, ['ai_feature_enabled'], '0')) === '1';
                    app.state.ai.featureEnabled = enabled;
                    chk.checked = enabled;
                    aiBadge(enabled ? 'on' : 'off');
                    var configured = field(res, ['openai_configured'], false);
                    var confEl = byId('settings-ai-configured');
                    if (confEl) { confEl.textContent = configured ? 'پیکربندی شده' : 'تنظیم نشده'; confEl.style.color = configured ? '#34d399' : '#f87171'; }
                    var model = field(res, ['openai_model'], '');
                    var modelEl = byId('settings-ai-model');
                    if (modelEl) { modelEl.textContent = model || '—'; }
                    var status = byId('settings-ai-status');
                    if (status && !configured) { status.textContent = 'برای پاسخ‌دهی، کلید OpenAI باید در سرور تنظیم شده باشد.'; }
                    app.applyAiVisibility();
                } else {
                    app.state.ai.settingsLoaded = true;
                    app.state.ai.featureEnabled = false;
                    aiBadge('off');
                    var st = byId('settings-ai-status');
                    if (st) { st.textContent = (res && res.message) || 'دریافت تنظیمات هوش مصنوعی ناموفق بود.'; }
                    app.applyAiVisibility();
                }
            });
        };
        app.saveAiSettings = function () {
            var chk = byId('settings-ai-enabled'); if (!chk) { return; }
            var enabled = chk.checked ? 1 : 0;
            var status = byId('settings-ai-status');
            if (status) { status.textContent = 'در حال ذخیره…'; }
            app.req('save_ai_settings', { ai_feature_enabled: enabled }).then(function (res) {
                if (res && res.status === 'success') {
                    var on = String(field(res, ['ai_feature_enabled'], enabled)) === '1';
                    app.state.ai.settingsLoaded = true;
                    app.state.ai.featureEnabled = on;
                    aiBadge(on ? 'on' : 'off');
                    if (status) { status.textContent = on ? 'دستیار هوش مصنوعی فعال شد.' : 'دستیار هوش مصنوعی برای همه کاربران غیرفعال شد.'; }
                    toast(res.message || 'تنظیمات ذخیره شد');
                    // Refresh permissions so every AI surface appears/disappears at once.
                    app.state.rbacCurrentPermissionsLoaded = false;
                    Promise.resolve(app.loadCurrentRbacPermissions(true)).then(function () {
                        if (typeof app.applyRbacMenuVisibility === 'function') { app.applyRbacMenuVisibility(); }
                        app.applyAiVisibility();
                    });
                } else {
                    if (status) { status.textContent = (res && res.message) || 'ذخیره تنظیمات ناموفق بود.'; }
                    toast((res && res.message) || 'ذخیره ناموفق بود', 'error');
                }
            });
        };

        /* =================================================================
         * Per-modal reset hook
         * ================================================================= */
        app.onAiModalOpen = function (id) {
            if (id === 'modalProfile') { resetBriefPanel(); }
            else if (id === 'modalDeal') { resetRiskPanel(); }
            else if (id === 'modalActivity') { resetActivityDraft(); }
            app.applyAiVisibility();
        };

        /* =================================================================
         * Lifecycle monkey-patches (keep app.js untouched)
         * ================================================================= */
        if (typeof app.loadCurrentRbacPermissions === 'function') {
            var _load = app.loadCurrentRbacPermissions.bind(app);
            app.loadCurrentRbacPermissions = function (force) {
                return Promise.resolve(_load(force)).then(function (r) { try { app.applyAiVisibility(); } catch (e) { } return r; });
            };
        }
        if (typeof app.applyRbacMenuVisibility === 'function') {
            var _menu = app.applyRbacMenuVisibility.bind(app);
            app.applyRbacMenuVisibility = function () { var r = _menu(); try { app.applyAiVisibility(); } catch (e) { } return r; };
        }
        if (typeof app.openModal === 'function') {
            var _open = app.openModal.bind(app);
            app.openModal = function (id, options) { var r = _open(id, options); try { app.onAiModalOpen(id); } catch (e) { } return r; };
        }
        if (typeof app.switchSettingsSection === 'function') {
            var _switch = app.switchSettingsSection.bind(app);
            app.switchSettingsSection = function (key) {
                var r = _switch(key);
                if (String(key) === 'system') { try { app.loadAiSettings(); } catch (e) { } }
                return r;
            };
        }
        window.setTimeout(function () {
            try {
                if (app.state && app.state.rbacCurrentPermissionsLoaded === true) { app.applyAiVisibility(); }
                var activeSettings = document.querySelector('[data-settings-section="system"]:not(.hidden)');
                if (activeSettings) { app.loadAiSettings(); }
            } catch (e) { }
        }, 0);

        /* =================================================================
         * Global event wiring (delegated, bound once)
         * ================================================================= */
        document.addEventListener('click', function (e) {
            var chip = e.target.closest && e.target.closest('[data-ai-chip]');
            if (chip) {
                var kind = chip.getAttribute('data-ai-chip-kind') || 'chat';
                if (kind === 'kpi') { app.explainKpiWithAi(); return; }
                var input = byId('ai-prompt-input');
                if (input) { input.value = chip.textContent.trim(); }
                app.sendAiChat();
                return;
            }
            var fb = e.target.closest && e.target.closest('[data-ai-fb]');
            if (fb && !fb.disabled) {
                app.sendAiFeedback(fb.getAttribute('data-ai-fb'), parseInt(fb.getAttribute('data-ai-rating'), 10), fb);
            }
        });
        document.addEventListener('keydown', function (e) {
            if (e.key === 'Escape' && app.state.ai && app.state.ai.drawerOpen) { app.closeAiDrawer(); }
        });

        // Mobile: drag the drawer grip down to dismiss.
        (function () {
            var drawer = null, startY = 0, dy = 0, dragging = false;
            document.addEventListener('touchstart', function (e) {
                var grip = e.target && e.target.closest && e.target.closest('#ai-drawer .ai-drawer__grip, #ai-drawer .ai-drawer__header');
                if (!grip) { dragging = false; return; }
                if (window.innerWidth > 768) { dragging = false; return; }
                drawer = byId('ai-drawer'); if (!drawer) { return; }
                startY = e.touches[0].clientY; dy = 0; dragging = true;
                drawer.style.transition = 'none';
            }, { passive: true });
            document.addEventListener('touchmove', function (e) {
                if (!dragging || !drawer) { return; }
                dy = e.touches[0].clientY - startY;
                if (dy > 0) { drawer.style.transform = 'translateY(' + dy + 'px)'; }
            }, { passive: true });
            document.addEventListener('touchend', function () {
                if (!dragging || !drawer) { return; }
                dragging = false;
                var el = drawer; drawer = null;
                el.style.transition = '';
                if (dy > 110) { app.closeAiDrawer(); } else { el.style.transform = ''; }
            }, { passive: true });
        })();

        // Permissions may already be loaded (e.g. after a hot reload) — reconcile now.
        try { app.applyAiVisibility(); } catch (e) { }
    });
})();
