
// APP_USER will be defined in layout
const APP_USER = window.APP_USER || { role: 'guest', didar_id: null, name: '' };
const APP_DEBUG = window.APP_DEBUG !== false;
const APP_DIDAR_UI_ENABLED = window.APP_DIDAR_UI_ENABLED === true;
const FEATURE_HISTORY_V1_DEFAULT = true;
const FEATURE_MODAL_FRESHNESS_V1_DEFAULT = true;
const FEATURE_HISTORY_STORAGE_KEY = 'feature_spa_history_v1';
const FEATURE_MODAL_FRESHNESS_STORAGE_KEY = 'feature_modal_freshness_v1';

// Reduce noisy debug logs in production while keeping warnings/errors visible.
// This only filters clearly-marked debug messages and common dev-only traces.
(function patchDebugConsoleLogging() {
    if (APP_DEBUG || typeof console === 'undefined' || console.__appDebugPatched) {
        return;
    }
    const originalLog = typeof console.log === 'function' ? console.log.bind(console) : null;
    if (!originalLog) {
        return;
    }
    const suppressedPrefixes = [
        '[DEBUG]',
        '[CONTACT_SWITCH]',
        '[DEAL_SAVE',
        'Tehran Gregorian:',
        'response received',
        'get_didar_person response',
        'get_lead_didar_data response',
        'get_legacy_deals response'
    ];
    console.log = function (...args) {
        const first = args.length > 0 ? args[0] : '';
        if (typeof first === 'string') {
            for (let index = 0; index < suppressedPrefixes.length; index++) {
                if (first.startsWith(suppressedPrefixes[index])) {
                    return;
                }
            }
        }
        return originalLog(...args);
    };
    if (typeof console.debug === 'function') {
        console.debug = function () {};
    }
    console.__appDebugPatched = true;
})();

(function ensureSweetAlertOverlayZIndex() {
    if (typeof document === 'undefined') return;
    if (document.getElementById('app-swal-zindex-fix')) return;
    const style = document.createElement('style');
    style.id = 'app-swal-zindex-fix';
    style.textContent = '.swal2-container{z-index:7000 !important;}';
    document.head.appendChild(style);
})();

// Tehran Timezone Helper Functions
function getTehranNow() {
    // Get current date/time in Tehran timezone
    const now = new Date();
    const formatter = new Intl.DateTimeFormat('en-CA', {
        timeZone: 'Asia/Tehran',
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false
    });
    const parts = formatter.formatToParts(now);
    
    const year = parseInt(parts.find(p => p.type === 'year').value);
    const month = parseInt(parts.find(p => p.type === 'month').value) - 1; // JS months are 0-indexed
    const day = parseInt(parts.find(p => p.type === 'day').value);
    const hour = parseInt(parts.find(p => p.type === 'hour').value);
    const minute = parseInt(parts.find(p => p.type === 'minute').value);
    const second = parseInt(parts.find(p => p.type === 'second').value);
    
    return new Date(year, month, day, hour, minute, second);
}

function getTehranDate() {
    // Get current date in Tehran timezone (without time)
    const now = new Date();
    const formatter = new Intl.DateTimeFormat('en-CA', {
        timeZone: 'Asia/Tehran',
        year: 'numeric',
        month: '2-digit',
        day: '2-digit'
    });
    const parts = formatter.formatToParts(now);
    
    const year = parseInt(parts.find(p => p.type === 'year').value);
    const month = parseInt(parts.find(p => p.type === 'month').value) - 1;
    const day = parseInt(parts.find(p => p.type === 'day').value);
    
    return new Date(year, month, day);
}

function getTehranPersianDate() {
    // Use moment-jalaali for accurate date conversion (fixes leap year bug)
    if (typeof moment !== 'undefined') {
        const testMoment = moment();
        if (typeof testMoment.jYear === 'function') {
            // Get current date in Tehran timezone
            const now = new Date();
            const formatter = new Intl.DateTimeFormat('en-CA', {
                timeZone: 'Asia/Tehran',
                year: 'numeric',
                month: '2-digit',
                day: '2-digit'
            });
            const parts = formatter.formatToParts(now);

            const gYear = parseInt(parts.find(p => p.type === 'year').value);
            const gMonth = parseInt(parts.find(p => p.type === 'month').value);
            const gDay = parseInt(parts.find(p => p.type === 'day').value);

            // Use moment-jalaali to convert gregorian to persian
            const gregorianMoment = moment(`${gYear}-${String(gMonth).padStart(2, '0')}-${String(gDay).padStart(2, '0')}`, 'YYYY-MM-DD');

            // Get Persian date components using moment-jalaali API
            const persianYear = gregorianMoment.jYear();
            const persianMonth = gregorianMoment.jMonth() + 1; // moment-jalaali months are 0-indexed
            const persianDay = gregorianMoment.jDate();

            // Create persianDate object from array format for compatibility with persianDatepicker
            if (typeof persianDate !== 'undefined') {
                const pDate = new persianDate([persianYear, persianMonth, persianDay]);

                // Debug log (only once)
                if (!window._tehranDateLogged) {
                    console.log('Tehran Gregorian:', gYear, gMonth, gDay, '-> Persian (moment-jalaali):', pDate.format('YYYY/MM/DD'));
                    window._tehranDateLogged = true;
                    setTimeout(() => { window._tehranDateLogged = false; }, 1000);
                }

                return pDate;
            }
        }
    }

    // Fallback to persianDate if moment-jalaali not available
    if (typeof persianDate === 'undefined') {
        console.warn('Neither jalali-moment nor persianDate available');
        return null;
    }

    // Get current date in Tehran timezone
    const now = new Date();
    const formatter = new Intl.DateTimeFormat('en-CA', {
        timeZone: 'Asia/Tehran',
        year: 'numeric',
        month: '2-digit',
        day: '2-digit'
    });
    const parts = formatter.formatToParts(now);

    const gYear = parseInt(parts.find(p => p.type === 'year').value);
    const gMonth = parseInt(parts.find(p => p.type === 'month').value);
    const gDay = parseInt(parts.find(p => p.type === 'day').value);

    // Use persianDate directly (fallback)
    return new persianDate([gYear, gMonth, gDay]);
}

function parseAppDateValue(dateString) {
    const raw = String(dateString || '').trim();
    if (!raw) return null;

    const dateOnlyMatch = raw.match(/^(\d{4})-(\d{2})-(\d{2})$/);
    if (dateOnlyMatch) {
        return new Date(
            parseInt(dateOnlyMatch[1], 10),
            parseInt(dateOnlyMatch[2], 10) - 1,
            parseInt(dateOnlyMatch[3], 10)
        );
    }

    const localDateTimeMatch = raw.match(/^(\d{4})-(\d{2})-(\d{2})[ ](\d{2}):(\d{2})(?::(\d{2}))?/);
    if (localDateTimeMatch) {
        return new Date(
            parseInt(localDateTimeMatch[1], 10),
            parseInt(localDateTimeMatch[2], 10) - 1,
            parseInt(localDateTimeMatch[3], 10),
            parseInt(localDateTimeMatch[4], 10) || 0,
            parseInt(localDateTimeMatch[5], 10) || 0,
            parseInt(localDateTimeMatch[6], 10) || 0
        );
    }

    return new Date(raw);
}

function toPersianDate(dateString) {
    if (!dateString) return '';
    try {
        // Use moment-jalaali for accurate conversion (fixes leap year bug)
        if (typeof moment !== 'undefined') {
            const testMoment = moment();
            if (typeof testMoment.jYear === 'function') {
                let date = parseAppDateValue(dateString);
                
                const m = moment(date);
                return m.format('jYYYY/jMM/jDD').replace(/\//g, '/');
            }
        }
        
        // Fallback to persianDate
        if (typeof persianDate === 'undefined') {
            console.warn('persianDate library not loaded');
            return dateString.substring(0, 10);
        }
        
        // Parse date-only and local datetime values as local calendar values.
        let date = parseAppDateValue(dateString);
        
        const pDate = new persianDate(date);
        return pDate.format('YYYY/MM/DD');
    } catch(e) {
        console.warn('Error in toPersianDate:', e, dateString);
        return dateString.substring(0, 10);
    }
}

function toPersianDateTime(dateString) {
    if (!dateString) return '';
    try {
        // Use moment-jalaali for accurate conversion (fixes leap year bug)
        if (typeof moment !== 'undefined' && typeof moment.jYear === 'function') {
            let date = parseAppDateValue(dateString);
            
            const m = moment(date);
            return m.format('jYYYY/jMM/jDD HH:mm').replace(/\//g, '/');
        }
        
        // Fallback to persianDate
        if (typeof persianDate === 'undefined') {
            console.warn('Neither jalali-moment nor persianDate available');
            return dateString;
        }
        
        let date = parseAppDateValue(dateString);
        
        const pDate = new persianDate(date);
        return pDate.format('YYYY/MM/DD HH:mm');
    } catch(e) {
        console.warn('Error in toPersianDateTime:', e, dateString);
        return dateString;
    }
}

function persianToGregorian(jalaliDate, time = '00:00') {
    if (!jalaliDate || !jalaliDate.trim()) return null;
    try {
        // Check if persianDate is available
        if (typeof persianDate === 'undefined') {
            console.warn('persianDate library not loaded');
            return null;
        }
        
        const parts = jalaliDate.trim().split('/');
        if (parts.length !== 3) return null;
        const year = parseInt(parts[0]);
        const month = parseInt(parts[1]);
        const day = parseInt(parts[2]);
        
        // Validate date parts
        if (isNaN(year) || isNaN(month) || isNaN(day)) return null;
        if (year < 1300 || year > 1500) return null; // Reasonable Persian year range
        if (month < 1 || month > 12) return null;
        if (day < 1 || day > 31) return null;
        
        // Create Persian date object
        let pDate;
        try {
            pDate = new persianDate([year, month, day]);
            
            // Convert to native JavaScript Date object (this handles the conversion automatically)
            const nativeDate = pDate.toDate();
            
            // Validate the date
            if (!nativeDate || isNaN(nativeDate.getTime())) {
                return null;
            }
            
            // Extract time parts
            const timeParts = time.split(':');
            const hours = parseInt(timeParts[0] || 0);
            const minutes = parseInt(timeParts[1] || 0);
            
            // IMPORTANT: Create final date using local timezone
            // The date should be treated as local time, not UTC
            // Format: YYYY-MM-DD HH:mm:ss (for local timezone storage)
            const year4 = nativeDate.getFullYear();
            const month2 = String(nativeDate.getMonth() + 1).padStart(2, '0');
            const day2 = String(nativeDate.getDate()).padStart(2, '0');
            const hour2 = String(hours).padStart(2, '0');
            const min2 = String(minutes).padStart(2, '0');
            
            // Return in local timezone format (not ISO/UTC)
            return `${year4}-${month2}-${day2} ${hour2}:${min2}:00`;
        } catch(e) {
            console.error('Error in persianDate conversion:', e);
            return null;
        }
    } catch(e) {
        console.error('Error converting Persian date:', e, 'Input:', jalaliDate);
        return null;
    }
}

function parseOffset(offsetValue) {
    const match = offsetValue.match(/GMT([+-])(\d{1,2}):?(\d{2})?/);
    if (!match) return null;
    const sign = match[1] === '-' ? -1 : 1;
    const hours = parseInt(match[2], 10) || 0;
    const minutes = parseInt(match[3] || '0', 10);
    return sign * (hours * 60 + minutes);
}

function getTehranNow() {
    const formatter = new Intl.DateTimeFormat('en-US', {
        timeZone: 'Asia/Tehran',
        hour12: false,
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        timeZoneName: 'shortOffset'
    });

    const now = new Date();
    const parts = formatter.formatToParts(now);
    const mapped = {};
    let offsetMinutes = null;

    for (const part of parts) {
        if (part.type === 'year' || part.type === 'month' || part.type === 'day' ||
            part.type === 'hour' || part.type === 'minute' || part.type === 'second') {
            mapped[part.type] = parseInt(part.value, 10);
        }
        if (part.type === 'timeZoneName' && offsetMinutes === null) {
            offsetMinutes = parseOffset(part.value);
        }
    }

    const tehranOffset = offsetMinutes !== null ? offsetMinutes : -210;
    const localOffset = now.getTimezoneOffset();
    const diff = tehranOffset - localOffset;
    const tehranTimestamp = now.getTime() + diff * 60000;

    return new Date(tehranTimestamp);
}

// Validation Helper Functions
function validatePersianText(text, fieldName) {
    if (!text || text.trim() === '') return { valid: true }; // Allow empty
    // فقط حروف فارسی، فاصله و اعراب
    const persianRegex = /^[\u0600-\u06FF\s\u200C\u200D]+$/;
    if (!persianRegex.test(text.trim())) {
        return { valid: false, message: `${fieldName} باید فقط شامل حروف فارسی باشد` };
    }
    return { valid: true };
}

function validateNationalId(nationalId) {
    if (!nationalId || nationalId.trim() === '') return { valid: true }; // Allow empty
    const cleaned = nationalId.trim().replace(/\D/g, ''); // فقط اعداد
    if (cleaned.length !== 10) {
        return { valid: false, message: 'کد ملی باید دقیقاً 10 رقم باشد' };
    }
    return { valid: true };
}

function validatePhone(phone, fieldName) {
    if (!phone || phone.trim() === '') return { valid: true }; // Allow empty
    const cleaned = phone.trim().replace(/\D/g, ''); // فقط اعداد
    if (cleaned.length !== 10) {
        return { valid: false, message: `${fieldName} باید دقیقاً 10 رقم باشد (بدون صفر اول)` };
    }
    if (cleaned.startsWith('0')) {
        return { valid: false, message: `${fieldName} نباید با صفر شروع شود` };
    }
    return { valid: true };
}

// تبدیل اعداد فارسی به انگلیسی
function toEnglishDigits(str = '') {
    const map = {
        '۰':'0','۱':'1','۲':'2','۳':'3','۴':'4','۵':'5','۶':'6','۷':'7','۸':'8','۹':'9',
        '٠':'0','١':'1','٢':'2','٣':'3','٤':'4','٥':'5','٦':'6','٧':'7','٨':'8','٩':'9'
    };
    return String(str).replace(/[۰-۹٠-٩]/g, d => map[d] || d);
}

// نرمال‌سازی تاریخ به فرمت YYYY/MM/DD با ارقام انگلیسی
function normalizeDate(val = '') {
    const clean = toEnglishDigits(val).trim().replace(/-/g, '/');
    if (!clean) return '';
    if (/^\d{4}\/\d{1,2}\/\d{1,2}$/.test(clean)) return clean;
    return '';
}

// Money formatting helpers (###,### while typing)
function parseMoneyValue(val) {
    const s = toEnglishDigits(String(val ?? '')).replace(/,/g, '').trim();
    if (!s) return 0;
    const cleaned = s.replace(/[^\d.]/g, '');
    const n = parseFloat(cleaned);
    return Number.isFinite(n) ? n : 0;
}

function formatMoneyString(val) {
    const s = toEnglishDigits(String(val ?? '')).replace(/,/g, '');
    const cleaned = s.replace(/[^\d]/g, '');
    if (!cleaned) return '';
    return cleaned.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

function formatMoneyInput(el) {
    if (!el) return;
    const raw = String(el.value ?? '');
    const start = typeof el.selectionStart === 'number' ? el.selectionStart : raw.length;
    const digitsLeft = raw.slice(0, start).replace(/[^\d]/g, '').length;
    const formatted = formatMoneyString(raw);
    el.value = formatted;
    if (typeof el.setSelectionRange === 'function') {
        let pos = 0;
        let digitCount = 0;
        while (pos < formatted.length && digitCount < digitsLeft) {
            if (/\d/.test(formatted[pos])) digitCount++;
            pos++;
        }
        el.setSelectionRange(pos, pos);
    }
}

const app = {
	    state: { 
	        user: APP_USER,
        leads: [],
        leadsPagination: {
            currentPage: 1,
            perPage: 50,
            totalCount: 0,
            totalPages: 0
        }, 
        deals: [],
        activityTypes: [],
        didarUsers: [],
        referrals: [],
        currentLead: null,
        currentDeals: [],
        dealHasActivity: false,
        originalDealTitleOptions: null,
        crmOwnerId: null,
        crmOwnerName: '',
        lookups: {},
        lookupsLoaded: false,
        lookupGroups: [],
        lookupItems: [],
        lookupSelectedGroup: '',
        lookupProductsManagerRows: [],
        didarDeals: [], // Cache for Didar deals to avoid API inconsistency
        products: [],
        kpiUsers: [],
        kpiProducts: [],
        kpiFilters: {
            date_from: '',
            date_to: '',
            owner_didar_id: '',
            product_id: '',
            owner_didar_ids: [],
            product_ids: [],
            deal_statuses: [],
            deal_lead_types: []
        },
        performanceFilters: {
            date_from: '',
            date_to: '',
            owner_didar_ids: [],
            product_ids: []
        },
        performanceDashboard: null,
        performanceActiveTab: 'activity',
        performanceCharts: {},
        performanceChartSource: {},
        performanceRenderedRows: {},
        performanceDragEnabled: {
            sales: false,
            activity: false
        },
        performanceChartWindow: {
            salesSize: 14,
            salesOffset: 0,
            activitySize: 14,
            activityOffset: 0
        },
        kpiAdvancedTables: {},
        kpiAdvancedUi: {},
        kpiAdvancedRenderedRows: {},
        kpiAdvancedVisibleTables: {},
        kpiDailyOperationalTables: {},
        kpiDailyOperationalUi: {},
        kpiDrilldown: {
            tableId: '',
            metricKey: '',
            row: null,
            data: null,
            loading: false
        },
        kpiDrilldownExport: {
            datasets: {},
            selectedDataset: ''
        },
        kpiPickerOpenKey: null,
        showKpiDealDetails: false,
        activityStagePrev: '',
        saleStatusMap: {
            success: { title: 'موفق', color: 'border-green-500', bg: 'bg-green-600', icon: '✅' },
            failed: { title: 'ناموفق', color: 'border-red-500', bg: 'bg-red-600', icon: '❌' },
            unknown: { title: 'نامشخص', color: 'border-gray-500', bg: 'bg-gray-600', icon: '➖' }
        },
        pipelineStages: [
            'بلاتکلیف',
            'معامله جدید',
            'تماس عدم پاسخ',
            'تماس پیگیری',
            'ارسال لینک پرداخت',
            'پرداخت',
            'پیگیری مانده حساب',
            'برگزاری دوره',
            'پشتیبانی',
            'ارجاع به CRM'
        ],
	        paymentsLocked: false,
        dealInfoLocked: false,
        dealInfoAdminUnlocked: false,
	        lastPaymentsCache: [],
	        exportCatalog: null,
	        exportCurrentDatasetId: null
	    },
    featureFlags: {
        historyV1: false,
        modalFreshnessV1: true
    },
    _telemetryBound: false,
    _requestSeq: 0,
    _contextTokenSeq: 0,
    _inFlightControllers: {},
    _historyBound: false,
    _historyUiBound: false,
    _isApplyingHistoryState: false,
    _lastHistoryState: null,
    _historyUiSyncTimer: null,
    _scrollSyncTimer: null,
    _dirtyTrackingBound: false,
    _beforeUnloadHandler: null,
    _beforeUnloadBound: false,
    dirtyForms: {},
    _activeContextTokens: {
        profile: null,
        dealModal: null
    },

    initFeatureFlags() {
        const getStoredBool = (key) => {
            try {
                const raw = localStorage.getItem(key);
                if (raw === 'true') return true;
                if (raw === 'false') return false;
            } catch (_) {}
            return null;
        };
        const storedHistoryFlag = getStoredBool(FEATURE_HISTORY_STORAGE_KEY);
        const storedFreshnessFlag = getStoredBool(FEATURE_MODAL_FRESHNESS_STORAGE_KEY);
        const historyFlag = (storedHistoryFlag !== null)
            ? storedHistoryFlag
            : ((typeof window.FEATURE_SPA_HISTORY_V1 !== 'undefined')
                ? window.FEATURE_SPA_HISTORY_V1 === true
                : FEATURE_HISTORY_V1_DEFAULT);
        const freshnessFlag = (storedFreshnessFlag !== null)
            ? storedFreshnessFlag
            : ((typeof window.FEATURE_MODAL_FRESHNESS_V1 !== 'undefined')
                ? window.FEATURE_MODAL_FRESHNESS_V1 !== false
                : FEATURE_MODAL_FRESHNESS_V1_DEFAULT);
        this.featureFlags.historyV1 = historyFlag;
        this.featureFlags.modalFreshnessV1 = freshnessFlag;

        window.FEATURE_SPA_HISTORY_V1 = this.featureFlags.historyV1;
        window.FEATURE_MODAL_FRESHNESS_V1 = this.featureFlags.modalFreshnessV1;
        this.logTelemetry('flags_init', {
            history_v1: this.featureFlags.historyV1,
            modal_freshness_v1: this.featureFlags.modalFreshnessV1
        });
    },

    persistFeatureFlags() {
        try {
            localStorage.setItem(FEATURE_HISTORY_STORAGE_KEY, this.featureFlags.historyV1 ? 'true' : 'false');
            localStorage.setItem(FEATURE_MODAL_FRESHNESS_STORAGE_KEY, this.featureFlags.modalFreshnessV1 ? 'true' : 'false');
            return { ok: true };
        } catch (error) {
            return { ok: false, error };
        }
    },

    logTelemetry(event, payload = {}, level = 'info') {
        const entry = {
            event,
            level,
            ts: new Date().toISOString(),
            ...payload
        };
        if (APP_DEBUG || level !== 'debug') {
            console.log('[APP_TELEMETRY]', entry);
        }
        if (typeof Logger !== 'undefined' && typeof Logger.log === 'function') {
            try {
                Logger.log('AppTelemetry', entry, 'app_telemetry', null);
            } catch (_) {}
        }
    },

    bindGlobalTelemetryHandlers() {
        if (this._telemetryBound || typeof window === 'undefined') return;
        this._telemetryBound = true;

        window.addEventListener('error', (event) => {
            this.logTelemetry('window_error', {
                message: event?.message || '',
                filename: event?.filename || '',
                lineno: event?.lineno || 0,
                colno: event?.colno || 0
            }, 'error');
        });

        window.addEventListener('unhandledrejection', (event) => {
            const reason = event?.reason;
            this.logTelemetry('window_unhandled_rejection', {
                message: reason?.message || String(reason || '')
            }, 'error');
        });
    },

    createContextToken(scope, meta = {}) {
        this._contextTokenSeq += 1;
        const token = `${scope}:${Date.now()}:${this._contextTokenSeq}`;
        this._activeContextTokens[scope] = token;
        this.logTelemetry('context_token_created', { scope, token, ...meta }, 'debug');
        return token;
    },

    isContextTokenActive(scope, token) {
        return this._activeContextTokens[scope] === token;
    },

    dropIfStaleContext(scope, token, meta = {}) {
        if (!this.featureFlags.modalFreshnessV1) return false;
        const stale = !this.isContextTokenActive(scope, token);
        if (stale) {
            this.logTelemetry('stale_context_dropped', { scope, token, ...meta }, 'debug');
        }
        return stale;
    },

    composeAbortSignal(signals = []) {
        const controller = new AbortController();
        const list = (signals || []).filter(Boolean);
        const abort = (reason) => {
            if (!controller.signal.aborted) controller.abort(reason);
        };
        list.forEach((sig) => {
            if (sig.aborted) {
                abort(sig.reason);
                return;
            }
            sig.addEventListener('abort', () => abort(sig.reason), { once: true });
        });
        return controller.signal;
    },

    cancelInFlightRequest(requestKey) {
        if (!requestKey) return;
        const existing = this._inFlightControllers[requestKey];
        if (existing && !existing.signal.aborted) {
            existing.abort();
            this.logTelemetry('request_aborted_previous', { requestKey }, 'debug');
        }
    },

    getViewCapabilityMap() {
        return {
            'dashboard': 'menu.dashboard',
            'performance-dashboard': 'menu.performance_dashboard',
            'leads': 'menu.leads',
            'operation-manager': 'menu.operation_manager',
            'blataklif-leads': 'menu.blataklif_leads',
            'deals': 'menu.deals',
            'legacy-deals': 'menu.legacy_deals',
            'referrals': 'menu.referrals',
            'persons': 'menu.persons',
            'reminders': 'menu.reminders',
            'settings': 'menu.settings',
            'lookups': 'menu.lookups',
            'exports': 'menu.exports',
            'cheques': 'menu.cheques',
            'audit-logs': 'menu.audit_logs',
            'users': 'menu.users'
        };
    },

    getKnownViews() {
        return [...Object.keys(this.getViewCapabilityMap()), 'phone-logs'];
    },

    normalizeView(view) {
        const fallback = 'dashboard';
        const normalized = String(view || '').trim();
        return this.getKnownViews().includes(normalized) ? normalized : fallback;
    },

    canAccessView(view, options = {}) {
        const opts = options || {};
        const map = this.getViewCapabilityMap();
        const requiredCap = map[view];
        if (!requiredCap) return true;
        const allowed = this.canCurrentUserCapabilityClient(requiredCap);
        if (!allowed && opts.showWarning) {
            Swal.fire('عدم دسترسی', 'دسترسی شما به این بخش مجاز نیست', 'warning');
        }
        return allowed;
    },

    getCurrentView() {
        const active = document.querySelector('.sidebar-item.active');
        if (active && active.id) return active.id.replace('menu-', '');
        const visible = Array.from(document.querySelectorAll('.view-section'))
            .find(el => !el.classList.contains('hidden') && String(el.id || '').startsWith('view-'));
        if (visible) return String(visible.id).replace('view-', '');
        return 'dashboard';
    },

    getOpenModalId() {
        const openModal = document.querySelector('[id^="modal"]:not(.hidden)');
        return openModal ? String(openModal.id || '') : '';
    },

    buildHistoryHash(view) {
        return `#/${this.normalizeView(view)}`;
    },

    parseViewFromHash() {
        const hash = String(window.location.hash || '').trim();
        const match = hash.match(/^#\/([a-zA-Z0-9\-_]+)$/);
        if (!match) return '';
        return this.normalizeView(match[1]);
    },

    collectHistoryUiState(view) {
        const ui = { scrollY: Number(window.scrollY || 0) };
        const readFilters = (ids) => {
            const out = {};
            (ids || []).forEach((id) => {
                const el = document.getElementById(id);
                if (el) out[id] = el.value ?? '';
            });
            return out;
        };

        if (view === 'leads') {
            ui.filters = readFilters([
                'leads-search', 'leads-customer-code-search', 'leads-stage-filter', 'leads-owner-filter', 'leads-product-filter',
                'leads-customer-level-filter', 'leads-register-from', 'leads-register-to',
                'leads-activity-from', 'leads-activity-to'
            ]);
            ui.pagination = { currentPage: Number(this.state.leadsPagination?.currentPage || 1) };
        } else if (view === 'deals') {
            ui.filters = readFilters([
                'deals-search', 'deals-customer-code-search', 'deals-pipeline-filter', 'deals-status-filter', 'deals-owner-filter',
                'deals-register-from', 'deals-register-to', 'deals-activity-from', 'deals-activity-to'
            ]);
        } else if (view === 'lookups') {
            ui.lookupSelectedGroup = this.state.lookupSelectedGroup || '';
        }
        return ui;
    },

    applyHistoryUiState(view, ui = {}) {
        const data = ui || {};
        const applyFilters = (filters) => {
            Object.entries(filters || {}).forEach(([id, value]) => {
                const el = document.getElementById(id);
                if (!el) return;
                el.value = value ?? '';
            });
        };

        if (view === 'leads') {
            applyFilters(data.filters || {});
            if (data.pagination?.currentPage && this.state.leadsPagination) {
                this.state.leadsPagination.currentPage = Number(data.pagination.currentPage) || 1;
            }
        } else if (view === 'deals') {
            applyFilters(data.filters || {});
        } else if (view === 'lookups') {
            if (data.lookupSelectedGroup) this.state.lookupSelectedGroup = String(data.lookupSelectedGroup);
        }
    },

    buildHistoryState(params = {}) {
        const p = params || {};
        return {
            v: 1,
            ts: Date.now(),
            view: this.normalizeView(p.view || this.getCurrentView()),
            modal: p.modal || null,
            ui: p.ui || this.collectHistoryUiState(this.normalizeView(p.view || this.getCurrentView()))
        };
    },

    isValidHistoryState(state) {
        if (!state || typeof state !== 'object') return false;
        if (Number(state.v || 0) !== 1) return false;
        if (!this.getKnownViews().includes(String(state.view || ''))) return false;
        if (state.modal && typeof state.modal !== 'object') return false;
        return true;
    },

    syncHistoryState(state, options = {}) {
        if (!this.featureFlags.historyV1 || typeof history === 'undefined') return;
        const opts = options || {};
        const safe = this.isValidHistoryState(state) ? state : this.buildHistoryState(state || {});
        const replace = opts.replace === true;
        const hash = this.buildHistoryHash(safe.view);
        if (replace) history.replaceState(safe, '', hash);
        else history.pushState(safe, '', hash);
        this._lastHistoryState = safe;
        this.logTelemetry('history_state_sync', {
            replace,
            source: String(opts.source || ''),
            view: safe.view,
            modal: safe.modal?.id || ''
        }, 'debug');
    },

    replaceCurrentHistoryUiState(source = 'ui_sync') {
        if (!this.featureFlags.historyV1) return;
        if (this._isApplyingHistoryState) return;
        const view = this.normalizeView(this.getCurrentView());
        const current = this.isValidHistoryState(history.state)
            ? history.state
            : this.buildHistoryState({ view, modal: null });
        const next = this.buildHistoryState({
            view,
            modal: current.modal || null,
            ui: this.collectHistoryUiState(view)
        });
        this.syncHistoryState(next, { replace: true, source });
    },

    getDirtyFormConfig() {
        return [
            { key: 'lead_form', selector: '#lead-form' },
            { key: 'deal_form', selector: '#deal-form' },
            { key: 'activity_form', selector: '#modalActivity form' },
            { key: 'support_form', selector: '#modalSupport form' },
            { key: 'satisfaction_form', selector: '#modalSatisfaction form' }
        ];
    },

    bindDirtyFormTracking() {
        if (this._dirtyTrackingBound) return;
        this._dirtyTrackingBound = true;
        this._beforeUnloadHandler = (event) => {
            if (!this.isAnyFormDirty()) return;
            event.preventDefault();
            event.returnValue = '';
        };
        this.getDirtyFormConfig().forEach((cfg) => {
            this.dirtyForms[cfg.key] = false;
            const form = document.querySelector(cfg.selector);
            if (!form) return;
            const markDirty = () => this.setFormDirty(cfg.key, true);
            form.addEventListener('input', markDirty, true);
            form.addEventListener('change', markDirty, true);
        });
    },

    setFormDirty(key, dirty = true) {
        this.dirtyForms[String(key)] = !!dirty;
        this.syncBeforeUnloadGuard();
    },

    clearDirtyForm(key) {
        this.setFormDirty(key, false);
    },

    clearDirtyFormsByModal(id) {
        const map = {
            modalLead: 'lead_form',
            modalDeal: 'deal_form',
            modalActivity: 'activity_form',
            modalSupport: 'support_form',
            modalSatisfaction: 'satisfaction_form'
        };
        const key = map[String(id || '')];
        if (key) this.clearDirtyForm(key);
    },

    getDirtyKeyByModal(id) {
        const map = {
            modalLead: 'lead_form',
            modalDeal: 'deal_form',
            modalActivity: 'activity_form',
            modalSupport: 'support_form',
            modalSatisfaction: 'satisfaction_form'
        };
        return map[String(id || '')] || '';
    },

    isAnyFormDirty(keys = null) {
        if (Array.isArray(keys) && keys.length) {
            return keys.some((k) => !!this.dirtyForms[String(k)]);
        }
        return Object.values(this.dirtyForms || {}).some(Boolean);
    },

    syncBeforeUnloadGuard() {
        if (!this._beforeUnloadHandler) return;
        const shouldBind = this.isAnyFormDirty();
        if (shouldBind && !this._beforeUnloadBound) {
            window.addEventListener('beforeunload', this._beforeUnloadHandler);
            this._beforeUnloadBound = true;
        } else if (!shouldBind && this._beforeUnloadBound) {
            window.removeEventListener('beforeunload', this._beforeUnloadHandler);
            this._beforeUnloadBound = false;
        }
    },

    async confirmDiscardChanges(context = 'navigation', keys = null) {
        if (!this.isAnyFormDirty(keys)) return true;
        if (typeof Swal === 'undefined') {
            return window.confirm('تغییرات ذخیره‌نشده دارید. ادامه می‌دهید؟');
        }
        const res = await Swal.fire({
            title: 'تغییرات ذخیره‌نشده',
            text: 'در این بخش تغییرات ذخیره‌نشده دارید. آیا مایل به ادامه هستید؟',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'ادامه',
            cancelButtonText: 'انصراف'
        });
        this.logTelemetry('dirty_confirm', { context, confirmed: !!res.isConfirmed }, 'debug');
        return !!res.isConfirmed;
    },

    async navigateToView(view, options = {}) {
        const opts = options || {};
        let target = this.normalizeView(view);
        const currentBefore = this.normalizeView(this.getCurrentView());
        if (!this.canAccessView(target, { showWarning: !opts.silentDenied })) {
            target = 'dashboard';
            if (!this.canAccessView(target, { showWarning: !opts.silentDenied })) {
                return false;
            }
        }

        if (!opts.skipUnsavedCheck && !opts.fromPop) {
            const ok = await this.confirmDiscardChanges('navigate_view');
            if (!ok) return false;
        }

        if (opts.ui) this.applyHistoryUiState(target, opts.ui);
        const rendered = this.setView(target, { source: opts.source || 'navigate', silentDenied: true });
        if (!rendered) return false;

        if (this.featureFlags.historyV1 && !opts.skipHistory && !opts.fromPop && !this._isApplyingHistoryState) {
            const isViewChange = target !== currentBefore;
            const state = this.buildHistoryState({
                view: target,
                modal: null,
                ui: this.collectHistoryUiState(target)
            });
            this.syncHistoryState(state, {
                replace: opts.replace === true || !isViewChange,
                source: opts.source || (isViewChange ? 'navigate_push' : 'navigate_replace')
            });
        }
        return true;
    },

    async applyHistoryState(state, options = {}) {
        const opts = options || {};
        const safeState = this.isValidHistoryState(state) ? state : this.buildHistoryState({ view: 'dashboard', modal: null });
        if (this._isApplyingHistoryState) return;
        this._isApplyingHistoryState = true;
        try {
            const targetModalId = safeState.modal?.id || '';
            const currentModalId = this.getOpenModalId();

            if (currentModalId && currentModalId !== targetModalId) {
                this.closeModal(currentModalId, { source: 'history', skipHistory: true });
            }
            if (targetModalId && targetModalId !== currentModalId) {
                const entityId = String(safeState.modal?.entityId || '');
                if (targetModalId === 'modalProfile' && entityId) {
                    await this.openPersonProfile(entityId);
                } else if (targetModalId === 'modalDeal' && entityId) {
                    await this.openDealModal(entityId);
                } else {
                    this.openModal(targetModalId, {
                        source: 'history',
                        skipHistory: true,
                        entityId
                    });
                }
            }

            const currentView = this.normalizeView(this.getCurrentView());
            const targetView = this.normalizeView(safeState.view);
            if (targetView !== currentView) {
                await this.navigateToView(targetView, {
                    replace: true,
                    source: opts.source || 'history',
                    fromPop: true,
                    skipHistory: true,
                    skipUnsavedCheck: true,
                    silentDenied: true,
                    ui: safeState.ui || {}
                });
            } else if (safeState.ui) {
                this.applyHistoryUiState(targetView, safeState.ui);
            }

            if (safeState.ui && Number.isFinite(Number(safeState.ui.scrollY))) {
                const y = Number(safeState.ui.scrollY) || 0;
                setTimeout(() => window.scrollTo({ top: y, behavior: 'auto' }), 0);
            }
            this._lastHistoryState = safeState;
        } finally {
            this._isApplyingHistoryState = false;
        }
    },

    bindHistoryUiSync() {
        if (this._historyUiBound || !this.featureFlags.historyV1) return;
        this._historyUiBound = true;
        const ids = [
            'leads-search', 'leads-customer-code-search', 'leads-stage-filter', 'leads-owner-filter', 'leads-product-filter',
            'leads-customer-level-filter', 'leads-register-from', 'leads-register-to',
            'leads-activity-from', 'leads-activity-to',
            'deals-search', 'deals-customer-code-search', 'deals-pipeline-filter', 'deals-status-filter', 'deals-owner-filter',
            'deals-register-from', 'deals-register-to', 'deals-activity-from', 'deals-activity-to'
        ];
        ids.forEach((id) => {
            const el = document.getElementById(id);
            if (!el) return;
            const handler = () => {
                clearTimeout(this._historyUiSyncTimer);
                this._historyUiSyncTimer = setTimeout(() => this.replaceCurrentHistoryUiState('filter_change'), 180);
            };
            el.addEventListener('input', handler);
            el.addEventListener('change', handler);
        });

        window.addEventListener('scroll', () => {
            clearTimeout(this._scrollSyncTimer);
            this._scrollSyncTimer = setTimeout(() => this.replaceCurrentHistoryUiState('scroll_sync'), 200);
        }, { passive: true });
    },

    bindHistoryRouter() {
        if (this._historyBound || !this.featureFlags.historyV1) return;
        this._historyBound = true;
        try {
            if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
        } catch (_) {}

        window.addEventListener('popstate', async (event) => {
            if (!this.featureFlags.historyV1) return;
            const target = this.isValidHistoryState(event.state) ? event.state : this.buildHistoryState({ view: 'dashboard', modal: null });
            if (!this.isValidHistoryState(event.state)) {
                this.syncHistoryState(target, { replace: true, source: 'pop_invalid_fallback' });
            }
            const allow = await this.confirmDiscardChanges('history_popstate');
            if (!allow) {
                const restore = this._lastHistoryState || this.buildHistoryState({ view: this.getCurrentView(), modal: null });
                this.syncHistoryState(restore, { replace: true, source: 'pop_cancel_restore' });
                return;
            }
            await this.applyHistoryState(target, { source: 'popstate' });
        });
    },

    async bootstrapRouter() {
        if (!this.featureFlags.historyV1) {
            this.setView('dashboard', { source: 'bootstrap_no_history', silentDenied: true });
            return;
        }
        this.bindHistoryRouter();
        this.bindHistoryUiSync();
        const hashView = this.parseViewFromHash();
        const initial = hashView && this.canAccessView(hashView) ? hashView : 'dashboard';
        await this.navigateToView(initial, {
            replace: true,
            source: 'bootstrap',
            skipUnsavedCheck: true,
            silentDenied: true
        });
    },

    /**
     * بررسی وجود فعالیت برای معامله (دیتابیس داخلی)
     */
    async checkDealHasActivity(dealId) {
        const fallbackId =
            dealId ||
            this.state.currentDeal?.didar_deal_id ||
            this.state.currentDeal?.id ||
            this.state.currentDeal?.deal_id ||
            document.getElementById('deal-id')?.value ||
            '';
        if (!fallbackId) return false;
        try {
            const res = await this.req('has_deal_activity', { deal_id: fallbackId });
            const has = res.status === 'success' && !!res.has_activity;
            this.state.dealHasActivity = has;
            return has;
        } catch (e) {
            console.warn('has_deal_activity failed', e);
            this.state.dealHasActivity = false;
            return false;
        }
    },

    toggleDealActivityForm(hasActivity) {
        this.state.dealHasActivity = !!hasActivity;
        const formWrapper = document.getElementById('deal-activity-form-wrapper');
        const lockMsg = document.getElementById('deal-activity-locked-message');
        const submitBtn = document.getElementById('deal-activity-submit-btn');
        const form = document.getElementById('deal-activity-form');
        const fields = form ? form.querySelectorAll('input, select, textarea, button') : [];
        const chips = document.getElementById('deal-activity-service-chips');
        const svcSelect = document.getElementById('deal-activity-service-select');
        const svcAdd = document.getElementById('deal-activity-service-add');

        if (hasActivity) {
            lockMsg?.classList.remove('hidden');
            formWrapper?.classList.remove('hidden'); // نمایش برای مشاهده
            fields.forEach(el => {
                if (el === submitBtn) {
                    el.classList.add('hidden');
                    return;
                }
                if (el.tagName === 'BUTTON') {
                    el.disabled = true;
                    el.classList.add('cursor-not-allowed', 'opacity-60');
                } else {
                    if ('readOnly' in el) el.readOnly = true;
                    if ('disabled' in el) el.disabled = true;
                    el.classList.add('bg-gray-900', 'cursor-not-allowed');
                }
            });
            if (chips) chips.classList.add('pointer-events-none', 'opacity-70');
            if (svcSelect) {
                svcSelect.disabled = true;
                svcSelect.classList.add('cursor-not-allowed', 'bg-gray-900');
            }
            if (svcAdd) {
                svcAdd.disabled = true;
                svcAdd.classList.add('cursor-not-allowed', 'opacity-60');
            }
        } else {
            lockMsg?.classList.add('hidden');
            formWrapper?.classList.remove('hidden');
            fields.forEach(el => {
                if (el === submitBtn) {
                    el.classList.remove('hidden');
                    return;
                }
                if (el.tagName === 'BUTTON') {
                    el.disabled = false;
                    el.classList.remove('cursor-not-allowed', 'opacity-60');
                } else {
                    if ('readOnly' in el) el.readOnly = false;
                    if ('disabled' in el) el.disabled = false;
                    el.classList.remove('bg-gray-900', 'cursor-not-allowed');
                }
            });
            if (chips) chips.classList.remove('pointer-events-none', 'opacity-70');
            if (svcSelect) {
                svcSelect.disabled = false;
                svcSelect.classList.remove('cursor-not-allowed', 'bg-gray-900');
            }
            if (svcAdd) {
                svcAdd.disabled = false;
                svcAdd.classList.remove('cursor-not-allowed', 'opacity-60');
            }
        }
    },

    // UTILITIES
    toEnglishDigits(str = '') {
        const map = {
            '۰':'0','۱':'1','۲':'2','۳':'3','۴':'4','۵':'5','۶':'6','۷':'7','۸':'8','۹':'9',
            '٠':'0','١':'1','٢':'2','٣':'3','٤':'4','٥':'5','٦':'6','٧':'7','٨':'8','٩':'9'
        };
        return String(str).replace(/[۰-۹٠-٩]/g, d => map[d] || d);
    },

    normalizeDate(val = '') {
        const clean = this.toEnglishDigits(val).trim().replace(/-/g, '/');
        if (!clean) return '';
        if (/^\d{4}\/\d{1,2}\/\d{1,2}$/.test(clean)) return clean;
        return '';
    },

    quickPipelineChange(dealId, stage, status) {
        if (!stage) return;
        // برای مراحل دوره/پشتیبانی/اتمام/مرجوعی از مودال استفاده کن
        if (stage === 'برگزاری دوره' || stage === 'پشتیبانی' || stage === 'اتمام' || stage === 'مرجوعی') {
            // نیاز به اطلاعات معامله فعلی برای contact_id
            if (this.state.currentDeal && (this.state.currentDeal.didar_deal_id === dealId || String(this.state.currentDeal.id) === String(dealId))) {
                this.openCourseModal(this.state.currentDeal, stage);
            } else {
                // اگر در state نبود، از API بگیر
                this.req('get_deal', {deal_id: dealId}).then(res => {
                    if (res.status === 'success' && res.deal) {
                        this.openCourseModal(res.deal, stage);
                    }
                });
            }
            return;
        }
        // سایر مراحل سریع
        this.req('set_deal_pipeline_stage', {deal_id: dealId, pipeline_stage: stage}).then(res => {
            if (res.status === 'success') {
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'مرحله به‌روزرسانی شد', timer:1500, showConfirmButton:false});
                this.loadDeals();
            } else {
                Swal.fire('خطا', res.message || 'به‌روزرسانی مرحله انجام نشد', 'error');
            }
        });
    },

    openCourseModal(deal, presetStage = '') {
        if (!deal) return;
        const modal = document.getElementById('modalCourse');
        if (!modal) return;
        document.getElementById('course-deal-id').value = deal.didar_deal_id || deal.id || '';
        document.getElementById('course-contact-id').value = deal.local_person_id || deal.contact_didar_id || '';
        document.getElementById('course-virtual').checked = false;
        document.getElementById('course-online').checked = false;
        document.getElementById('course-access-created').checked = false;
        document.getElementById('course-link-sent').checked = false;
        document.getElementById('course-date-jalali').value = '';
        document.getElementById('course-refund').checked = false;
        document.getElementById('course-refund-reason').value = '';
        document.getElementById('refund-section').classList.add('hidden');
        const nextStageSelect = document.getElementById('course-next-stage');
        if (presetStage === 'پشتیبانی') nextStageSelect.value = 'پشتیبانی';
        else nextStageSelect.value = 'اتمام';
        this.openModal('modalCourse');
    },

    toggleRefundSection() {
        const refundCheckbox = document.getElementById('course-refund');
        const section = document.getElementById('refund-section');
        if (refundCheckbox && section) {
            if (refundCheckbox.checked) section.classList.remove('hidden');
            else section.classList.add('hidden');
        }
    },

    async submitCourseStatus(e) {
        e.preventDefault();
        const form = e.target;
        const dealId = form.deal_id.value;
        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله نامعتبر است', 'error');
            return;
        }
        let courseDate = form.course_date.value || '';
        let gregDate = '';
        if (courseDate) {
            gregDate = persianToGregorian(courseDate);
        }
        const data = {
            deal_id: dealId,
            contact_id: form.contact_id.value || '',
            course_virtual: form.course_virtual.checked ? '1' : '',
            course_online: form.course_online.checked ? '1' : '',
            access_created: form.access_created.checked ? '1' : '',
            link_sent: form.link_sent.checked ? '1' : '',
            course_date: gregDate,
            next_stage: form.next_stage.value,
            refund: form.refund.checked ? '1' : '',
            refund_reason: form.refund_reason.value || ''
        };
        const res = await this.req('set_course_status', data);
        if (res.status === 'success') {
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'به‌روز شد', timer:1500, showConfirmButton:false});
            this.closeModal('modalCourse');
            this.loadDeals();
        } else {
            Swal.fire('خطا', res.message || 'ذخیره نشد', 'error');
        }
    },

    async addProductPrompt() {
        const { value: name } = await Swal.fire({
            title: 'افزودن محصول',
            input: 'text',
            inputLabel: 'نام محصول',
            inputPlaceholder: 'نام محصول را وارد کنید',
            showCancelButton: true,
            confirmButtonText: 'ذخیره',
            cancelButtonText: 'لغو',
            inputValidator: (value) => {
                if (!value || !value.trim()) {
                    return 'نام محصول الزامی است';
                }
            }
        });
        if (!name) return;
        try {
            const res = await this.req('save_product', { name });
            if (res.status === 'success') {
                await this.loadProducts();
                Swal.fire('موفق', 'محصول ذخیره شد', 'success');
            } else {
                Swal.fire('خطا', res.message || 'ذخیره محصول ناموفق بود', 'error');
            }
        } catch (e) {
            Swal.fire('خطا', 'ذخیره محصول ناموفق بود', 'error');
        }
    },

    renderProductOptions(selectedIds = []) {
        const container = document.getElementById('deal-products-list');
        if (!container) return;
        const selectedSet = new Set((selectedIds || []).map(id => String(id)));
        if (!this.state.products || this.state.products.length === 0) {
            container.innerHTML = '<div class="text-xs text-gray-500">محصولی ثبت نشده است.</div>';
            return;
        }
        container.innerHTML = this.state.products.map(p => {
            const checked = selectedSet.has(String(p.id)) ? 'checked' : '';
            return `
                <label class="flex items-center gap-2 text-sm text-gray-300">
                    <input type="checkbox" name="product_ids[]" value="${p.id}" class="form-checkbox text-yellow-500" ${checked}>
                    <span>${p.name}</span>
                </label>
            `;
        }).join('');
    },

    renderDealTitleOptions(selectedValue = null) {
        const select = document.getElementById('deal-title');
        if (!select) return;
        const current = selectedValue !== null ? selectedValue : (select.value || '');
        const generated = Array.from(select.options || []).filter(opt => opt.dataset && opt.dataset.generated === '1');

        select.innerHTML = '<option value="">-- انتخاب خدمات قابل ارائه --</option>';
        const allowedServices = this.getAllowedServiceLookupItemsDetailed();
        if (allowedServices.length) {
            allowedServices.forEach(item => {
                const serviceValue = (item.value ?? item.title ?? '').trim ? (item.value ?? item.title ?? '').trim() : (item.value ?? item.title ?? '');
                if (!serviceValue) return;
                const linkedProductIds = this.getProductsLinkedToServiceLookupItem(item.id).map(p => String(p.id));
                const primaryProductId = linkedProductIds[0] || '';
                const opt = document.createElement('option');
                opt.value = serviceValue;
                opt.textContent = item.title || serviceValue;
                if (primaryProductId) opt.dataset.productId = primaryProductId;
                if (linkedProductIds.length) opt.dataset.productIds = linkedProductIds.join(',');
                if (item.id) opt.dataset.serviceLookupItemId = String(item.id);
                select.appendChild(opt);
            });
        } else {
            const products = Array.isArray(this.state.products) ? this.state.products : [];
            products.forEach(p => {
                if (!p || !p.name) return;
                const opt = document.createElement('option');
                opt.value = p.name;
                opt.textContent = p.name;
                opt.dataset.productId = p.id;
                select.appendChild(opt);
            });
        }

        generated.forEach(generatedOpt => {
            if (!generatedOpt.value) return;
            const exists = Array.from(select.options || []).some(o => o.value === generatedOpt.value);
            if (exists) return;
            const opt = document.createElement('option');
            opt.value = generatedOpt.value;
            opt.textContent = generatedOpt.textContent;
            opt.dataset.generated = '1';
            select.appendChild(opt);
        });

        if (current) {
            const hasCurrent = Array.from(select.options || []).some(o => o.value === current);
            if (hasCurrent) {
                select.value = current;
            }
        }
    },
    syncDealProductsFromSelectedService() {
        const titleSelect = document.getElementById('deal-title');
        const productsContainer = document.getElementById('deal-products-list');
        if (!titleSelect || !productsContainer) return;
        const selectedOption = titleSelect.selectedOptions?.[0];
        if (!selectedOption) return;
        const raw = String(selectedOption.dataset?.productIds || '').trim();
        if (raw !== '') {
            const ids = raw.split(',').map(v => v.trim()).filter(Boolean);
            this.renderProductOptions(ids);
            return;
        }
        if (selectedOption.dataset?.serviceLookupItemId) {
            // Service selected but no linked products configured: clear stale auto-selected values.
            this.renderProductOptions([]);
        }
    },

    parseRequestedServicesForDisplay(value) {
        if (!value) return [];
        if (Array.isArray(value)) {
            return value.map(v => String(v || '').trim()).filter(Boolean);
        }
        const raw = String(value).trim();
        if (!raw) return [];
        try {
            const parsed = JSON.parse(raw);
            if (Array.isArray(parsed)) {
                return parsed.map(v => String(v || '').trim()).filter(Boolean);
            }
        } catch (_) {}
        return raw
            .split(/[,،]/)
            .map(v => String(v || '').trim())
            .filter(Boolean);
    },

    getLeadProductsDisplayValue(lead) {
        if (!lead || typeof lead !== 'object') return '-';
        const customerProducts = String(lead.customer_products || '').trim();
        if (customerProducts) return customerProducts;
        const requested = this.parseRequestedServicesForDisplay(lead.requested_services);
        return requested.length ? requested.join('، ') : '-';
    },

    getLeadProductFilterEntries() {
        const entries = [];
        const seenServiceValues = new Set();
        const products = Array.isArray(this.state.products) ? this.state.products : [];
        const services = this.getAllowedServiceLookupItemsDetailed().filter(item => String(item.is_active ?? 1) === '1');

        services.forEach(service => {
            const serviceId = parseInt(service?.id ?? 0, 10);
            if (!serviceId) return;
            const linkedProducts = this.getProductsLinkedToServiceLookupItem(serviceId);
            if (!linkedProducts.length) return;
            const serviceValue = String(service.value ?? service.title ?? '').trim();
            const serviceLabel = String(service.title ?? service.value ?? '').trim();
            if (!serviceValue || seenServiceValues.has(serviceValue)) return;
            seenServiceValues.add(serviceValue);
            entries.push({
                value: `svc::${serviceValue}`,
                label: serviceLabel || serviceValue,
                sort_key: String(serviceLabel || serviceValue).toLowerCase()
            });
        });

        products.forEach(product => {
            const productId = parseInt(product?.id ?? 0, 10);
            if (!productId) return;
            const linkedServiceId = parseInt(product?.service_lookup_item_id ?? 0, 10);
            if (linkedServiceId > 0) return;
            const name = String(product?.name || '').trim();
            if (!name) return;
            entries.push({
                value: `prod::${productId}`,
                label: name,
                sort_key: String(name).toLowerCase()
            });
        });

        entries.sort((a, b) => String(a.sort_key).localeCompare(String(b.sort_key), 'fa'));
        return entries;
    },

    renderLeadProductFilter() {
        const select = document.getElementById('leads-product-filter');
        if (!select) return;
        const current = select.value;
        select.innerHTML = '<option value="">محصول / خدمت</option>';

        const appendOption = (value, label) => {
            const opt = document.createElement('option');
            opt.value = value;
            opt.textContent = label;
            select.appendChild(opt);
        };

        const entries = this.getLeadProductFilterEntries();
        entries.forEach(entry => appendOption(entry.value, entry.label));

        if (current) {
            select.value = current;
        }
    },

    initLeadFiltersDatePickers() {
        const ids = [
            'leads-register-from', 'leads-register-to', 'leads-activity-from', 'leads-activity-to',
            'deals-register-from', 'deals-register-to', 'deals-activity-from', 'deals-activity-to'
        ];
        const initInput = (input) => {
            if (!input) return;
            if (typeof $ === 'undefined' || typeof $.fn.persianDatepicker === 'undefined') {
                setTimeout(() => initInput(input), 200);
                return;
            }
            try {
                if ($(input).data('persianDatepicker')) {
                    $(input).persianDatepicker('destroy');
                }
            } catch (_) {}
            $(input).persianDatepicker({
                format: 'YYYY/MM/DD',
                initialValue: false,
                autoClose: true,
                observer: true
            });
        };
        ids.forEach(id => initInput(document.getElementById(id)));
    },

    async renderDealActivitiesInline(contactId) {
        const container = document.getElementById('deal-activities-inline');
        if (!container) return;
        container.innerHTML = '<div class="text-center text-gray-500 py-6">در حال بارگذاری...</div>';
        try {
            const dealId =
                document.getElementById('deal-id')?.value ||
                this.state.currentDeal?.didar_deal_id ||
                this.state.currentDeal?.id ||
                this.state.currentDeal?.deal_id ||
                '';

            if (dealId) {
                const res = await this.req('get_deal_activities', { deal_id: dealId });
                if (res.status === 'success') {
                    const acts = res.activities || [];
                    this.renderDidarActivities(acts, container);
                    this.updateDealActivitiesBadge(acts.length || 0);
                return;
            }
            }

            // برای معاملات جدید، فعالیت‌ها نمایش داده نشود
            container.innerHTML = '<div class="text-center text-gray-500 py-6">فعالیتی یافت نشد</div>';
            this.updateDealActivitiesBadge(0);
        } catch (e) {
            console.error('Error rendering inline activities:', e);
            container.innerHTML = '<div class="text-center text-red-400 py-6">خطا در دریافت فعالیت‌ها</div>';
            this.updateDealActivitiesBadge(0);
        }
    },
    leadSources: [
        {value:'instagram', label:'اینستاگرام'},
        {value:'telegram', label:'تلگرام'},
        {value:'book', label:'کتاب'},
        {value:'website', label:'سایت'},
        {value:'whatsapp', label:'واتساپ'},
        {value:'campaign', label:'کمپین'},
        {value:'event', label:'ایونت (event)'},
        {value:'support', label:'پشتیبانی'},
        {value:'incoming_call', label:'تماس ورودی'},
        {value:'visit', label:'مراجعه حضوری'},
        {value:'referral', label:'معرف'},
        {value:'other', label:'سایر'}
    ],
    leadSourceDetails: {
        instagram: [
            {value:'instagram_direct', label:'اینستاگرام - دایرکت'},
            {value:'instagram_box', label:'اینستاگرام - باکس'},
            {value:'instagram_highlight', label:'اینستاگرام - هایلایت'},
            {value:'instagram_channel', label:'اینستاگرام - کانال'},
            {value:'instagram_auto', label:'اینستاگرام - اتوحنفی'}
        ],
        whatsapp: [
            {value:'whatsapp_marketing', label:'واتساپ مارکتینگ'}
        ],
        website: [
            {value:'website_signup', label:'سایت - ثبت نام در سایت'},
            {value:'website_course_signup', label:'سایت - ثبت نام دوره'},
            {value:'website_failed_purchase', label:'سایت - خرید ناموفق'}
        ],
        campaign: [
            {value:'campaign_sms', label:'کمپین - پیامک'},
            {value:'campaign_divar', label:'کمپین - دیوار'},
            {value:'campaign_porsline', label:'کمپین - پرسلاین'},
            {value:'campaign_web', label:'کمپین وب'}
        ],
        event: [
            {value:'event_live', label:'ایونت - لایو'},
            {value:'event_meetup', label:'ایونت - دورهمی'},
            {value:'event_course', label:'ایونت - دوره'}
        ],
        referral: [
            {value:'referral_internal', label:'معرف - همکاران داخلی'},
            {value:'referral_external', label:'معرف - همکاران خارج از سازمان'},
            {value:'referral_teacher', label:'معرف - استاد مطلبی'},
            {value:'referral_customer', label:'معرف - مشتری قدیم'}
        ],
        default: [
            {value:'generic', label:'عمومی'}
        ]
    },
    contentTopics: [
        {value:'car_intelligence', label:'هوش خودرو'},
        {value:'in_person_inspection', label:'کارشناسی حضوری'},
        {value:'business', label:'کسب و کار'},
        {value:'mini_course', label:'مینی دوره هوش خودرو'},
        {value:'vip_channel', label:'کانال VIP'},
        {value:'budgeting', label:'بودجه بندی'},
        {value:'online_exhibition', label:'نمایشگاه آنلاین'},
        {value:'consulting', label:'مشاوره با استاد'},
        {value:'inspection_device', label:'دستگاه کارشناسی'},
        {value:'car_intelligence_test', label:'تست هوش خودرو'},
        {value:'financial_personality', label:'تست تیپ شخصیت مالی'},
        {value:'smart_trader', label:'معامله گر هوشمند'},
        {value:'detailing_training', label:'آموزش دیتیلینگ'},
        {value:'divar_car', label:'دیوار خودرو'},
        {value:'inspection_test', label:'تست کارشناسی'},
        {value:'book', label:'کتاب هوش خودرو'},
        {value:'cip', label:'CIP'}
    ],
        getLeadSourceLabel(value) {
        return this.getLookupLabel('lead_sources', value);
    },
    getLeadDetailLabel(value) {
        return this.getLookupLabel('lead_source_details', value);
    },
    getContentTopicLabel(value) {
        return this.getLookupLabel('content_topics', value);
    },
    escapeHtml(str) {
        if(!str) return '';
        return str.replace(/&/g, '&amp;')
                  .replace(/</g, '&lt;')
                  .replace(/>/g, '&gt;')
                  .replace(/"/g, '&quot;')
                  .replace(/'/g, '&#39;');
    },
    
    /**
     * Strip HTML tags and convert HTML entities to plain text
     * @param {string} html - HTML string to clean
     * @param {boolean} preserveLineBreaks - If true, convert <br> and <p> to line breaks
     * @returns {string} Clean text
     */
    stripHtml(html, preserveLineBreaks = true) {
        if (!html) return '';
        
        // Create a temporary div element to parse HTML
        const tempDiv = document.createElement('div');
        tempDiv.innerHTML = html;
        
        // Get text content (automatically strips HTML tags)
        let text = tempDiv.textContent || tempDiv.innerText || '';
        
        // Replace common HTML entities
        text = text.replace(/&nbsp;/g, ' ')
                   .replace(/&amp;/g, '&')
                   .replace(/&lt;/g, '<')
                   .replace(/&gt;/g, '>')
                   .replace(/&quot;/g, '"')
                   .replace(/&#39;/g, "'")
                   .replace(/&apos;/g, "'");
        
        // Clean up multiple spaces and line breaks
        if (preserveLineBreaks) {
            // Replace multiple spaces with single space, but preserve line breaks
            text = text.replace(/[ \t]+/g, ' ')
                       .replace(/\n\s*\n/g, '\n\n') // Preserve paragraph breaks
                       .trim();
        } else {
            // Replace all whitespace with single space
            text = text.replace(/\s+/g, ' ').trim();
        }
        
        return text;
    },

    isAdmin() {
        return (this.state.user?.role === 'admin');
    },
    isSystemRole() {
        return (this.state.user?.role === 'system');
    },
    isAccountant() {
        return (this.state.user?.role === 'accountant');
    },
    isAdminOrCrmSpecialist() {
        const role = this.state.user?.role;
        return role === 'admin' || role === 'crm_specialist' || role === 'system';
    },
    isAdminLikeForAnalytics() {
        return this.isAdmin() || this.isSystemRole();
    },
    getRbacDataScopeClient(resourceKey) {
        const key = String(resourceKey || '').trim();
        if (!key) return null;
        const scopes = this.state.rbacCurrentPermissions?.scopes;
        if (!Array.isArray(scopes)) return null;
        return scopes.find(s => String(s?.resource_key || '') === key) || null;
    },
    hasCrossUserScopeClient(resourceKey) {
        if (this.isAdminLikeForAnalytics()) return true;
        const scope = this.getRbacDataScopeClient(resourceKey);
        if (!scope) return false;
        const type = String(scope.scope_type || 'self');
        if (type === 'all') return true;
        if (type === 'selected_users') {
            return Array.isArray(scope.selected_user_ids) && scope.selected_user_ids.length > 0;
        }
        return false;
    },
    shouldLoadDidarUsersForFilters() {
        if (this.isAdminOrCrmSpecialist()) return true;
        return ['dashboard', 'leads', 'deals', 'kpi', 'performance'].some(key => this.hasCrossUserScopeClient(key));
    },
    async loadCurrentRbacPermissions(force = false) {
        if (!this.state.user || !this.state.user.role || this.state.user.role === 'guest') return;
        if (!force && this.state.rbacCurrentPermissionsLoaded) return;
        const res = await this.req('rbac_current_permissions');
        if (res.status !== 'success') return;
        this.state.rbacCurrentPermissionsLoaded = true;
        this.state.rbacCurrentPermissions = res;
    },
    canCurrentUserCapabilityClient(capabilityKey) {
        const role = this.state.user?.role || 'agent';
        if (role === 'admin') return true;
        const caps = this.state.rbacCurrentPermissions?.capabilities;
        if (Array.isArray(caps) && this.state.rbacCurrentPermissionsLoaded) {
            const aliases = {
                'menu.dashboard': 'dashboard.view',
                'menu.leads': 'leads.view',
                'menu.operation_manager': 'operation_manager.view',
                'menu.deals': 'deals.view',
                'menu.performance_dashboard': 'performance.view',
                'menu.kpi_dashboard': 'kpi.view',
                'action.leads.create': 'leads.create',
                'action.leads.edit': 'leads.edit',
                'action.leads.delete': 'leads.delete',
                'action.deals.create': 'deals.create',
                'action.deals.edit': 'deals.edit',
                'action.deals.delete': 'deals.delete',
                'action.payments.create': 'payments.create',
                'action.payments.edit': 'payments.edit',
                'action.payments.delete': 'payments.delete'
            };
            const candidates = new Set([capabilityKey]);
            if (aliases[capabilityKey]) candidates.add(aliases[capabilityKey]);
            Object.entries(aliases).forEach(([legacy, canonical]) => {
                if (canonical === capabilityKey) candidates.add(legacy);
            });
            if (['deals.status.edit', 'deals.pipeline.edit', 'deals.course.edit', 'deals.reactivate', 'deals.force_success', 'deals.reconcile'].includes(capabilityKey)) {
                candidates.add('deals.edit');
                candidates.add('action.deals.edit');
            }
            if (['leads.transfer_owner', 'leads.backfill_products'].includes(capabilityKey)) {
                candidates.add('leads.edit');
                candidates.add('action.leads.edit');
            }
            return Array.from(candidates).some(key => caps.includes(key));
        }
        const fallback = {
            accountant: ['menu.exports', 'export.catalog', 'export.dataset.payments'],
            crm_specialist: ['menu.dashboard', 'menu.leads', 'menu.operation_manager', 'operation_manager.view', 'operation_manager.preview', 'operation_manager.rules.view', 'lead_pools.view', 'lead_owner_history.view', 'menu.blataklif_leads', 'menu.deals', 'menu.legacy_deals', 'menu.referrals', 'menu.persons', 'menu.exports', 'menu.reminders', 'menu.settings', 'action.deals.edit', 'action.payments.create', 'action.payments.edit'],
            agent: ['menu.dashboard', 'menu.performance_dashboard', 'menu.leads', 'menu.blataklif_leads', 'menu.deals', 'menu.legacy_deals', 'menu.persons', 'menu.reminders', 'menu.settings', 'action.leads.edit', 'action.deals.create', 'action.deals.edit', 'action.payments.create', 'action.payments.edit'],
            system: ['menu.dashboard', 'menu.performance_dashboard', 'menu.leads', 'menu.operation_manager', 'operation_manager.view', 'operation_manager.preview', 'operation_manager.execute', 'operation_manager.rules.view', 'operation_manager.rules.manage', 'operation_manager.automation.run', 'lead_pools.view', 'lead_pools.manage', 'lead_owner_history.view', 'menu.blataklif_leads', 'menu.deals', 'menu.legacy_deals', 'menu.referrals', 'menu.persons', 'menu.reminders', 'menu.exports', 'menu.kpi_dashboard', 'action.leads.create', 'action.leads.edit', 'action.deals.create', 'action.deals.edit', 'action.payments.create', 'action.payments.edit', 'kpi.view', 'kpi.drilldown', 'kpi.deals.details', 'kpi.targets.manage', 'performance.view', 'performance.drilldown']
        };
        return (fallback[role] || []).includes(capabilityKey);
    },
    canActionClient(actionKey) {
        return this.canCurrentUserCapabilityClient(`action.${actionKey}`);
    },
    roleHasAnyCapabilityPrefix(prefix) {
        const caps = this.state.rbacCurrentPermissions?.capabilities;
        return Array.isArray(caps) && caps.some(c => String(c || '').startsWith(prefix));
    },
    canPerformanceFeatureClient(capabilityKey) {
        const globallyDisabledPerfCaps = [
            'performance.chart.deal_status',
            'performance.chart.conversion_funnel'
        ];
        if (globallyDisabledPerfCaps.includes(capabilityKey)) {
            return false;
        }
        const nonAdminHiddenPerfCaps = [
            'performance.table.activity_summary',
            'performance.table.activity_type_details'
        ];
        if (!this.isAdminLikeForAnalytics() && nonAdminHiddenPerfCaps.includes(capabilityKey)) {
            return false;
        }
        if (this.isAdminLikeForAnalytics()) return true;
        const hasPerfSpecificCaps =
            this.roleHasAnyCapabilityPrefix('performance.') ||
            this.roleHasAnyCapabilityPrefix('performance.chart.') ||
            this.roleHasAnyCapabilityPrefix('performance.table.') ||
            this.roleHasAnyCapabilityPrefix('performance.card.');
        if (!hasPerfSpecificCaps) {
            return this.canCurrentUserCapabilityClient('menu.performance_dashboard');
        }
        return this.canCurrentUserCapabilityClient(capabilityKey);
    },
    canKpiFeatureClient(capabilityKey) {
        if (this.isAdminLikeForAnalytics()) return true;
        const hasKpiSpecificCaps = this.roleHasAnyCapabilityPrefix('kpi.');
        if (!hasKpiSpecificCaps) {
            return this.canCurrentUserCapabilityClient('menu.kpi_dashboard');
        }
        return this.canCurrentUserCapabilityClient(capabilityKey);
    },
    applyRbacActionVisibility() {
        const setHidden = (id, hidden) => {
            const el = document.getElementById(id);
            if (!el) return;
            el.classList.toggle('hidden', !!hidden);
            if ('disabled' in el) el.disabled = !!hidden;
        };
        setHidden('leads-add-btn', !this.canActionClient('leads.create'));
        setHidden('profile-add-deal-btn', !this.canActionClient('deals.create'));

        const leadEditBtn = document.getElementById('lead-edit-btn');
        if (leadEditBtn) leadEditBtn.classList.toggle('hidden', !this.canActionClient('leads.edit'));

        const leadDeleteBtn = document.getElementById('lead-delete-btn');
        if (leadDeleteBtn) {
            const canDeleteLead = this.canActionClient('leads.delete');
            leadDeleteBtn.classList.toggle('hidden', !canDeleteLead);
            leadDeleteBtn.disabled = !canDeleteLead;
        }

        const dealInfoSubmitBtn = document.getElementById('deal-info-submit-btn');
        if (dealInfoSubmitBtn) {
            const canSaveDeal = this.canActionClient('deals.create') || this.canActionClient('deals.edit');
            dealInfoSubmitBtn.classList.toggle('hidden', !canSaveDeal);
            dealInfoSubmitBtn.disabled = !canSaveDeal;
        }
        const dealPaymentsSubmitBtn = document.getElementById('deal-payments-submit-btn');
        if (dealPaymentsSubmitBtn) {
            const canSavePaymentsViaDeal = this.canActionClient('payments.create') || this.canActionClient('payments.edit') || this.canActionClient('payments.delete');
            dealPaymentsSubmitBtn.classList.toggle('hidden', !canSavePaymentsViaDeal);
            dealPaymentsSubmitBtn.disabled = !canSavePaymentsViaDeal;
        }
        const addPaymentBtn = document.getElementById('deal-add-payment-btn');
        if (addPaymentBtn) {
            const canCreatePayment = this.canActionClient('payments.create');
            addPaymentBtn.classList.toggle('hidden', !canCreatePayment);
            addPaymentBtn.disabled = !canCreatePayment;
        }
    },
    applyRbacMenuVisibility() {
        const menuCapMap = {
            'menu-dashboard': 'menu.dashboard',
            'menu-performance-dashboard': 'menu.performance_dashboard',
            'menu-leads': 'menu.leads',
            'menu-operation-manager': 'menu.operation_manager',
            'menu-blataklif-leads': 'menu.blataklif_leads',
            'menu-deals': 'menu.deals',
            'menu-legacy-deals': 'menu.legacy_deals',
            'menu-referrals': 'menu.referrals',
            'menu-persons': 'menu.persons',
            'menu-reminders': 'menu.reminders',
            'menu-settings': 'menu.settings',
            'menu-lookups': 'menu.lookups',
            'menu-exports': 'menu.exports',
            'menu-audit-logs': 'menu.audit_logs',
            'menu-users': 'menu.users',
            'menu-kpi': 'menu.kpi_dashboard'
        };
        Object.entries(menuCapMap).forEach(([menuId, cap]) => {
            const el = document.getElementById(menuId);
            if (!el) return;
            const allowed = this.canCurrentUserCapabilityClient(cap);
            el.classList.toggle('hidden', !allowed);
        });

        const adminSection = document.getElementById('admin-section');
        if (adminSection) {
            const hasVisibleItem = Array.from(adminSection.querySelectorAll('.sidebar-item'))
                .some(el => !el.classList.contains('hidden'));
            adminSection.classList.toggle('hidden', !hasVisibleItem);
        }
        const kpiSection = document.getElementById('kpi-menu-section');
        if (kpiSection) {
            const hasVisibleItem = Array.from(kpiSection.querySelectorAll('.sidebar-item'))
                .some(el => !el.classList.contains('hidden'));
            kpiSection.classList.toggle('hidden', !hasVisibleItem);
        }
    },
    isLeadOwner(lead) {
        if(!lead || !lead.owner_didar_id) return false;
        return this.state.user?.didar_id && this.state.user.didar_id === lead.owner_didar_id;
    },
    async req(action, data = {}, options = {}) {
        const opts = options || {};
        const requestKey = String(opts.requestKey || '').trim();
        const externalSignal = opts.signal || null;
        const silentAbort = opts.silentAbort !== false;
        const shouldCancelByKey = this.featureFlags.modalFreshnessV1 && !!requestKey;
        const reqId = ++this._requestSeq;

        const fd = new FormData();
        for(let k in data) {
            if(data[k] !== null && data[k] !== undefined) {
                fd.append(k, data[k]);
            }
        }
        this.logTelemetry('request_start', { reqId, action, requestKey }, 'debug');
        console.log(`[DEBUG] API Request: ${action}`, data);
        try {
            const timeoutController = new AbortController();
            if (shouldCancelByKey) {
                this.cancelInFlightRequest(requestKey);
            }
            const keyController = shouldCancelByKey ? new AbortController() : null;
            if (shouldCancelByKey && keyController) {
                this._inFlightControllers[requestKey] = keyController;
            }
            const signal = this.composeAbortSignal([timeoutController.signal, keyController?.signal, externalSignal]);
            const timeoutId = setTimeout(() => timeoutController.abort(), 180000); // 3 minutes timeout
             
            const r = await fetch('?api=' + action, {
                method:'POST', 
                body:fd,
                signal
            });
             
            clearTimeout(timeoutId);
            if (shouldCancelByKey && this._inFlightControllers[requestKey] === keyController) {
                delete this._inFlightControllers[requestKey];
            }
             
            if (!r.ok) {
                // Try to parse detailed error from server
                try {
                    const errRes = await r.json();
                    if (errRes && errRes.message) {
                        console.log(`[DEBUG] API Error Response: ${action}`, errRes);
                        return { status: 'error', message: errRes.message };
                    }
                } catch(_) {}
                throw new Error(`HTTP ${r.status}: ${r.statusText}`);
            }
             
            const response = await r.json();
            this.logTelemetry('request_success', { reqId, action, requestKey, status: response?.status || '' }, 'debug');
            console.log(`[DEBUG] API Response: ${action}`, response);
            return response;
        } catch(e) { 
            if (shouldCancelByKey) {
                const c = this._inFlightControllers[requestKey];
                if (c && c.signal && c.signal.aborted) {
                    delete this._inFlightControllers[requestKey];
                }
            }
            if (e && e.name === 'AbortError') {
                this.logTelemetry('request_abort', { reqId, action, requestKey }, 'debug');
                if (silentAbort) {
                    return { status: 'aborted', aborted: true, message: 'REQUEST_ABORTED' };
                }
            }
            console.error(`[DEBUG] API Error for ${action}:`, e);
             
            // Check if it's a network error or timeout
            if (e.name === 'AbortError' || e.message.includes('fetch')) {
                throw new Error('NETWORK_ERROR'); // Throw to be caught by setupSync
            }
            
            return {status:'error', message: 'خطا در ارتباط با سرور: ' + e.message}; 
        }
    },

    toggleSidebar() {
        const sidebar = document.getElementById('main-sidebar');
        const backdrop = document.getElementById('sidebar-backdrop');
        if (!sidebar || !backdrop) return;
        
        const isClosed = sidebar.classList.contains('translate-x-full');
        if (isClosed) {
            sidebar.classList.remove('translate-x-full');
            sidebar.classList.add('translate-x-0');
            backdrop.classList.remove('hidden');
        } else {
            sidebar.classList.add('translate-x-full');
            sidebar.classList.remove('translate-x-0');
            backdrop.classList.add('hidden');
        }
    },

    closeSidebar() {
        const sidebar = document.getElementById('main-sidebar');
        const backdrop = document.getElementById('sidebar-backdrop');
        if (!sidebar || !backdrop) return;
        
        // Only if it's open (mobile)
        if (!sidebar.classList.contains('translate-x-full')) {
            sidebar.classList.add('translate-x-full');
            sidebar.classList.remove('translate-x-0');
            backdrop.classList.add('hidden');
        }
    },

    initTheme() {
        const theme = localStorage.getItem('theme') || 'dark';
        const body = document.body;
        const icon = document.getElementById('theme-icon');
        
        if (theme === 'light') {
            body.classList.add('light-theme');
            if (icon) {
                icon.classList.remove('fa-sun');
                icon.classList.add('fa-moon');
            }
        } else {
            body.classList.remove('light-theme');
            if (icon) {
                icon.classList.remove('fa-moon');
                icon.classList.add('fa-sun');
            }
        }
    },

    toggleTheme() {
        const body = document.body;
        const icon = document.getElementById('theme-icon');
        const isLight = body.classList.contains('light-theme');
        
        if (isLight) {
            body.classList.remove('light-theme');
            localStorage.setItem('theme', 'dark');
            if (icon) {
                icon.classList.remove('fa-moon');
                icon.classList.add('fa-sun');
            }
        } else {
            body.classList.add('light-theme');
            localStorage.setItem('theme', 'light');
            if (icon) {
                icon.classList.remove('fa-sun');
                icon.classList.add('fa-moon');
            }
        }
    },

    async init() {
        if (typeof Logger !== 'undefined') Logger.init();
        this.initFeatureFlags();
        this.bindGlobalTelemetryHandlers();
        console.log('[DEBUG] App init started');
        console.log('[DEBUG] Current user state:', this.state.user);
        console.log('[DEBUG] isAdmin:', this.isAdmin());
        console.log('[DEBUG] isAdminOrCrmSpecialist:', this.isAdminOrCrmSpecialist());
        
        await this.loadLookups();
        await this.loadCurrentRbacPermissions();
        this.applyLookupsToStaticSelects();
        this.applyRbacMenuVisibility();
        this.applyRbacActionVisibility();
        this.initMoneyInputFormatting();
        this.initTheme();
        this.loadActivityTypes();
        this.applyDidarUiDisabledState();
        this.bindDirtyFormTracking();
        
        if (this.shouldLoadDidarUsersForFilters()) {
            console.log('[DEBUG] User is eligible for cross-user filters, loading Didar users...');
            await this.loadDidarUsers();
        } else {
            console.log('[DEBUG] User is not eligible for cross-user filters, skipping Didar users');
        }
        
        // Bootstrap view router (history-aware when feature flag is enabled)
        await this.bootstrapRouter();
        if (this.isAdmin()) {
            this.loadReferrals();
        }
        
        console.log('[DEBUG] App init complete');
        
        // Initialize notifications
        this.loadNotifications();
        this.startNotificationPolling();

        // Request notification permission after a short delay
        setTimeout(() => {
            if ('Notification' in window) {
                console.log('Notification support detected, permission:', Notification.permission);

                // Check if we're on HTTPS (required for notifications in some browsers)
                if (location.protocol !== 'https:' && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') {
                    console.log('⚠️ Desktop notifications require HTTPS. Current protocol:', location.protocol);
                }

                if (Notification.permission === 'default') {
                    console.log('Requesting notification permission on startup');
                    this.requestNotificationPermission();
                } else if (Notification.permission === 'denied') {
                    console.log('Notifications are blocked by user');
                } else {
                    console.log('Notifications already granted');
                }
            } else {
                console.log('This browser does not support desktop notifications');
            }
        }, 2000);

        // Load notification settings (default to enabled)
        this.notificationSoundEnabled = localStorage.getItem('notificationSoundEnabled') !== 'false';
        this.notificationPopupEnabled = localStorage.getItem('notificationPopupEnabled') !== 'false';
        this.notificationBrowserEnabled = localStorage.getItem('notificationBrowserEnabled') !== 'false';

        // Update sound button
        this.updateSoundButton();

        // Initialize reminder offset calculators
        this.initReminderOffsetListeners();

        console.log('Notification settings loaded:', {
            sound: this.notificationSoundEnabled,
            popup: this.notificationPopupEnabled,
            browser: this.notificationBrowserEnabled
        });
        
        // Close notifications dropdown when clicking outside
        document.addEventListener('click', (e) => {
            const container = document.getElementById('notifications-container');
            const dropdown = document.getElementById('notifications-dropdown');
            if (container && dropdown && !container.contains(e.target)) {
                dropdown.classList.add('hidden');
            }
        });
    },

    initMoneyInputFormatting() {
        if (this.state._moneyInputFormattingInitialized) return;
        this.state._moneyInputFormattingInitialized = true;

        document.addEventListener('input', (e) => {
            const el = e.target;
            if (!el || !(el instanceof HTMLElement)) return;
            if (!el.classList || !el.classList.contains('money-input')) return;
            formatMoneyInput(el);
        });
    },
    
    // SETUP
    async setupInit(e) {
        e.preventDefault();
        const apiKey = document.getElementById('setup-api-key').value;
        Swal.showLoading();
        const res = await this.req('setup_init', {api_key: apiKey});
        Swal.close();
        
        if(res.status === 'success') {
            document.getElementById('setup-step1').classList.add('hidden');
            const step2 = document.getElementById('setup-step2');
            step2.classList.remove('hidden');
            
            const list = document.getElementById('setup-users-list');
            list.innerHTML = '';
            res.users.forEach(u => {
                const div = document.createElement('div');
                div.className = 'p-3 bg-gray-800 rounded cursor-pointer hover:bg-gray-700 transition';
                div.innerHTML = `
                    <input type="radio" name="admin_user" value="${u.Id || u.UserId}" id="user-${u.Id || u.UserId}" class="mr-2">
                    <label for="user-${u.Id || u.UserId}" class="cursor-pointer">${u.DisplayName} (${u.UserName})</label>
                `;
                list.appendChild(div);
            });
            
            window.setupApiKey = apiKey;
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    async setupComplete() {
        const selected = document.querySelector('input[name="admin_user"]:checked');
        if(!selected) {
            Swal.fire('خطا', 'لطفاً کاربر ادمین را انتخاب کنید', 'error');
            return;
        }
        
        Swal.showLoading();
        const res = await this.req('setup_complete', {
            api_key: window.setupApiKey,
            admin_didar_id: selected.value
        });
        
        if(res.status === 'success') {
            document.getElementById('setup-step2').classList.add('hidden');
            document.getElementById('setup-step3').classList.remove('hidden');
            
            // Check if there's existing data to resume
            const statusRes = await this.req('get_sync_status', {});
            if(statusRes.status === 'success' && statusRes.counts.deals > 0) {
                const resumeBtn = document.getElementById('setup-resume-btn');
                resumeBtn.classList.remove('hidden');
                resumeBtn.innerHTML = `<i class="fa-solid fa-play ml-1"></i> ادامه همگام‌سازی (${statusRes.counts.deals} معامله sync شده)`;
            } else {
                this.setupSync();
            }
        } else {
            Swal.close();
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    async setupSync(step = 'users', from = 0) {
        try {
            const res = await this.req('setup_sync', {step, from});
            
        if(res.status === 'success') {
                document.getElementById('setup-progress-bar').style.width = res.progress + '%';
                document.getElementById('setup-progress-text').textContent = res.message;
                
                // نمایش پیشرفت دقیق‌تر
                let progressDetail = `مرحله: ${step} | پیشرفت: ${res.progress}%`;
                if (res.total_count && res.from !== undefined) {
                    const synced = Math.min(res.from + (res.limit || 500), res.total_count);
                    progressDetail += ` | ${synced} از ${res.total_count}`;
                }
                document.getElementById('setup-progress-detail').textContent = progressDetail;
                
                // Hide resume button during successful sync
                document.getElementById('setup-resume-btn').classList.add('hidden');
                
                if(res.next_step === 'complete') {
                    // Calculate virtual stages in batch after sync completes
                    Swal.fire({icon:'info', title:'در حال محاسبه مراحل مجازی...', text:'لطفاً صبر کنید', allowOutsideClick: false});
                    try {
                        // virtual stages deprecated
                    } catch (e) {
                        console.error('Error updating virtual stages:', e);
                    }
                    Swal.fire({icon:'success', title:'نصب تکمیل شد', text:'در حال انتقال...', timer:2000});
                    setTimeout(() => location.reload(), 2000);
                } else {
                    // Fix: محاسبه nextFrom برای جلوگیری از لوپ بی‌نهایت
                    let nextFrom = 0;
                    if(res.next_step === step) {
                        // اگر همان step است، from را افزایش بده
                        nextFrom = (res.from || 0) + (res.limit || 500);
                    }
                    
                    setTimeout(() => this.setupSync(res.next_step, nextFrom), 500);
                }
            } else {
                // Show resume button on API error
                document.getElementById('setup-resume-btn').classList.remove('hidden');
                Swal.fire('خطا', res.message, 'error');
            }
        } catch (error) {
            // Network error handling
            console.error('Network error in setupSync:', error);
            document.getElementById('setup-resume-btn').classList.remove('hidden');
            
            // Check if it's a network error
            const isNetworkError = error.message === 'NETWORK_ERROR' || 
                                   error.message.includes('fetch') || 
                                   error.message.includes('network') ||
                                   error.message.includes('Failed to fetch');
            
            Swal.fire({
                icon: 'warning',
                title: 'خطا در ارتباط',
                html: isNetworkError 
                    ? 'ارتباط با سرور قطع شد.<br>داده‌های sync شده حفظ شده‌اند.<br>می‌توانید با دکمه "ادامه همگام‌سازی" از همانجا ادامه دهید.'
                    : 'خطا در پردازش درخواست.<br>لطفاً دوباره تلاش کنید.',
                showCancelButton: true,
                confirmButtonText: 'ادامه همگام‌سازی',
                cancelButtonText: 'بستن',
                allowOutsideClick: false
            }).then((result) => {
                if (result.isConfirmed) {
                    this.resumeSync();
                }
            });
        }
    },
    
    async resumeSync() {
        try {
            const res = await this.req('resume_sync', {});
            if (res.status === 'success') {
                this.setupSync(res.step || 'users', res.from || 0);
            } else {
                Swal.fire('خطا', res.message || 'خطا در ادامه همگام‌سازی', 'error');
            }
        } catch (error) {
            console.error('Error in resumeSync:', error);
            Swal.fire('خطا', 'خطا در ادامه همگام‌سازی: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    // LOGIN
    async login(e) {
        e.preventDefault();
        const username = document.getElementById('login-user-select').value;
        const password = document.getElementById('login-password').value;
        
        if (!username || !password) {
            Swal.fire('خطا', 'لطفاً نام کاربری و رمز عبور را وارد کنید', 'error');
            return;
        }
        
        const res = await this.req('login', {username: username, password: password});
        if(res.status === 'success') {
            location.reload();
        } else {
            Swal.fire('خطا', res.message || 'نام کاربری یا رمز عبور اشتباه است', 'error');
        }
    },

    refresh() {
        const active = document.querySelector('.sidebar-item.active');
        if(active) {
            const view = active.id.replace('menu-', '');
            if(view === 'dashboard') this.loadDashboard();
            if(view === 'leads') this.loadLeads();
            if(view === 'deals') this.loadDeals();
            if(view === 'referrals') this.loadReferrals();
            if(view === 'persons') this.loadPersons();
            if(view === 'settings') this.loadSettings();
        }
    },
    
    // SETTINGS
    loadSettings() {
        // Settings view is static, no need to load data
        // Just make sure forms are reset
        document.querySelectorAll('#view-settings form').forEach((form) => form.reset());
        const userIdInput = document.getElementById('settings-change-password-user-id');
        if (userIdInput) userIdInput.value = window.CURRENT_USER_ID || 0;
        this.switchSettingsSection('account');
        this.setSettingsCardSaveState('didar-write', 'idle');
        this.setSettingsCardSaveState('external-api', 'idle');
        this.setSettingsCardSaveState('service-governance', 'idle');
        this.setSettingsCardSaveState('auto-migration', 'idle');
        this.setSettingsCardSaveState('feature-flags', 'idle');
        if (this.isAdminLikeForAnalytics() || this.hasCrossUserScopeClient('kpi')) {
            this.loadDidarWriteSettings();
            if (this.isAdmin()) {
                this.loadExternalApiSettings();
                this.loadServiceGovernanceSettings();
            }
            this.loadAutoMigrationSettings();
            this.loadFeatureFlagSettings();
        }
    },

    switchSettingsSection(sectionKey = 'account') {
        const target = String(sectionKey || 'account');
        const sections = ['account', 'integration', 'system'];
        sections.forEach((key) => {
            const el = document.getElementById(`settings-section-${key}`);
            if (!el) return;
            el.classList.toggle('hidden', key !== target);
        });
        document.querySelectorAll('[data-settings-section-btn]').forEach((btn) => {
            const key = String(btn.getAttribute('data-settings-section-btn') || '');
            const active = key === target;
            btn.classList.toggle('bg-yellow-500/20', active);
            btn.classList.toggle('border-yellow-500/60', active);
            btn.classList.toggle('text-yellow-300', active);
            btn.classList.toggle('bg-gray-800/60', !active);
            btn.classList.toggle('border-gray-700', !active);
            btn.classList.toggle('text-gray-200', !active);
        });
    },

    setSettingsCardSaveState(cardKey, state = 'idle', message = '') {
        const badge = document.getElementById(`settings-badge-${cardKey}`);
        if (!badge) return;
        const labels = {
            idle: 'بدون تغییر',
            pending: 'در حال ذخیره',
            saved: 'ذخیره شد',
            failed: 'خطا در ذخیره'
        };
        const classes = {
            idle: ['border-gray-700', 'text-gray-300', 'bg-gray-800/70'],
            pending: ['border-yellow-600/60', 'text-yellow-300', 'bg-yellow-900/20'],
            saved: ['border-green-600/60', 'text-green-300', 'bg-green-900/20'],
            failed: ['border-red-600/60', 'text-red-300', 'bg-red-900/20']
        };
        const all = [
            'border-gray-700', 'text-gray-300', 'bg-gray-800/70',
            'border-yellow-600/60', 'text-yellow-300', 'bg-yellow-900/20',
            'border-green-600/60', 'text-green-300', 'bg-green-900/20',
            'border-red-600/60', 'text-red-300', 'bg-red-900/20'
        ];
        all.forEach((cn) => badge.classList.remove(cn));
        (classes[state] || classes.idle).forEach((cn) => badge.classList.add(cn));
        badge.textContent = message || labels[state] || labels.idle;
    },

    loadFeatureFlagSettings() {
        if (!this.isAdmin()) return;
        const historyCheckbox = document.getElementById('settings-feature-history-enabled');
        const freshnessCheckbox = document.getElementById('settings-feature-freshness-enabled');
        const status = document.getElementById('settings-feature-flags-status');
        if (historyCheckbox) historyCheckbox.checked = this.featureFlags.historyV1 === true;
        if (freshnessCheckbox) freshnessCheckbox.checked = this.featureFlags.modalFreshnessV1 !== false;
        if (status) {
            status.textContent = `مسیریاب تاریخچه: ${this.featureFlags.historyV1 ? 'فعال' : 'غیرفعال'} | محافظ تازگی مودال: ${this.featureFlags.modalFreshnessV1 ? 'فعال' : 'غیرفعال'}`;
        }
    },

    async saveFeatureFlagSettings() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        this.setSettingsCardSaveState('feature-flags', 'pending');
        try {
            const historyCheckbox = document.getElementById('settings-feature-history-enabled');
            const freshnessCheckbox = document.getElementById('settings-feature-freshness-enabled');
            const nextHistory = !!historyCheckbox?.checked;
            const nextFreshness = !!freshnessCheckbox?.checked;
            const prevHistory = this.featureFlags.historyV1 === true;
            const prevFreshness = this.featureFlags.modalFreshnessV1 !== false;

            this.featureFlags.historyV1 = nextHistory;
            this.featureFlags.modalFreshnessV1 = nextFreshness;
            window.FEATURE_SPA_HISTORY_V1 = this.featureFlags.historyV1;
            window.FEATURE_MODAL_FRESHNESS_V1 = this.featureFlags.modalFreshnessV1;
            const persistResult = this.persistFeatureFlags();
            if (!persistResult.ok) {
                this.featureFlags.historyV1 = prevHistory;
                this.featureFlags.modalFreshnessV1 = prevFreshness;
                window.FEATURE_SPA_HISTORY_V1 = prevHistory;
                window.FEATURE_MODAL_FRESHNESS_V1 = prevFreshness;
                this.setSettingsCardSaveState('feature-flags', 'failed');
                Swal.fire('خطا', 'ذخیره در مرورگر ممکن نشد. احتمالاً دسترسی به localStorage مسدود است.', 'error');
                return;
            }

            if (!prevHistory && nextHistory) {
                this.bindHistoryRouter();
                this.bindHistoryUiSync();
                this.syncHistoryState(this.buildHistoryState({
                    view: this.normalizeView(this.getCurrentView()),
                    modal: null,
                    ui: this.collectHistoryUiState(this.normalizeView(this.getCurrentView()))
                }), { replace: true, source: 'settings_enable_history' });
            }

            this.logTelemetry('flags_updated_from_settings', {
                history_v1_prev: prevHistory,
                history_v1_next: nextHistory,
                modal_freshness_prev: prevFreshness,
                modal_freshness_next: nextFreshness
            });

            this.loadFeatureFlagSettings();
            this.setSettingsCardSaveState('feature-flags', 'saved');
            Swal.fire({
                toast: true,
                position: 'bottom-end',
                icon: 'success',
                title: 'فلگ‌های سیستمی ذخیره شد',
                showConfirmButton: false,
                timer: 2200
            });
        } catch (error) {
            this.setSettingsCardSaveState('feature-flags', 'failed');
            Swal.fire('خطا', 'خطا در ذخیره فلگ‌های سیستمی', 'error');
        }
    },

    async loadDidarWriteSettings() {
        try {
            const res = await this.req('get_didar_write_settings');
            if (res.status !== 'success') return;
            const enabled = String(res.didar_write_enabled || '1') === '1';
            const checkbox = document.getElementById('settings-didar-write-enabled');
            const status = document.getElementById('settings-didar-write-status');
            if (checkbox) checkbox.checked = enabled;
            if (status) {
                status.textContent = enabled
                    ? 'ارسال به دیدار فعال است.'
                    : 'ارسال به دیدار غیرفعال است.';
            }
        } catch (e) {
            console.warn('Failed to load Didar write settings', e);
        }
    },

    async saveDidarWriteSettings() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        this.setSettingsCardSaveState('didar-write', 'pending');
        try {
            const checkbox = document.getElementById('settings-didar-write-enabled');
            const enabled = checkbox ? (checkbox.checked ? '1' : '0') : '1';
            const res = await this.req('save_didar_write_settings', { didar_write_enabled: enabled });
            if (res.status === 'success') {
                const status = document.getElementById('settings-didar-write-status');
                if (status) {
                    status.textContent = enabled === '1'
                        ? 'ارسال به دیدار فعال است.'
                        : 'ارسال به دیدار غیرفعال است.';
                }
                this.setSettingsCardSaveState('didar-write', 'saved');
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'تنظیمات ذخیره شد', showConfirmButton:false, timer:2000});
            } else {
                this.setSettingsCardSaveState('didar-write', 'failed');
                Swal.fire('خطا', res.message || 'ذخیره تنظیمات با خطا مواجه شد', 'error');
            }
        } catch (error) {
            this.setSettingsCardSaveState('didar-write', 'failed');
            Swal.fire('خطا', 'خطا در ارتباط با سرور هنگام ذخیره تنظیمات', 'error');
        }
    },

    async loadExternalApiSettings() {
        if (!this.isAdmin()) return;
        try {
            const res = await this.req('get_external_api_settings');
            if (res.status !== 'success') return;
            const enabled = String(res.external_api_enabled || '0') === '1';
            const enabledEl = document.getElementById('settings-external-api-enabled');
            const endpointEl = document.getElementById('settings-external-api-endpoint');
            const maskedEl = document.getElementById('settings-external-api-token-masked');
            const statusEl = document.getElementById('settings-external-api-status');
            const newTokenBox = document.getElementById('settings-external-api-new-token-box');
            const passwordEl = document.getElementById('settings-external-api-admin-password');
            if (enabledEl) enabledEl.checked = enabled;
            if (endpointEl) endpointEl.value = res.endpoint || '/?api=get_lead_assignment';
            if (maskedEl) maskedEl.value = res.token_masked || '';
            if (newTokenBox) newTokenBox.classList.add('hidden');
            if (passwordEl) passwordEl.value = '';
            if (statusEl) {
                statusEl.textContent = res.token_configured
                    ? (enabled ? 'API فعال است و کلید تنظیم شده است.' : 'کلید تنظیم شده ولی API غیرفعال است.')
                    : 'هنوز کلیدی برای API ساخته نشده است.';
            }
        } catch (error) {
            console.warn('Failed to load external API settings', error);
        }
    },

    async saveExternalApiSettings() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        this.setSettingsCardSaveState('external-api', 'pending');
        try {
            const enabledEl = document.getElementById('settings-external-api-enabled');
            const enabled = enabledEl && enabledEl.checked ? '1' : '0';
            const res = await this.req('save_external_api_settings', { external_api_enabled: enabled });
            if (res.status === 'success') {
                this.setSettingsCardSaveState('external-api', 'saved');
                this.loadExternalApiSettings();
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'تنظیمات API ذخیره شد', showConfirmButton:false, timer:2000});
            } else {
                this.setSettingsCardSaveState('external-api', 'failed');
                Swal.fire('خطا', res.message || 'ذخیره تنظیمات API با خطا مواجه شد', 'error');
            }
        } catch (error) {
            this.setSettingsCardSaveState('external-api', 'failed');
            Swal.fire('خطا', 'خطا در ارتباط با سرور هنگام ذخیره تنظیمات API', 'error');
        }
    },

    async generateExternalApiToken() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        const passwordEl = document.getElementById('settings-external-api-admin-password');
        const password = passwordEl ? String(passwordEl.value || '') : '';
        if (!password) {
            Swal.fire('خطا', 'رمز عبور ادمین الزامی است', 'error');
            return;
        }
        this.setSettingsCardSaveState('external-api', 'pending');
        try {
            const res = await this.req('generate_external_api_token', { current_admin_password: password });
            if (res.status === 'success') {
                const tokenEl = document.getElementById('settings-external-api-new-token');
                const boxEl = document.getElementById('settings-external-api-new-token-box');
                if (tokenEl) tokenEl.value = res.token || '';
                if (boxEl) boxEl.classList.remove('hidden');
                this.setSettingsCardSaveState('external-api', 'saved');
                this.loadExternalApiSettings();
                if (boxEl) boxEl.classList.remove('hidden');
                if (tokenEl) tokenEl.value = res.token || '';
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'کلید API ساخته شد', showConfirmButton:false, timer:2500});
            } else {
                this.setSettingsCardSaveState('external-api', 'failed');
                Swal.fire('خطا', res.message || 'ساخت کلید با خطا مواجه شد', 'error');
            }
        } catch (error) {
            this.setSettingsCardSaveState('external-api', 'failed');
            Swal.fire('خطا', 'خطا در ارتباط با سرور هنگام ساخت کلید', 'error');
        }
    },

    async revokeExternalApiToken() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        const passwordEl = document.getElementById('settings-external-api-admin-password');
        const password = passwordEl ? String(passwordEl.value || '') : '';
        if (!password) {
            Swal.fire('خطا', 'رمز عبور ادمین الزامی است', 'error');
            return;
        }
        const confirm = await Swal.fire({
            title: 'حذف کلید API؟',
            text: 'بعد از حذف، اتصال اپ‌های خارجی تا ساخت کلید جدید قطع می‌شود.',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'حذف کلید',
            cancelButtonText: 'انصراف'
        });
        if (!confirm.isConfirmed) return;

        this.setSettingsCardSaveState('external-api', 'pending');
        try {
            const res = await this.req('revoke_external_api_token', { current_admin_password: password });
            if (res.status === 'success') {
                this.setSettingsCardSaveState('external-api', 'saved');
                this.loadExternalApiSettings();
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'کلید API حذف شد', showConfirmButton:false, timer:2500});
            } else {
                this.setSettingsCardSaveState('external-api', 'failed');
                Swal.fire('خطا', res.message || 'حذف کلید با خطا مواجه شد', 'error');
            }
        } catch (error) {
            this.setSettingsCardSaveState('external-api', 'failed');
            Swal.fire('خطا', 'خطا در ارتباط با سرور هنگام حذف کلید', 'error');
        }
    },

    async copyExternalApiToken() {
        const tokenEl = document.getElementById('settings-external-api-new-token');
        const token = tokenEl ? String(tokenEl.value || '') : '';
        if (!token) return;
        try {
            await navigator.clipboard.writeText(token);
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'کلید کپی شد', showConfirmButton:false, timer:1600});
        } catch (error) {
            if (tokenEl) {
                tokenEl.focus();
                tokenEl.select();
            }
            Swal.fire('خطا', 'کپی خودکار ممکن نشد؛ متن کلید انتخاب شد.', 'warning');
        }
    },

    async loadServiceGovernanceSettings() {
        if (!this.isAdmin()) return;
        try {
            const res = await this.req('get_service_governance_settings');
            if (res.status !== 'success') return;
            const settings = res.settings || {};
            const ownerEl = document.getElementById('settings-app-default-owner');
            const sourceEl = document.getElementById('settings-app-source-code');
            const strategyEl = document.getElementById('settings-vip-effective-deal-strategy');
            const statusEl = document.getElementById('settings-service-governance-status');
            if (ownerEl) {
                ownerEl.innerHTML = '<option value="">-- انتخاب کاربر --</option>';
                (res.users || []).forEach((user) => {
                    if (!user || !user.didar_user_id) return;
                    const opt = document.createElement('option');
                    opt.value = user.didar_user_id;
                    opt.textContent = `${user.display_name || user.email || user.didar_user_id}${user.is_active ? '' : ' (غیرفعال)'}`;
                    ownerEl.appendChild(opt);
                });
                ownerEl.value = settings.app_default_owner_didar_id || '';
            }
            if (sourceEl) sourceEl.value = settings.app_source_code || 'mobile_app';
            if (strategyEl) strategyEl.value = settings.vip_effective_deal_strategy || 'latest_vip_deal';
            if (statusEl) {
                statusEl.textContent = settings.app_default_owner_didar_id
                    ? 'مسئول پیش‌فرض اپ تنظیم شده است.'
                    : 'برای ساخت لیدهای جدید از اپ، مسئول پیش‌فرض باید انتخاب شود.';
            }
        } catch (error) {
            console.warn('Failed to load service governance settings', error);
        }
    },

    async saveServiceGovernanceSettings() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        this.setSettingsCardSaveState('service-governance', 'pending');
        try {
            const ownerEl = document.getElementById('settings-app-default-owner');
            const sourceEl = document.getElementById('settings-app-source-code');
            const strategyEl = document.getElementById('settings-vip-effective-deal-strategy');
            const res = await this.req('save_service_governance_settings', {
                app_default_owner_didar_id: ownerEl ? ownerEl.value : '',
                app_source_code: sourceEl ? sourceEl.value : 'mobile_app',
                vip_effective_deal_strategy: strategyEl ? strategyEl.value : 'latest_vip_deal'
            });
            if (res.status === 'success') {
                this.setSettingsCardSaveState('service-governance', 'saved');
                this.loadServiceGovernanceSettings();
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'تنظیمات اپ ذخیره شد', showConfirmButton:false, timer:2000});
            } else {
                this.setSettingsCardSaveState('service-governance', 'failed');
                Swal.fire('خطا', res.message || 'ذخیره تنظیمات اپ با خطا مواجه شد', 'error');
            }
        } catch (error) {
            this.setSettingsCardSaveState('service-governance', 'failed');
            Swal.fire('خطا', 'خطا در ارتباط با سرور هنگام ذخیره تنظیمات اپ', 'error');
        }
    },

    async loadAutoMigrationSettings() {
        try {
            const res = await this.req('get_auto_migration_settings');
            if (res.status !== 'success') return;
            const enabled = String(res.auto_migration_enabled || '0') === '1';
            const checkbox = document.getElementById('settings-auto-migration-enabled');
            const status = document.getElementById('settings-auto-migration-status');
            if (checkbox) checkbox.checked = enabled;
            if (status) {
                status.textContent = enabled
                    ? 'مایگریشن خودکار فعال است (ممکن است روی سرعت بارگذاری اثر بگذارد).'
                    : 'مایگریشن خودکار غیرفعال است (پیشنهادی برای کار عادی).';
            }
        } catch (e) {
            console.warn('Failed to load auto migration settings', e);
        }
    },

    async saveAutoMigrationSettings() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'دسترسی محدود به مدیر سیستم', 'error');
            return;
        }
        this.setSettingsCardSaveState('auto-migration', 'pending');
        try {
            const checkbox = document.getElementById('settings-auto-migration-enabled');
            const enabled = checkbox ? (checkbox.checked ? '1' : '0') : '0';
            const res = await this.req('save_auto_migration_settings', { auto_migration_enabled: enabled });
            if (res.status === 'success') {
                const status = document.getElementById('settings-auto-migration-status');
                if (status) {
                    status.textContent = enabled === '1'
                        ? 'مایگریشن خودکار فعال است (ممکن است روی سرعت بارگذاری اثر بگذارد).'
                        : 'مایگریشن خودکار غیرفعال است (پیشنهادی برای کار عادی).';
                }
                this.setSettingsCardSaveState('auto-migration', 'saved');
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'تنظیمات ذخیره شد', showConfirmButton:false, timer:2000});
            } else {
                this.setSettingsCardSaveState('auto-migration', 'failed');
                Swal.fire('خطا', res.message || 'ذخیره تنظیمات با خطا مواجه شد', 'error');
            }
        } catch (error) {
            this.setSettingsCardSaveState('auto-migration', 'failed');
            Swal.fire('خطا', 'خطا در ارتباط با سرور هنگام ذخیره تنظیمات', 'error');
        }
    },

    // REFERRALS
    async loadReferrals() {
        try {
            const res = await this.req('get_referrals');
            if (res.status === 'success') {
                this.state.referrals = res.referrals || [];
                this.renderReferrals();
            }
        } catch (e) {
            console.warn('Failed to load referrals', e);
            this.state.referrals = [];
            this.renderReferrals();
        }
    },

    renderReferrals() {
        const tbody = document.getElementById('referrals-list');
        if (!tbody) return;

        if (this.state.referrals.length === 0) {
            tbody.innerHTML = '<tr><td colspan="5" class="text-center py-8 text-gray-500">هیچ ارجاعی یافت نشد</td></tr>';
            return;
        }

        tbody.innerHTML = this.state.referrals.map(r => `
            <tr class="hover:bg-gray-800/30">
                <td class="p-3">
                    <div class="text-sm text-gray-200">${r.first_name || ''} ${r.last_name || ''}</div>
                    <div class="text-xs text-gray-500 mt-1">${r.mobile_phone || ''}</div>
                </td>
                <td class="p-3 text-xs text-gray-300">${r.phones ? JSON.parse(r.phones).join(' ، ') : ''}</td>
                <td class="p-3 text-xs text-gray-300">${r.referrer_name || '---'}</td>
                <td class="p-3 text-xs text-gray-400">${r.created_at ? new Date(r.created_at).toLocaleString('fa-IR') : ''}</td>
                <td class="p-3 text-xs text-gray-300 whitespace-pre-wrap">${r.note || ''}</td>
            </tr>
        `).join('');
    },
    
    // DASHBOARD
    async loadDashboard() {
        const ownerFilterWrap = document.getElementById('dashboard-owner-filter-wrap');
        if (ownerFilterWrap) {
            ownerFilterWrap.classList.toggle('hidden', !this.hasCrossUserScopeClient('dashboard'));
        }
        const ownerFilter = document.getElementById('dashboard-owner-filter')?.value || '';
        const statusFilter = document.getElementById('dashboard-status-filter')?.value || 'all';

        const res = await this.req('get_dashboard_stats', {
            owner_didar_id: ownerFilter,
            status: statusFilter
        });
        if(res.status === 'success') {
            const stats = res.stats;
            const content = document.getElementById('dashboard-content');
            
            content.innerHTML = `
                <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 mb-6">
                    <div class="glass p-4 border-r-4 border-blue-500 cursor-pointer hover:bg-gray-800/50 transition" onclick="app.goToLeadsWithFilter('all')">
                        <div class="text-sm text-gray-400 mb-1">کل لید</div>
                        <div class="text-2xl font-bold text-white">${stats.total_leads || stats.total_persons || 0}</div>
                        </div>
                    <div class="glass p-4 border-r-4 border-green-500 cursor-pointer hover:bg-gray-800/50 transition" onclick="app.goToLeadsWithFilter('success')">
                        <div class="text-sm text-gray-400 mb-1">لید های موفق</div>
                        <div class="text-2xl font-bold text-white">${stats.successful_leads || 0}</div>
                    </div>
                    <div class="glass p-4 border-r-4 border-red-500 cursor-pointer hover:bg-gray-800/50 transition" onclick="app.goToLeadsWithFilter('failed')">
                        <div class="text-sm text-gray-400 mb-1">لید های ناموفق</div>
                        <div class="text-2xl font-bold text-white">${stats.failed_leads || 0}</div>
                    </div>
                </div>
                
                <div class="glass p-6">
                    <h3 class="text-lg font-bold mb-4 text-yellow-500">کاریز معاملات</h3>
                    <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-7 gap-3">
                        ${this.state.pipelineStages.map(stage => `
                            <div class="text-center p-3 bg-gray-800 rounded cursor-pointer hover:bg-gray-700 transition" onclick="app.goToDealsWithFilter('${stage}')">
                                <div class="text-xs text-gray-400 mb-1">${stage}</div>
                                <div class="text-xl font-bold text-white">${(stats.deals_by_stage && stats.deals_by_stage[stage]) ? stats.deals_by_stage[stage] : 0}</div>
                            </div>
                        `).join('')}
                    </div>
                    <div class="mt-4 text-xs text-gray-400">کل جاری (Pending): ${stats.ongoing_deals || 0}</div>
                </div>
            `;
        }
    },
    
    // ACTIVITY TYPES
    async loadActivityTypes() {
        const res = await this.req('get_activity_types');
        if(res.status === 'success') {
            this.state.activityTypes = res.types || [];
        }
    },

    async loadProducts() {
        try {
            const res = await this.req('get_products');
            if (res.status === 'success') {
                this.state.products = res.products || [];
                this.renderProductOptions();
                this.renderLeadProductFilter();
                this.renderDealTitleOptions();
                if (this.state.lookupSelectedGroup === '__products_manager__') this.renderLookupProductsManagerTable();
            }
        } catch (e) {
            console.error('Failed to load products', e);
        }
    },
    
	    async loadDidarUsers() {
	        try {
	            console.log('[DEBUG] Starting loadDidarUsers...');
	            const res = await this.req('get_didar_users');
	            console.log('[DEBUG] get_didar_users response:', res);
	            
	            if(res.status === 'success') {
	                this.state.didarUsers = res.users || [];
	                console.log('[DEBUG] Didar users loaded successfully:', this.state.didarUsers.length, 'users');
	                console.log('[DEBUG] Users data:', JSON.stringify(this.state.didarUsers, null, 2));
	                
	                this.populateLeadOwnerSelect();
	                this.populateOwnerFilterSelects();
	                
	                // اگر لیست خالی است، به کاربر اطلاع بده
	                if (this.state.didarUsers.length === 0) {
	                    console.warn('[DEBUG] No Didar users found. This might indicate an issue with API or all users are inactive.');
	                }
	            } else {
	                // نمایش خطا به کاربر
	                console.error('[DEBUG] Failed to load Didar users:', res.message || 'Unknown error', res);
	                if (res.message) {
	                    Swal.fire({
	                        icon: 'warning',
	                        title: 'هشدار',
	                        text: 'خطا در دریافت لیست کاربران دیدار: ' + res.message,
	                        confirmButtonText: 'باشه',
	                        timer: 5000
	                    });
	                }
	                this.state.didarUsers = [];
	                this.populateLeadOwnerSelect();
	                this.populateOwnerFilterSelects();
	            }
	        } catch (error) {
	            console.error('[DEBUG] Error loading Didar users:', error);
	            this.state.didarUsers = [];
	            this.populateLeadOwnerSelect();
	            this.populateOwnerFilterSelects();
	        }
	    },

    async loadReminders() {
        const container = document.getElementById('reminders-list-container');
        if (!container) return;

        container.innerHTML = '<div class="text-center py-20 col-span-full"><div class="loading-spinner mb-4 mx-auto"></div><p class="text-gray-400">در حال بارگذاری معاملات گذشته...</p></div>';

        try {
            const res = await this.req('get_reminders_list');
            if (res.status === 'success') {
                const reminders = res.reminders || [];
                if (reminders.length === 0) {
                    container.innerHTML = '<div class="text-center py-20 col-span-full"><i class="fa-solid fa-clock-rotate-left text-4xl text-gray-700 mb-4 block"></i><p class="text-gray-500">هیچ یادآوری فعالی یافت نشد.</p></div>';
                    return;
                }

                // Priority levels configuration
                const priorityLevels = {
                    'high': {
                        color: 'border-red-600 bg-red-500/10',
                        icon: 'fa-exclamation-triangle',
                        badge: 'bg-red-500/20 text-red-400',
                        text: 'اولویت بالا'
                    },
                    'medium': {
                        color: 'border-yellow-600 bg-yellow-500/10',
                        icon: 'fa-clock',
                        badge: 'bg-yellow-500/20 text-yellow-400',
                        text: 'اولویت متوسط'
                    },
                    'low': {
                        color: 'border-blue-600 bg-blue-500/10',
                        icon: 'fa-info-circle',
                        badge: 'bg-blue-500/20 text-blue-400',
                        text: 'اولویت پایین'
                    }
                };

                container.innerHTML = reminders.map(rem => {
                    const contactId = rem.contact_didar_id || rem.contact_id || (rem.contact ? rem.contact.didar_contact_id : '') || rem.local_person_id;
                    const isOverdue = new Date(rem.reminder_time) < new Date();
                    const statusColor = rem.reminder_sent ? 'border-gray-500' : (isOverdue ? 'border-red-500' : 'border-blue-500');
                    const statusText = rem.reminder_sent ? 'ارسال شده' : (isOverdue ? 'زمان گذشته' : 'در انتظار');
                    const badgeClass = rem.reminder_sent ? 'bg-gray-500/20 text-gray-400' : (isOverdue ? 'bg-red-500/20 text-red-400' : 'bg-blue-500/20 text-blue-400');

                    const timeToReminder = new Date(rem.reminder_time) - new Date();
                    const hoursToReminder = timeToReminder / (1000 * 60 * 60);
                    let priority = 'low';
                    if (isOverdue || hoursToReminder < 1) priority = 'high';
                    else if (hoursToReminder < 24) priority = 'medium';

                    const priorityData = priorityLevels[priority];
                    
                    const timeRemaining = isOverdue ? 
                        `${Math.abs(Math.floor(hoursToReminder))} ساعت پیش` : 
                        `${Math.floor(hoursToReminder)} ساعت دیگر`;

                    const customerName = (rem.first_name || '') + ' ' + (rem.last_name || '').trim();
                    const avatarInitials = customerName.length > 0 ? 
                        customerName.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase() : 
                        '؟؟';

                    return `
                        <div class="glass p-5 rounded-2xl border border-gray-700/50 hover:border-blue-500/50 transition-all duration-500 group flex flex-col h-full relative overflow-hidden">
                            <!-- Top Status Bar -->
                            <div class="flex justify-between items-center mb-4">
                                <span class="text-[9px] px-2.5 py-1 rounded-full font-bold tracking-tight ${priorityData.badge} flex items-center gap-1.5">
                                    <i class="fa-solid ${priorityData.icon} text-[8px]"></i>
                                    ${priorityData.text}
                                </span>
                                <span class="text-[9px] px-2.5 py-1 rounded-full font-bold ${badgeClass}">
                                    ${statusText}
                                </span>
                            </div>

                            <!-- Main Content -->
                            <div class="flex items-center gap-4 mb-5">
                                <div class="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-600/20 to-purple-600/20 border border-blue-500/20 flex items-center justify-center text-blue-400 font-black text-sm shadow-inner group-hover:scale-110 transition-transform">
                                    ${avatarInitials}
                                </div>
                                <div class="flex-1 min-w-0">
                                    <h4 class="text-white font-bold text-sm truncate mb-0.5">${this.escapeHtml(customerName || 'مشتری نامشخص')}</h4>
                                    <p class="text-[10px] text-gray-400 font-medium truncate opacity-80">${this.escapeHtml(rem.activity_title || 'فعالیت بدون عنوان')}</p>
                                </div>
                            </div>

                            <!-- Timing Info -->
                            <div class="grid grid-cols-2 gap-2 mb-5">
                                <div class="bg-gray-800/40 p-2 rounded-lg border border-gray-700/30">
                                    <span class="block text-[8px] text-gray-500 mb-0.5">زمان یادآوری</span>
                                    <span class="text-[10px] text-gray-300 font-bold font-mono">${toPersianDateTime(rem.reminder_time)}</span>
                                </div>
                                <div class="bg-gray-800/40 p-2 rounded-lg border border-gray-700/30">
                                    <span class="block text-[8px] text-gray-500 mb-0.5">وضعیت زمانی</span>
                                    <span class="text-[10px] ${isOverdue ? 'text-red-400' : 'text-green-400'} font-bold">${timeRemaining}</span>
                                </div>
                            </div>

                            <!-- Progress Section -->
                            <div class="mb-6">
                                <div class="flex justify-between text-[9px] mb-1.5 px-0.5">
                                    <span class="text-gray-500">پیشرفت فعالیت</span>
                                    <span class="text-blue-400 font-bold">${rem.reminder_sent ? '100%' : 'در جریان'}</span>
                                </div>
                                <div class="w-full bg-gray-900/50 rounded-full h-1.5 overflow-hidden border border-gray-800">
                                    <div class="bg-gradient-to-r from-blue-600 to-indigo-500 h-full rounded-full transition-all duration-700 ease-out"
                                         style="width: ${rem.reminder_sent ? '100' : Math.max(15, Math.min(85, (new Date() - new Date(rem.due_date)) / (new Date(rem.reminder_time) - new Date(rem.due_date)) * 100))}%">
                                    </div>
                                </div>
                            </div>

                            <!-- Actions Group -->
                            <div class="mt-auto space-y-3">
                                <div class="grid grid-cols-2 gap-2">
                                    <button onclick="app.openPersonProfile('${contactId}')"
                                        class="text-[10px] bg-blue-600/10 hover:bg-blue-600 text-blue-400 hover:text-white py-2 rounded-xl transition-all border border-blue-500/20 flex items-center justify-center gap-2">
                                        <i class="fa-solid fa-user-tag"></i> مشاهده لید
                                    </button>
                                    <button onclick="app.markReminderDone('${rem.activity_didar_id}')"
                                        class="text-[10px] bg-emerald-600/10 hover:bg-emerald-600 text-emerald-400 hover:text-white py-2 rounded-xl transition-all border border-emerald-500/20 flex items-center justify-center gap-2">
                                        <i class="fa-solid fa-check-circle"></i> انجام شد
                                    </button>
                                </div>
                                
                                <div class="flex justify-between items-center gap-1.5 pt-3 border-t border-gray-800">
                                    <button onclick="app.snoozeReminder('${rem.activity_didar_id}', 60)" class="flex-1 text-[9px] bg-gray-800 hover:bg-yellow-600/20 text-gray-400 hover:text-yellow-500 py-1.5 rounded-lg border border-gray-700/50 transition-colors">+۱س</button>
                                    <button onclick="app.snoozeReminder('${rem.activity_didar_id}', 240)" class="flex-1 text-[9px] bg-gray-800 hover:bg-yellow-600/20 text-gray-400 hover:text-yellow-500 py-1.5 rounded-lg border border-gray-700/50 transition-colors">+۴س</button>
                                    <button onclick="app.snoozeReminder('${rem.activity_didar_id}', 1440)" class="flex-1 text-[9px] bg-gray-800 hover:bg-yellow-600/20 text-gray-400 hover:text-yellow-500 py-1.5 rounded-lg border border-gray-700/50 transition-colors">+۱روز</button>
                                    <div class="w-px h-4 bg-gray-800 mx-1"></div>
                                    <button onclick="app.resendReminder('${rem.activity_didar_id}')" class="p-2 text-gray-500 hover:text-blue-400 transition-colors" title="ارسال مجدد">
                                        <i class="fa-solid fa-bell"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    `;
                }).join('');
            } else {
                container.innerHTML = `<div class="text-center py-20 col-span-full text-red-400">${res.message || 'خطا در دریافت یادآوری‌ها'}</div>`;
            }
        } catch (error) {
            console.error('Reminders load failed:', error);
            container.innerHTML = '<div class="text-center py-20 col-span-full text-red-400">خطا در ارتباط با سرور</div>';
        }
    },

    // Reminder Actions
    async editReminder(activityId) {
        // TODO: Implement edit reminder functionality
        Swal.fire({
            title: 'ویرایش یادآوری',
            text: 'این قابلیت به زودی اضافه خواهد شد',
            icon: 'info',
            confirmButtonText: 'باشه'
        });
    },

    async resendReminder(activityId) {
        try {
            const result = await Swal.fire({
                title: 'ارسال مجدد اعلان',
                text: 'آیا مطمئن هستید که می‌خواهید اعلان را مجدداً ارسال کنید؟',
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: 'بله، ارسال کن',
                cancelButtonText: 'لغو'
            });

            if (result.isConfirmed) {
                const res = await this.req('resend_reminder', { activity_id: activityId });
                if (res.status === 'success') {
                    Swal.fire({
                        title: 'موفق!',
                        text: 'اعلان مجدداً ارسال شد',
                        icon: 'success',
                        timer: 2000
                    });
                    this.loadReminders();
                } else {
                    throw new Error(res.message);
                }
            }
        } catch (error) {
            console.error('Resend reminder failed:', error);
            Swal.fire({
                title: 'خطا!',
                text: error.message || 'خطا در ارسال مجدد اعلان',
                icon: 'error'
            });
        }
    },

    async markReminderDone(activityId) {
        try {
            const result = await Swal.fire({
                title: 'علامت‌گذاری به عنوان انجام شده',
                text: 'این یادآوری به عنوان انجام شده علامت‌گذاری خواهد شد',
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: 'بله، انجام شد',
                cancelButtonText: 'لغو'
            });

            if (result.isConfirmed) {
                const res = await this.req('mark_reminder_done', { activity_id: activityId });
                if (res.status === 'success') {
                    Swal.fire({
                        title: 'موفق!',
                        text: 'یادآوری به عنوان انجام شده علامت‌گذاری شد',
                        icon: 'success',
                        timer: 2000
                    });
                    this.loadReminders();
                } else {
                    throw new Error(res.message);
                }
            }
        } catch (error) {
            console.error('Mark reminder done failed:', error);
            Swal.fire({
                title: 'خطا!',
                text: error.message || 'خطا در علامت‌گذاری یادآوری',
                icon: 'error'
            });
        }
    },

    async snoozeReminder(activityId, minutes) {
        try {
            const timeLabels = {
                60: '۱ ساعت',
                240: '۴ ساعت',
                1440: '۱ روز'
            };

            const result = await Swal.fire({
                title: 'تعویق یادآوری',
                text: `یادآوری به مدت ${timeLabels[minutes]} تعویق خواهد شد`,
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: 'بله، تعویق کن',
                cancelButtonText: 'لغو'
            });

            if (result.isConfirmed) {
                const res = await this.req('snooze_reminder', {
                    activity_id: activityId,
                    minutes: minutes
                });
                if (res.status === 'success') {
                    Swal.fire({
                        title: 'موفق!',
                        text: `یادآوری به مدت ${timeLabels[minutes]} تعویق شد`,
                        icon: 'success',
                        timer: 2000
                    });
                    this.loadReminders();
                } else {
                    throw new Error(res.message);
                }
            }
        } catch (error) {
            console.error('Snooze reminder failed:', error);
            Swal.fire({
                title: 'خطا!',
                text: error.message || 'خطا در تعویق یادآوری',
                icon: 'error'
            });
        }
    },

	    populateOwnerFilterSelects() {
	        const leadsOwnerSelect = document.getElementById('leads-owner-filter');
	        const dealsOwnerSelect = document.getElementById('deals-owner-filter');
	        const legacyDealsOwnerSelect = document.getElementById('legacy-deals-owner-filter');
	        const dashboardOwnerSelect = document.getElementById('dashboard-owner-filter');
	        
	        console.log('[DEBUG] populateOwnerFilterSelects called');
	        console.log('[DEBUG] leadsOwnerSelect exists:', !!leadsOwnerSelect);
	        console.log('[DEBUG] dealsOwnerSelect exists:', !!dealsOwnerSelect);
	        console.log('[DEBUG] legacyDealsOwnerSelect exists:', !!legacyDealsOwnerSelect);
	        console.log('[DEBUG] dashboardOwnerSelect exists:', !!dashboardOwnerSelect);
	        console.log('[DEBUG] didarUsers count:', this.state.didarUsers?.length || 0);

	        const render = (select, isDashboard = false) => {
	            if (!select) {
	                console.log('[DEBUG] Select element is null, skipping render');
	                return;
	            }
	            
	            const current = select.value || '';
	            console.log('[DEBUG] Rendering select, current value:', current, 'isDashboard:', isDashboard);
	            
	            select.innerHTML = isDashboard ? '<option value="">همه مسئول‌ها (کل سیستم)</option>' : '<option value="">همه مسئول‌ها</option>';
	            
	            console.log('[DEBUG] Starting to add user options. Users array:', this.state.didarUsers);
	            
	            (this.state.didarUsers || []).forEach((user, index) => {
	                // Use owner_id (didar_id) if available, otherwise fallback to internal id
	                const value = user.owner_id || user.id;
	                const label = user.display_name || user.email || ('کاربر ' + user.id);
	                
	                console.log(`[DEBUG] User ${index}:`, { value, label, user });
	                
	                if (!value) {
	                    console.log(`[DEBUG] User ${index} skipped - no value`);
	                    return;
	                }
	                
	                const opt = document.createElement('option');
	                opt.value = value;
	                opt.textContent = label;
	                select.appendChild(opt);
	                console.log(`[DEBUG] User ${index} added to select with value: ${value}, label: ${label}`);
	            });
	            
	            console.log('[DEBUG] Total options in select:', select.options.length);
	            // Restore previous value if it still exists in the new list
	            select.value = current;
	        };

	        render(leadsOwnerSelect);
	        render(dealsOwnerSelect);
	        render(legacyDealsOwnerSelect);
	        render(dashboardOwnerSelect, true);
	        
	        console.log('[DEBUG] populateOwnerFilterSelects complete');
	    },
    
    populateLeadOwnerSelect() {
        const select = document.getElementById('lead-owner-select');
        const prevSelect = document.getElementById('lead-previous-owner-select');
        if(!select && !prevSelect) {
            console.warn('lead owner selects not found');
            return;
        }
        if (select) {
            select.innerHTML = '<option value="">-- انتخاب مسئول از دیدار --</option>';
        }
        if (prevSelect) {
            prevSelect.innerHTML = '<option value="">-- انتخاب مسئول قبلی --</option>';
        }
        
        if (!this.state.didarUsers || this.state.didarUsers.length === 0) {
            console.warn('No Didar users available to populate select');
            if (select) {
                const opt = document.createElement('option');
                opt.value = '';
                opt.textContent = '-- هیچ کاربری یافت نشد --';
                opt.disabled = true;
                select.appendChild(opt);
            }
            if (prevSelect) {
                const opt2 = document.createElement('option');
                opt2.value = '';
                opt2.textContent = '-- هیچ کاربری یافت نشد --';
                opt2.disabled = true;
                prevSelect.appendChild(opt2);
            }
            return;
        }
        
        let addedCount = 0;
        this.state.didarUsers.forEach(user => {
            if(!user.id) {
                console.warn('User without id found:', user);
                return;
            }
            const value = user.owner_id || user.id;
            const label = user.display_name || user.email || user.id;
            if (select) {
                const opt = document.createElement('option');
                opt.value = value;
                opt.textContent = label;
                select.appendChild(opt);
            }
            if (prevSelect) {
                const optPrev = document.createElement('option');
                optPrev.value = value;
                optPrev.textContent = label;
                prevSelect.appendChild(optPrev);
            }
            addedCount++;
        });
        
        console.log(`Populated lead owner select with ${addedCount} users`);
    },
    
        populateSelectOptions(selectId, options, placeholderOverride = null) {
        const select = document.getElementById(selectId);
        if(!select) return;
        const current = select.value;
        let placeholder = placeholderOverride;
        if (!placeholder) {
            const existingPlaceholder = select.querySelector('option[value=""]')?.textContent;
            if (existingPlaceholder) {
                placeholder = existingPlaceholder;
            }
        }
        if (!placeholder) {
            // Get default placeholder based on select ID
            placeholder = '-- انتخاب --';
            if (selectId === 'lead-source') {
                placeholder = 'شیوه آشنایی *';
            } else if (selectId === 'lead-content-topic') {
                placeholder = 'موضوع محتوا *';
            }
        }
        select.innerHTML = `<option value="">${placeholder}</option>`;
        options.forEach(opt => {
            const option = document.createElement('option');
            option.value = opt.value;
            option.textContent = opt.label ?? opt.title ?? opt.value;
            select.appendChild(option);
        });
        if (current) {
            select.value = current;
        }
    },

    async loadLookups() {
        try {
            const res = await this.req('get_lookups', {});
            if (res.status === 'success' && res.lookups) {
                this.state.lookups = res.lookups || {};
                this.state.lookupsLoaded = true;
                try {
                    const servicesRes = await this.req('get_lookups', { group: 'allowed_services', include_inactive: '1' });
                    if (servicesRes.status === 'success' && Array.isArray(servicesRes.items)) {
                        this.state.allowedServiceLookupItemsAll = servicesRes.items;
                    }
                } catch (serviceLookupError) {
                    console.warn('Failed to load allowed services archive:', serviceLookupError);
                    this.state.allowedServiceLookupItemsAll = [];
                }
                try {
                    // Load destination banks INCLUDING hidden/inactive ones so that older
                    // deals whose card was later disabled in the lookup manager can still
                    // resolve the bank title / card / account / sheba for display.
                    const banksRes = await this.req('get_lookups', { group: 'payment_destination_banks', include_inactive: '1' });
                    if (banksRes.status === 'success' && Array.isArray(banksRes.items)) {
                        this.state.paymentDestinationBanksAll = banksRes.items;
                    }
                } catch (bankLookupError) {
                    console.warn('Failed to load destination banks archive:', bankLookupError);
                    this.state.paymentDestinationBanksAll = [];
                }
                const pipeline = this.getLookupValues('pipeline_stages');
                if (pipeline.length) {
                    this.state.pipelineStages = pipeline;
                }
                if (this.isAdmin() && this.state.lookupSelectedGroup) {
                    await this.loadLookupItems(this.state.lookupSelectedGroup);
                }
            }
        } catch (error) {
            console.warn('Failed to load lookups:', error);
        }
    },

    getLookupItems(groupCode) {
        const group = this.state.lookups?.[groupCode];
        if (group && Array.isArray(group.items) && group.items.length) {
            return group.items;
        }
        if (groupCode === 'lead_sources') return this.leadSources || [];
        if (groupCode === 'content_topics') return this.contentTopics || [];
        return [];
    },

    getLookupOptions(groupCode) {
        const items = this.getLookupItems(groupCode);
        return items.map(item => ({
            value: item.value ?? item.title ?? item.label ?? '',
            label: item.title ?? item.label ?? item.value ?? ''
        })).filter(opt => opt.value !== '' || opt.label !== '');
    },

    getLookupValues(groupCode) {
        return this.getLookupOptions(groupCode).map(opt => opt.value || opt.label);
    },

    getLookupLabel(groupCode, value) {
        if (!value) return '-';
        const items = this.getLookupItems(groupCode);
        for (const item of items) {
            const itemValue = item.value ?? item.title ?? item.label;
            if (itemValue === value) {
                return item.title ?? item.label ?? itemValue;
            }
        }
        return value;
    },

    getLookupChildOptions(groupCode, parentValue) {
        const group = this.state.lookups?.[groupCode];
        if (group && Array.isArray(group.items) && group.items.length) {
            return group.items
                .filter(item => (item.parent_value || '') === parentValue)
                .map(item => ({
                    value: item.value ?? item.title ?? '',
                    label: item.title ?? item.value ?? ''
                }));
        }
        if (groupCode === 'lead_source_details') {
            const fallback = this.leadSourceDetails?.[parentValue] || this.leadSourceDetails?.default || [];
            return fallback.map(item => ({ value: item.value, label: item.label }));
        }
        return [];
    },
    normalizeLookupComparable(value) {
        return String(value ?? '').trim().replace(/\s+/g, ' ');
    },
    getLookupItemMatches(item, value) {
        const target = this.normalizeLookupComparable(value);
        if (!target || !item) return false;
        return [item.value, item.title, item.code, item.label]
            .some(candidate => this.normalizeLookupComparable(candidate) === target);
    },
    findLookupItem(groupCode, value, includeInactiveFallback = false) {
        const primaryItems = this.getLookupItems(groupCode) || [];
        let archivedItems = [];
        if (includeInactiveFallback) {
            if (groupCode === 'allowed_services') {
                archivedItems = this.state.allowedServiceLookupItemsAll || [];
            } else if (groupCode === 'payment_destination_banks') {
                archivedItems = this.state.paymentDestinationBanksAll || [];
            }
        }
        const items = [...primaryItems, ...archivedItems];
        const seen = new Set();
        const uniqueItems = items.filter(item => {
            const key = String(item?.id ?? `${item?.value ?? ''}:${item?.title ?? ''}`);
            if (seen.has(key)) return false;
            seen.add(key);
            return true;
        });
        return uniqueItems.find(item => this.getLookupItemMatches(item, value)) || null;
    },
    getLookupMeta(groupCode, value) {
        const item = this.findLookupItem(groupCode, value, true);
        if (!item || !item.meta_json) return {};
        try {
            const parsed = JSON.parse(item.meta_json);
            return parsed && typeof parsed === 'object' ? parsed : {};
        } catch (_) {
            return {};
        }
    },
    getAllowedServiceFixedPrice(serviceValue) {
        if (!serviceValue) return null;
        const meta = this.getLookupMeta('allowed_services', serviceValue);
        if (!meta || typeof meta !== 'object') return null;
        const rawPrice = meta.price ?? null;
        if (rawPrice === null || rawPrice === '') return null;
        const price = typeof rawPrice === 'number' ? rawPrice : parseMoneyValue(String(rawPrice));
        return Number.isFinite(price) && price > 0 ? price : null;
    },
    getAllowedServicePriceListCode(serviceValue) {
        if (!serviceValue) return '';
        const meta = this.getLookupMeta('allowed_services', serviceValue);
        if (!meta || typeof meta !== 'object') return '';
        return String(meta.price_list_code ?? '').trim();
    },
    getAllowedServiceLookupItemsDetailed(includeInactiveFallback = false) {
        const activeItems = this.getLookupItems('allowed_services') || [];
        const archivedItems = includeInactiveFallback ? (this.state.allowedServiceLookupItemsAll || []) : [];
        const seen = new Set();
        return [...activeItems, ...archivedItems].filter(item => {
            if (!item || !(item.id || item.value || item.title)) return false;
            const key = String(item.id ?? `${item.value ?? ''}:${item.title ?? ''}`);
            if (seen.has(key)) return false;
            seen.add(key);
            return true;
        });
    },
    getAllowedServiceLookupItemByValue(serviceValue) {
        const target = this.normalizeLookupComparable(serviceValue);
        if (!target) return null;
        const items = this.getAllowedServiceLookupItemsDetailed(true);
        return items.find(item => this.getLookupItemMatches(item, target)) || null;
    },
    getAllowedServiceLookupItemIdByValue(serviceValue) {
        const item = this.getAllowedServiceLookupItemByValue(serviceValue);
        const id = parseInt(item?.id ?? 0, 10);
        return Number.isFinite(id) && id > 0 ? id : 0;
    },
    getProductsLinkedToServiceLookupItem(serviceLookupItemId) {
        const targetId = parseInt(serviceLookupItemId || 0, 10);
        if (!targetId) return [];
        const products = Array.isArray(this.state.products) ? this.state.products : [];
        return products.filter(p => parseInt(p?.service_lookup_item_id ?? 0, 10) === targetId);
    },
    getAllowedServiceLinkedProductIds(serviceValue) {
        const serviceId = this.getAllowedServiceLookupItemIdByValue(serviceValue);
        return this.getProductsLinkedToServiceLookupItem(serviceId).map(p => String(p.id));
    },
    buildAllowedServicesOptionsHtml(selectedServiceLookupItemId = '') {
        const selected = String(selectedServiceLookupItemId ?? '');
        const items = this.getAllowedServiceLookupItemsDetailed();
        const options = ['<option value="">-</option>'];
        items.forEach(item => {
            const id = String(item.id ?? '');
            if (!id) return;
            const label = this.escapeHtml(item.title ?? item.value ?? id);
            options.push(`<option value="${id}" ${id === selected ? 'selected' : ''}>${label}</option>`);
        });
        return options.join('');
    },
    buildProductsMultiSelectHtml(selectedIds = []) {
        const selected = new Set((selectedIds || []).map(v => String(v)));
        const products = Array.isArray(this.state.products) ? this.state.products : [];
        if (!products.length) {
            return '<div class="text-[10px] text-gray-500">محصولی ثبت نشده</div>';
        }
        return `
            <select class="input bg-gray-800 text-xs min-w-[170px]" name="service_linked_product_ids" multiple size="${Math.min(5, Math.max(2, products.length))}">
                ${products.map(p => {
                    const id = String(p.id ?? '');
                    if (!id) return '';
                    return `<option value="${id}" ${selected.has(id) ? 'selected' : ''}>${this.escapeHtml(p.name || id)}</option>`;
                }).join('')}
            </select>
        `;
    },
    getLookupTableColumnCount() {
        return 17;
    },
    setLookupManagerModeUI(mode) {
        const itemsSection = document.getElementById('lookup-items-section');
        const productsSection = document.getElementById('lookup-products-manager-section');
        const rbacSection = document.getElementById('lookup-rbac-manager-section');
        const quickSettings = document.getElementById('field-quick-settings');
        const addBtn = document.getElementById('lookup-add-item-btn');

        if (itemsSection) itemsSection.classList.toggle('hidden', mode !== 'lookup_or_field');
        if (productsSection) productsSection.classList.toggle('hidden', mode !== 'products_manager');
        if (rbacSection) rbacSection.classList.toggle('hidden', mode !== 'rbac_manager');
        if (quickSettings) quickSettings.classList.toggle('hidden', mode !== 'lookup_or_field');
        if (addBtn) addBtn.disabled = (mode !== 'lookup_or_field');
    },
    toggleLookupServiceSpecificColumns(show) {
        const ids = [
            'lookup-col-header-service-price',
            'lookup-col-header-service-price-list-code',
            'lookup-col-header-service-products'
        ];
        ids.forEach(id => {
            const el = document.getElementById(id);
            if (!el) return;
            el.classList.toggle('hidden', !show);
        });
        document.querySelectorAll('.lookup-col-service-price,.lookup-col-service-price-list-code,.lookup-col-service-products')
            .forEach(el => el.classList.toggle('hidden', !show));
    },
    toggleLookupDestinationBankColumns(show) {
        const ids = [
            'lookup-col-header-destination-bank-name',
            'lookup-col-header-destination-account-type',
            'lookup-col-header-destination-card',
            'lookup-col-header-destination-account',
            'lookup-col-header-destination-sheba'
        ];
        ids.forEach(id => {
            const el = document.getElementById(id);
            if (!el) return;
            el.classList.toggle('hidden', !show);
        });
        document.querySelectorAll('.lookup-col-destination-bank-name,.lookup-col-destination-account-type,.lookup-col-destination-card,.lookup-col-destination-account,.lookup-col-destination-sheba')
            .forEach(el => el.classList.toggle('hidden', !show));
    },
    renderLookupProductsManagerTable() {
        const tbody = document.getElementById('lookup-products-table');
        if (!tbody) return;
        const rows = Array.isArray(this.state.products) ? this.state.products : [];
        if (!rows.length) {
            tbody.innerHTML = '<tr><td colspan="3" class="p-6 text-center text-gray-500">محصولی ثبت نشده است.</td></tr>';
            return;
        }
        tbody.innerHTML = rows.map(p => {
            const id = p.id || '';
            const name = this.escapeHtml(p.name || '');
            const selectedServiceId = String(p.service_lookup_item_id ?? '');
            return `
                <tr data-product-id="${id}">
                    <td class="p-3"><input class="input bg-gray-800 text-xs" name="name" value="${name}" placeholder="نام محصول"></td>
                    <td class="p-3">
                        <select class="input bg-gray-800 text-xs" name="service_lookup_item_id">
                            ${this.buildAllowedServicesOptionsHtml(selectedServiceId)}
                        </select>
                    </td>
                    <td class="p-3">
                        <div class="flex gap-2 justify-center">
                            <button type="button" class="px-3 py-1 rounded bg-blue-600 hover:bg-blue-500 text-xs" onclick="app.saveLookupProductRow(this)">Save</button>
                            <button type="button" class="px-3 py-1 rounded bg-red-600 hover:bg-red-500 text-xs" onclick="app.deleteLookupProductRow(this)">Delete</button>
                        </div>
                    </td>
                </tr>
            `;
        }).join('');
    },
    addLookupProductRow() {
        const tbody = document.getElementById('lookup-products-table');
        if (!tbody) return;
        if (tbody.querySelector('td[colspan="3"]')) {
            tbody.innerHTML = '';
        }
        const row = document.createElement('tr');
        row.dataset.productId = '';
        row.innerHTML = `
            <td class="p-3"><input class="input bg-gray-800 text-xs" name="name" value="" placeholder="نام محصول"></td>
            <td class="p-3">
                <select class="input bg-gray-800 text-xs" name="service_lookup_item_id">
                    ${this.buildAllowedServicesOptionsHtml('')}
                </select>
            </td>
            <td class="p-3">
                <div class="flex gap-2 justify-center">
                    <button type="button" class="px-3 py-1 rounded bg-blue-600 hover:bg-blue-500 text-xs" onclick="app.saveLookupProductRow(this)">Save</button>
                    <button type="button" class="px-3 py-1 rounded bg-red-600 hover:bg-red-500 text-xs" onclick="app.deleteLookupProductRow(this)">Delete</button>
                </div>
            </td>
        `;
        tbody.prepend(row);
    },
    async saveLookupProductRow(button) {
        const row = button.closest('tr');
        if (!row) return;
        const id = row.dataset.productId || '';
        const name = row.querySelector('[name="name"]')?.value?.trim() || '';
        const serviceLookupItemId = row.querySelector('[name="service_lookup_item_id"]')?.value || '';
        if (!name) {
            Swal.fire('خطا', 'نام محصول الزامی است', 'error');
            return;
        }
        const res = await this.req('save_product', {
            id,
            name,
            service_lookup_item_id: serviceLookupItemId
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ذخیره محصول', 'error');
            return;
        }
        await this.loadProducts();
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'محصول ذخیره شد', showConfirmButton: false, timer: 1500 });
        if (this.state.lookupSelectedGroup === 'allowed_services') {
            this.renderLookupItemsTable('allowed_services', this.state.lookupItems);
        }
    },
    async deleteLookupProductRow(button) {
        const row = button.closest('tr');
        if (!row) return;
        const id = row.dataset.productId || '';
        if (!id) {
            row.remove();
            return;
        }
        const result = await Swal.fire({
            title: 'حذف محصول؟',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'حذف',
            cancelButtonText: 'انصراف'
        });
        if (!result.isConfirmed) return;
        const res = await this.req('delete_product', { id });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در حذف محصول', 'error');
            return;
        }
        await this.loadProducts();
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'محصول حذف شد', showConfirmButton: false, timer: 1500 });
        if (this.state.lookupSelectedGroup === 'allowed_services') {
            this.renderLookupItemsTable('allowed_services', this.state.lookupItems);
        }
    },
    async loadLookupRbacManagerData(force = false) {
        if (!this.isAdmin()) return;
        if (!force && this.state.rbacCatalog && this.state.rbacCatalog.roles && this.state.rbacCatalog.capabilities) {
            this.renderLookupRbacManager();
            return;
        }
        const [catalogRes, usersRes] = await Promise.all([
            this.req('rbac_catalog'),
            this.req('get_users_list')
        ]);
        if (catalogRes.status !== 'success') {
            Swal.fire('خطا', catalogRes.message || 'خطا در دریافت RBAC', 'error');
            return;
        }
        this.state.rbacCatalog = {
            roles: catalogRes.roles || [],
            capabilities: catalogRes.capabilities || [],
            resources: catalogRes.resources || {},
            scope_types: catalogRes.scope_types || ['none', 'self', 'selected_users', 'all'],
            matrix: catalogRes.matrix || null
        };
        this.state.rbacUsersForScopes = (usersRes.status === 'success' ? (usersRes.users || []) : []);
        this.renderLookupRbacManager();
    },
    renderLookupRbacManager() {
        const roleSelect = document.getElementById('rbac-role-select');
        const cloneTemplate = document.getElementById('rbac-clone-template-role');
        const roles = Array.isArray(this.state.rbacCatalog?.roles) ? this.state.rbacCatalog.roles : [];
        const currentRoleKey = roleSelect?.value || this.state.rbacSelectedRoleKey || '';
        const buildOpts = () => roles.map(r => {
            const key = String(r.role_key || '').trim();
            if (!key) return '';
            const label = `${r.title || key}${String(r.is_system || '0') === '1' ? ' (سیستمی)' : ''}`;
            return `<option value="${this.escapeHtml(key)}">${this.escapeHtml(label)}</option>`;
        }).join('');
        if (roleSelect) {
            roleSelect.innerHTML = '<option value="">-- انتخاب نقش --</option>' + buildOpts();
            if (currentRoleKey) roleSelect.value = currentRoleKey;
        }
        if (cloneTemplate) {
            cloneTemplate.innerHTML = '<option value="">-- تمپلیت نقش --</option>' + buildOpts();
        }
        const userSelect = document.getElementById('rbac-user-select');
        if (userSelect) {
            const users = Array.isArray(this.state.rbacUsersForScopes) ? this.state.rbacUsersForScopes : [];
            const curUser = userSelect.value;
            userSelect.innerHTML = '<option value="">-- انتخاب کاربر --</option>' + users.map(u => {
                const id = String(u.id || '');
                if (!id) return '';
                const name = u.display_name || `${u.first_name || ''} ${u.last_name || ''}`.trim() || u.email || id;
                const roleTxt = u.role ? ` — ${u.role}` : '';
                return `<option value="${this.escapeHtml(id)}">${this.escapeHtml(name + roleTxt)}</option>`;
            }).join('');
            if (curUser) userSelect.value = curUser;
        }
        if (currentRoleKey) {
            this.onLookupRbacRoleChange();
        }
    },
    async onLookupRbacRoleChange() {
        const roleKey = document.getElementById('rbac-role-select')?.value || '';
        this.state.rbacSelectedRoleKey = roleKey;
        if (!roleKey) {
            this.renderLookupRbacRoleEditor(null, [], []);
            return;
        }
        const res = await this.req('rbac_role_permissions', { role_key: roleKey });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در دریافت دسترسی‌های نقش', 'error');
            return;
        }
        this.state.rbacCurrentRolePermissions = res;
        this.renderLookupRbacRoleEditor(res.role || null, res.capabilities || [], res.scopes || []);
    },
    renderLookupRbacRoleEditor(role, capabilityKeys, scopes) {
        const roleIdEl = document.getElementById('rbac-role-id');
        const roleKeyEl = document.getElementById('rbac-role-key');
        const roleTitleEl = document.getElementById('rbac-role-title');
        const roleActiveEl = document.getElementById('rbac-role-active');
        const roleSysBadge = document.getElementById('rbac-role-system-badge');
        if (roleIdEl) roleIdEl.value = role?.id || '';
        if (roleKeyEl) roleKeyEl.value = role?.role_key || '';
        if (roleTitleEl) roleTitleEl.value = role?.title || '';
        if (roleActiveEl) roleActiveEl.checked = String(role?.is_active ?? '1') === '1';
        if (roleSysBadge) roleSysBadge.classList.toggle('hidden', String(role?.is_system ?? '0') !== '1');

        const isSystemRole = String(role?.is_system ?? '0') === '1';
        const roleKeyVal = String(role?.role_key || '');
        if (roleKeyEl) roleKeyEl.disabled = isSystemRole; // system role_key is immutable
        const deleteBtn = document.getElementById('rbac-delete-role-btn');
        if (deleteBtn) {
            const canDelete = !!role && !isSystemRole && roleKeyVal !== 'admin';
            deleteBtn.classList.toggle('hidden', !canDelete);
        }

        this.state.rbacSelectedCapabilityKeys = Array.isArray(capabilityKeys) ? capabilityKeys.map(v => String(v)) : [];
        // Authoritative working copy: BOTH the matrix and the list view read/write this same set,
        // and saveLookupRbacCapabilities() emits exactly this set — so any capability not surfaced as
        // a column is preserved untouched, and a no-op edit round-trips to an identical grant set.
        this.state.rbacWorkingCaps = new Set(this.state.rbacSelectedCapabilityKeys);
        this.state.rbacSelectedScopes = Array.isArray(scopes) ? scopes : [];
        this.state.rbacCurrentRole = role || null;
        this.renderLookupRbacCapabilities();
        this.renderLookupRbacScopes();
        this.updateRbacRoleSummary();
    },
    renderLookupRbacCapabilities() {
        const view = this.state.rbacCapsView || 'matrix';
        if (view === 'list') this.renderRbacCapabilityList();
        else this.renderRbacCapabilityMatrix();
    },
    setRbacCapsView(view) {
        view = (view === 'list') ? 'list' : 'matrix';
        this.state.rbacCapsView = view;
        const matrixEl = document.getElementById('rbac-capabilities-matrix');
        const listEl = document.getElementById('rbac-capabilities-container');
        if (matrixEl) matrixEl.classList.toggle('hidden', view !== 'matrix');
        if (listEl) listEl.classList.toggle('hidden', view !== 'list');
        this._syncRbacCapsViewButtons();
        this.renderLookupRbacCapabilities();
    },
    _syncRbacCapsViewButtons() {
        const view = this.state.rbacCapsView || 'matrix';
        const set = (el, active) => {
            if (!el) return;
            el.classList.toggle('bg-cyan-600', active);
            el.classList.toggle('text-white', active);
            el.classList.toggle('bg-gray-800', !active);
            el.classList.toggle('text-gray-300', !active);
            el.setAttribute('aria-selected', active ? 'true' : 'false');
        };
        set(document.getElementById('rbac-caps-view-matrix-btn'), view === 'matrix');
        set(document.getElementById('rbac-caps-view-list-btn'), view === 'list');
    },
    // ---- shared helpers over the authoritative working set (this.state.rbacWorkingCaps) ----
    rbacCellState(keys, sel) {
        if (!keys || !keys.length) return 'empty';
        let on = 0;
        keys.forEach(k => { if (sel.has(k)) on++; });
        if (on === 0) return 'off';
        if (on === keys.length) return 'on';
        return 'mixed';
    },
    _rbacCountGranted(keys, sel) {
        let n = 0;
        (keys || []).forEach(k => { if (sel.has(k)) n++; });
        return n;
    },
    _rbacCapTitleMap() {
        const map = {};
        (this.state.rbacCatalog?.capabilities || []).forEach(c => { map[String(c.capability_key || '')] = String(c.title || ''); });
        return map;
    },
    _rbacModuleMatchesSearch(mod, search, capTitle) {
        if (!search) return true;
        if (String(mod.title || '').toLowerCase().includes(search)) return true;
        if (String(mod.key || '').toLowerCase().includes(search)) return true;
        return (mod.all_keys || []).some(k => k.toLowerCase().includes(search) || String(capTitle[k] || '').toLowerCase().includes(search));
    },
    _rbacDetectPreset(mod, sel, presets) {
        const granted = new Set((mod.all_keys || []).filter(k => sel.has(k)));
        for (const p of (presets || [])) {
            const expect = new Set();
            (p.actions || []).forEach(a => {
                const keys = a === 'advanced' ? (mod.advanced || []).map(x => x.key) : (mod.cells?.[a] || []);
                keys.forEach(k => expect.add(k));
            });
            if (expect.size === granted.size && [...expect].every(k => granted.has(k))) return p.key;
        }
        return '';
    },
    _rbacCellHtml(moduleKey, action, keys, sel, actionTitle, moduleTitle) {
        if (!keys || !keys.length) {
            return `<td class="rbac-cell"><span class="rbac-empty" title="این ماژول عملیات «${this.escapeHtml(actionTitle)}» را ندارد">—</span></td>`;
        }
        const state = this.rbacCellState(keys, sel);
        const cls = state === 'on' ? 'is-on' : (state === 'mixed' ? 'is-mixed' : 'is-off');
        const aria = state === 'on' ? 'true' : (state === 'mixed' ? 'mixed' : 'false');
        const icon = state === 'on' ? 'fa-check' : (state === 'mixed' ? 'fa-minus' : '');
        return `<td class="rbac-cell">
            <button type="button" class="rbac-chk rbac-act-${action} ${cls}" role="checkbox" aria-checked="${aria}"
                title="${this.escapeHtml(actionTitle)} — ${this.escapeHtml(moduleTitle)}"
                onclick="app.toggleRbacMatrixCell('${moduleKey}','${action}')">
                ${icon ? `<i class="fa-solid ${icon}"></i>` : ''}
            </button>
        </td>`;
    },
    renderRbacCapabilityMatrix() {
        const host = document.getElementById('rbac-capabilities-matrix');
        if (!host) return;
        const roleKey = this.state.rbacSelectedRoleKey || '';
        if (!roleKey) {
            host.innerHTML = '<div class="text-center text-gray-500 text-xs py-10">ابتدا یک نقش انتخاب کنید</div>';
            return;
        }
        const matrix = this.state.rbacCatalog?.matrix;
        if (!matrix || !Array.isArray(matrix.groups) || !matrix.groups.length) {
            // Backend without matrix support -> degrade gracefully to the flat list.
            this.state.rbacCapsView = 'list';
            const matrixEl = document.getElementById('rbac-capabilities-matrix');
            const listEl = document.getElementById('rbac-capabilities-container');
            if (matrixEl) matrixEl.classList.add('hidden');
            if (listEl) listEl.classList.remove('hidden');
            this._syncRbacCapsViewButtons();
            this.renderRbacCapabilityList();
            return;
        }
        const sel = this.state.rbacWorkingCaps || new Set();
        const actions = Array.isArray(matrix.actions) ? matrix.actions : [];
        const presets = Array.isArray(matrix.presets) ? matrix.presets : [];
        const search = (document.getElementById('rbac-capabilities-search')?.value || '').trim().toLowerCase();
        const capTitle = this._rbacCapTitleMap();
        const expanded = this.state.rbacMatrixExpanded instanceof Set ? this.state.rbacMatrixExpanded : (this.state.rbacMatrixExpanded = new Set());
        const actionIcon = { read: 'fa-eye', create: 'fa-plus', update: 'fa-pen', delete: 'fa-trash' };
        const index = {};
        const visible = [];
        const colCount = actions.length + 3;

        const thead = `<tr>
            <th class="rbac-mh rbac-mh-mod">ماژول</th>
            ${actions.map(a => `<th class="rbac-mh rbac-mh-${a.key}">
                <div class="rbac-mh-act" title="${this.escapeHtml(a.desc || '')}"><i class="fa-solid ${actionIcon[a.key] || 'fa-circle'}"></i><span>${this.escapeHtml(a.title)}</span></div>
                <button type="button" class="rbac-colall" title="انتخاب/حذف کل این ستون" onclick="app.toggleRbacMatrixColumn('${a.key}')"><i class="fa-solid fa-arrows-up-down"></i></button>
            </th>`).join('')}
            <th class="rbac-mh rbac-mh-adv">پیشرفته</th>
            <th class="rbac-mh rbac-mh-preset">پیش‌تنظیم</th>
        </tr>`;

        let bodyRows = '';
        matrix.groups.forEach(group => {
            const groupModules = (group.modules || []).filter(mod => this._rbacModuleMatchesSearch(mod, search, capTitle));
            if (!groupModules.length) return;
            bodyRows += `<tr class="rbac-grouprow"><td colspan="${colCount}">${this.escapeHtml(group.title)}</td></tr>`;
            groupModules.forEach(mod => {
                index[mod.key] = mod;
                visible.push(mod.key);
                const advKeys = (mod.advanced || []).map(a => a.key);
                const advState = this.rbacCellState(advKeys, sel);
                const cells = actions.map(a => this._rbacCellHtml(mod.key, a.key, mod.cells?.[a.key] || [], sel, a.title, mod.title)).join('');
                const presetVal = this._rbacDetectPreset(mod, sel, presets);
                const advBtn = advKeys.length
                    ? `<button type="button" class="rbac-adv-btn ${advState === 'on' ? 'is-on' : (advState === 'mixed' ? 'is-mixed' : 'is-off')}" onclick="app.toggleRbacMatrixAdvanced('${mod.key}')" title="نمایش دسترسی‌های پیشرفته">
                           <span>${this._rbacCountGranted(advKeys, sel)}/${advKeys.length}</span><i class="fa-solid ${expanded.has(mod.key) ? 'fa-chevron-up' : 'fa-chevron-down'}"></i>
                       </button>`
                    : '<span class="rbac-empty">—</span>';
                const presetSel = `<select class="rbac-preset-sel" title="اعمال پیش‌تنظیم روی این ماژول" onchange="app.applyRbacMatrixPreset('${mod.key}', this.value)">
                        <option value="">${presetVal ? '— سفارشی' : 'سفارشی'}</option>
                        ${presets.map(p => `<option value="${p.key}" ${presetVal === p.key ? 'selected' : ''}>${this.escapeHtml(p.title)}</option>`).join('')}
                    </select>`;
                bodyRows += `<tr class="rbac-modrow">
                    <td class="rbac-modcell"><div class="rbac-modtitle">${this.escapeHtml(mod.title)}</div><div class="rbac-modkey">${this.escapeHtml(mod.key)}</div></td>
                    ${cells}
                    <td class="rbac-cell">${advBtn}</td>
                    <td class="rbac-cell rbac-presetcell">${presetSel}</td>
                </tr>`;
                if (expanded.has(mod.key) && advKeys.length) {
                    const advItems = (mod.advanced || []).map(a => {
                        const on = sel.has(a.key) ? 'checked' : '';
                        return `<label class="rbac-advitem">
                            <input type="checkbox" class="accent-cyan-500 rbac-capability-checkbox" value="${this.escapeHtml(a.key)}" ${on} onchange="app.onRbacListCapToggle(this)">
                            <span class="rbac-advitem-title">${this.escapeHtml(a.title)}</span><span class="rbac-advitem-key">${this.escapeHtml(a.key)}</span>
                        </label>`;
                    }).join('');
                    bodyRows += `<tr class="rbac-advrow"><td colspan="${colCount}"><div class="rbac-advgrid">${advItems}</div></td></tr>`;
                }
            });
        });

        this.state.rbacMatrixIndex = index;
        this.state.rbacMatrixVisibleModules = visible;
        if (!bodyRows) {
            host.innerHTML = '<div class="text-center text-gray-500 text-xs py-10">چیزی یافت نشد</div>';
            return;
        }
        host.innerHTML = `<div class="rbac-matrix-scroll"><table class="rbac-matrix"><thead>${thead}</thead><tbody>${bodyRows}</tbody></table></div>
            <div class="rbac-matrix-legend">
                <span><i class="fa-solid fa-check rbac-lg-on"></i> دارد</span>
                <span><i class="fa-solid fa-minus rbac-lg-mixed"></i> جزئی</span>
                <span class="rbac-lg-muted">— این عملیات برای ماژول تعریف نشده</span>
            </div>`;
    },
    renderRbacCapabilityList() {
        const container = document.getElementById('rbac-capabilities-container');
        if (!container) return;
        const roleKey = this.state.rbacSelectedRoleKey || '';
        if (!roleKey) {
            container.innerHTML = '<div class="text-center text-gray-500 text-xs py-6">ابتدا یک نقش انتخاب کنید</div>';
            return;
        }
        const search = (document.getElementById('rbac-capabilities-search')?.value || '').trim().toLowerCase();
        const caps = Array.isArray(this.state.rbacCatalog?.capabilities) ? this.state.rbacCatalog.capabilities : [];
        const selected = this.state.rbacWorkingCaps || new Set();
        const filtered = caps.filter(c => {
            const key = String(c.capability_key || '');
            const title = String(c.title || '');
            if (!search) return true;
            return key.toLowerCase().includes(search) || title.toLowerCase().includes(search);
        });
        if (!filtered.length) {
            container.innerHTML = '<div class="text-center text-gray-500 text-xs py-6">چیزی یافت نشد</div>';
            return;
        }
        const groups = {};
        filtered.forEach(c => {
            const g = String(c.category || 'other');
            if (!groups[g]) groups[g] = [];
            groups[g].push(c);
        });
        container.innerHTML = Object.keys(groups).sort().map(cat => `
            <div>
                <div class="text-xs text-cyan-300 font-bold mb-2">${this.escapeHtml(cat)}</div>
                <div class="space-y-1">
                    ${groups[cat].map(c => {
                        const key = String(c.capability_key || '');
                        const checked = selected.has(key) ? 'checked' : '';
                        return `<label class="flex items-start gap-2 text-xs text-gray-300">
                            <input type="checkbox" class="accent-cyan-500 mt-0.5 rbac-capability-checkbox" value="${this.escapeHtml(key)}" ${checked} onchange="app.onRbacListCapToggle(this)">
                            <span><span class="text-gray-100">${this.escapeHtml(c.title || key)}</span><br><span class="text-[10px] text-gray-500 font-mono">${this.escapeHtml(key)}</span></span>
                        </label>`;
                    }).join('')}
                </div>
            </div>
        `).join('');
    },
    toggleRbacMatrixCell(moduleKey, action) {
        const mod = this.state.rbacMatrixIndex?.[moduleKey];
        if (!mod) return;
        const keys = action === 'advanced' ? (mod.advanced || []).map(a => a.key) : (mod.cells?.[action] || []);
        if (!keys.length) return;
        const sel = this.state.rbacWorkingCaps;
        if (this.rbacCellState(keys, sel) === 'on') keys.forEach(k => sel.delete(k));
        else keys.forEach(k => sel.add(k));
        this.renderRbacCapabilityMatrix();
        this.updateRbacRoleSummary();
    },
    toggleRbacMatrixColumn(action) {
        const idx = this.state.rbacMatrixIndex || {};
        const visible = this.state.rbacMatrixVisibleModules || Object.keys(idx);
        const sel = this.state.rbacWorkingCaps;
        const allKeys = [];
        visible.forEach(mk => { const mod = idx[mk]; if (mod) (mod.cells?.[action] || []).forEach(k => allKeys.push(k)); });
        if (!allKeys.length) return;
        const everyOn = allKeys.every(k => sel.has(k));
        if (everyOn) allKeys.forEach(k => sel.delete(k));
        else allKeys.forEach(k => sel.add(k));
        this.renderRbacCapabilityMatrix();
        this.updateRbacRoleSummary();
    },
    applyRbacMatrixPreset(moduleKey, presetKey) {
        if (!presetKey) return;
        const mod = this.state.rbacMatrixIndex?.[moduleKey];
        const preset = (this.state.rbacCatalog?.matrix?.presets || []).find(p => p.key === presetKey);
        if (!mod || !preset) return;
        const sel = this.state.rbacWorkingCaps;
        (mod.all_keys || []).forEach(k => sel.delete(k));
        (preset.actions || []).forEach(a => {
            const keys = a === 'advanced' ? (mod.advanced || []).map(x => x.key) : (mod.cells?.[a] || []);
            keys.forEach(k => sel.add(k));
        });
        this.renderRbacCapabilityMatrix();
        this.updateRbacRoleSummary();
    },
    toggleRbacMatrixAdvanced(moduleKey) {
        if (!(this.state.rbacMatrixExpanded instanceof Set)) this.state.rbacMatrixExpanded = new Set();
        const ex = this.state.rbacMatrixExpanded;
        if (ex.has(moduleKey)) ex.delete(moduleKey); else ex.add(moduleKey);
        this.renderRbacCapabilityMatrix();
    },
    onRbacListCapToggle(el) {
        const key = el.value;
        const sel = this.state.rbacWorkingCaps || (this.state.rbacWorkingCaps = new Set());
        if (el.checked) sel.add(key); else sel.delete(key);
        this.updateRbacRoleSummary();
        if ((this.state.rbacCapsView || 'matrix') === 'matrix') this.renderRbacCapabilityMatrix();
    },
    updateRbacRoleSummary() {
        const el = document.getElementById('rbac-role-summary');
        if (!el) return;
        const role = this.state.rbacCurrentRole;
        if (!role) {
            el.innerHTML = '<i class="fa-regular fa-hand-pointer text-gray-500"></i> یک نقش را برای مدیریت دسترسی‌ها انتخاب کنید';
            return;
        }
        const count = (this.state.rbacWorkingCaps && this.state.rbacWorkingCaps.size) || 0;
        const sys = String(role.is_system ?? '0') === '1';
        const dirty = this._rbacCapsDirty();
        el.innerHTML = `
            <span class="text-gray-100 font-bold">${this.escapeHtml(role.title || role.role_key || '')}</span>
            <span class="text-gray-600">·</span>
            <span class="text-cyan-300">${count} دسترسی فعال</span>
            ${sys ? '<span class="text-[10px] px-2 py-0.5 rounded bg-gray-700 text-gray-300 mr-1">سیستمی</span>' : ''}
            ${dirty ? '<span class="text-[10px] px-2 py-0.5 rounded bg-amber-600/30 text-amber-300 mr-1"><i class="fa-solid fa-circle-exclamation ml-1"></i>تغییرات ذخیره‌نشده</span>' : ''}
        `;
    },
    _rbacCapsDirty() {
        const orig = new Set((this.state.rbacSelectedCapabilityKeys || []).map(v => String(v)));
        const cur = this.state.rbacWorkingCaps || new Set();
        if (orig.size !== cur.size) return true;
        for (const k of cur) if (!orig.has(k)) return true;
        return false;
    },
    renderLookupRbacScopes() {
        const container = document.getElementById('rbac-scopes-container');
        if (!container) return;
        const roleKey = this.state.rbacSelectedRoleKey || '';
        if (!roleKey) {
            container.innerHTML = '<div class="text-center text-gray-500 text-xs py-6 border border-gray-800 rounded-lg bg-gray-900/30">ابتدا یک نقش انتخاب کنید</div>';
            return;
        }
        const scopeMap = {};
        (this.state.rbacSelectedScopes || []).forEach(s => { scopeMap[String(s.resource_key || '')] = s; });
        const users = Array.isArray(this.state.rbacUsersForScopes) ? this.state.rbacUsersForScopes : [];
        const userOptionsHtml = users.map(u => {
            const id = String(u.id || '');
            if (!id) return '';
            const name = u.display_name || `${u.first_name || ''} ${u.last_name || ''}`.trim() || u.email || id;
            return `<option value="${this.escapeHtml(id)}">${this.escapeHtml(name)}</option>`;
        }).join('');
        const resourceCatalog = this.state.rbacCatalog?.resources || {};
        const resources = Object.keys(resourceCatalog).length ? Object.keys(resourceCatalog) : ['dashboard', 'leads', 'deals', 'payments', 'kpi', 'performance'];
        const scopeTypes = Array.isArray(this.state.rbacCatalog?.scope_types) ? this.state.rbacCatalog.scope_types : ['none', 'self', 'selected_users', 'all'];
        const typeLabels = {
            none: 'بدون دسترسی داده',
            self: 'فقط خودم',
            all: 'همه کاربران',
            selected_users: 'کاربران منتخب'
        };
        container.innerHTML = resources.map(resourceKey => {
            const meta = resourceCatalog[resourceKey] || {};
            const s = scopeMap[resourceKey] || { resource_key: resourceKey, scope_type: (roleKey === 'admin' ? 'all' : 'self'), selected_user_ids: [] };
            const scopeType = String(s.scope_type || 'self');
            const selectedUsers = new Set((s.selected_user_ids || []).map(v => String(v)));
            return `
                <div class="border border-gray-800 rounded-lg p-3 bg-gray-900/30" data-rbac-scope-resource="${resourceKey}">
                    <div class="text-xs text-gray-200 mb-2 font-bold">${this.escapeHtml(meta.title || resourceKey)}</div>
                    <select class="input bg-gray-800 text-xs mb-2" name="scope_type" onchange="app.toggleRbacScopeUsers(this)">
                        ${scopeTypes.map(type => `<option value="${this.escapeHtml(type)}" ${scopeType === type ? 'selected' : ''}>${this.escapeHtml(typeLabels[type] || type)}</option>`).join('')}
                    </select>
                    <div class="rbac-scope-users-wrap ${scopeType === 'selected_users' ? '' : 'hidden'}">
                        <select class="input bg-gray-800 text-xs w-full" name="selected_user_ids" multiple size="6">
                            ${userOptionsHtml.replace(/<option value=\"([^\"]+)\">/g, (m, id) => `<option value="${id}" ${selectedUsers.has(String(id)) ? 'selected' : ''}>`)}
                        </select>
                    </div>
                </div>
            `;
        }).join('');
    },
    toggleRbacScopeUsers(selectEl) {
        const card = selectEl?.closest('[data-rbac-scope-resource]');
        if (!card) return;
        const wrap = card.querySelector('.rbac-scope-users-wrap');
        if (!wrap) return;
        wrap.classList.toggle('hidden', (selectEl.value || '') !== 'selected_users');
    },
    async saveLookupRbacRole() {
        const roleKey = (document.getElementById('rbac-role-key')?.value || '').trim();
        const title = (document.getElementById('rbac-role-title')?.value || '').trim();
        const id = document.getElementById('rbac-role-id')?.value || '';
        const isActive = document.getElementById('rbac-role-active')?.checked ? '1' : '0';
        if (!roleKey || !title) {
            Swal.fire('خطا', 'role_key و عنوان نقش الزامی است', 'error');
            return;
        }
        const res = await this.req('rbac_save_role', { id, role_key: roleKey, title, is_active: isActive });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ذخیره نقش', 'error');
            return;
        }
        this.state.rbacSelectedRoleKey = roleKey;
        await this.loadLookupRbacManagerData(true);
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'نقش ذخیره شد', showConfirmButton: false, timer: 1500 });
    },
    async cloneLookupRbacRole() {
        const newRoleKey = (document.getElementById('rbac-clone-role-key')?.value || '').trim();
        const newTitle = (document.getElementById('rbac-clone-role-title')?.value || '').trim();
        const templateRoleKey = (document.getElementById('rbac-clone-template-role')?.value || '').trim();
        if (!newRoleKey || !newTitle || !templateRoleKey) {
            Swal.fire('خطا', 'فیلدهای ساخت از تمپلیت کامل نیست', 'error');
            return;
        }
        const res = await this.req('rbac_clone_role', {
            new_role_key: newRoleKey,
            new_title: newTitle,
            template_role_key: templateRoleKey
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ساخت نقش از تمپلیت', 'error');
            return;
        }
        this.state.rbacSelectedRoleKey = newRoleKey;
        await this.loadLookupRbacManagerData(true);
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        const sel = document.getElementById('rbac-role-select');
        if (sel) sel.value = newRoleKey;
        await this.onLookupRbacRoleChange();
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'نقش جدید ساخته شد', showConfirmButton: false, timer: 1500 });
    },
    async saveLookupRbacCapabilities() {
        const roleKey = this.state.rbacSelectedRoleKey || '';
        if (!roleKey) {
            Swal.fire('خطا', 'ابتدا نقش را انتخاب کنید', 'error');
            return;
        }
        const capabilityKeys = Array.from(this.state.rbacWorkingCaps || []);
        const res = await this.req('rbac_save_role_capabilities', {
            role_key: roleKey,
            capability_keys: JSON.stringify(capabilityKeys)
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ذخیره دسترسی‌ها', 'error');
            return;
        }
        await this.onLookupRbacRoleChange();
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'دسترسی‌ها ذخیره شد', showConfirmButton: false, timer: 1500 });
    },
    async saveLookupRbacScopes() {
        const roleKey = this.state.rbacSelectedRoleKey || '';
        if (!roleKey) {
            Swal.fire('خطا', 'ابتدا نقش را انتخاب کنید', 'error');
            return;
        }
        const cards = Array.from(document.querySelectorAll('[data-rbac-scope-resource]'));
        const scopes = cards.map(card => {
            const resourceKey = card.dataset.rbacScopeResource || '';
            const scopeType = card.querySelector('[name="scope_type"]')?.value || 'self';
            const selectedUserIds = Array.from(card.querySelector('[name="selected_user_ids"]')?.selectedOptions || []).map(o => parseInt(o.value, 10)).filter(v => Number.isFinite(v) && v > 0);
            return { resource_key: resourceKey, scope_type: scopeType, selected_user_ids: selectedUserIds };
        });
        const res = await this.req('rbac_save_role_scopes', {
            role_key: roleKey,
            scopes: JSON.stringify(scopes)
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ذخیره Scope', 'error');
            return;
        }
        await this.onLookupRbacRoleChange();
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'Scope ذخیره شد', showConfirmButton: false, timer: 1500 });
    },
    async deleteLookupRbacRole() {
        const roleKey = this.state.rbacSelectedRoleKey || (document.getElementById('rbac-role-key')?.value || '').trim();
        if (!roleKey) {
            Swal.fire('خطا', 'ابتدا یک نقش انتخاب کنید', 'error');
            return;
        }
        const role = (this.state.rbacCatalog?.roles || []).find(r => String(r.role_key) === roleKey);
        if (role && String(role.is_system || '0') === '1') {
            Swal.fire('خطا', 'نقش سیستمی قابل حذف نیست', 'error');
            return;
        }
        const result = await Swal.fire({
            title: 'حذف نقش؟',
            text: `نقش «${roleKey}» و تمام دسترسی‌ها و محدوده‌های آن حذف می‌شود.`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'حذف',
            cancelButtonText: 'انصراف',
            confirmButtonColor: '#b91c1c'
        });
        if (!result.isConfirmed) return;
        const res = await this.req('rbac_delete_role', { role_key: roleKey });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در حذف نقش', 'error');
            return;
        }
        this.state.rbacSelectedRoleKey = '';
        const sel = document.getElementById('rbac-role-select');
        if (sel) sel.value = '';
        await this.loadLookupRbacManagerData(true);
        this.renderLookupRbacRoleEditor(null, [], []);
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'نقش حذف شد', showConfirmButton: false, timer: 1500 });
    },
    async onLookupRbacUserChange() {
        const userId = document.getElementById('rbac-user-select')?.value || '';
        const body = document.getElementById('rbac-user-override-body');
        if (!userId) {
            if (body) body.classList.add('hidden');
            this.state.rbacUserOverrides = null;
            return;
        }
        const res = await this.req('rbac_user_permissions', { user_id: userId });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در دریافت دسترسی کاربر', 'error');
            return;
        }
        this.state.rbacUserOverrides = {
            user_id: userId,
            capability_overrides: res.capability_overrides || {},
            scopes: Array.isArray(res.scopes) ? res.scopes : []
        };
        if (body) body.classList.remove('hidden');
        this.renderLookupRbacUserCapabilities();
        this.renderLookupRbacUserScopes();
    },
    renderLookupRbacUserCapabilities() {
        const container = document.getElementById('rbac-user-capabilities-container');
        if (!container) return;
        const overrides = this.state.rbacUserOverrides?.capability_overrides || {};
        const search = (document.getElementById('rbac-user-capabilities-search')?.value || '').trim().toLowerCase();
        const caps = Array.isArray(this.state.rbacCatalog?.capabilities) ? this.state.rbacCatalog.capabilities : [];
        const filtered = caps.filter(c => {
            const key = String(c.capability_key || '');
            const title = String(c.title || '');
            return !search || key.toLowerCase().includes(search) || title.toLowerCase().includes(search);
        });
        if (!filtered.length) {
            container.innerHTML = '<div class="text-center text-gray-500 text-xs py-6">چیزی یافت نشد</div>';
            return;
        }
        const groups = {};
        filtered.forEach(c => { const g = String(c.category || 'other'); (groups[g] = groups[g] || []).push(c); });
        container.innerHTML = Object.keys(groups).sort().map(cat => `
            <div>
                <div class="text-xs text-amber-300 font-bold mb-2">${this.escapeHtml(cat)}</div>
                <div class="space-y-1">
                    ${groups[cat].map(c => {
                        const key = String(c.capability_key || '');
                        const cur = overrides[key] === 'allow' ? 'allow' : (overrides[key] === 'deny' ? 'deny' : '');
                        return `<div class="flex items-center justify-between gap-2 text-xs">
                            <span class="text-gray-300 truncate" title="${this.escapeHtml(key)}">${this.escapeHtml(c.title || key)}</span>
                            <select class="input bg-gray-800 text-[11px] rbac-user-cap-select" data-cap="${this.escapeHtml(key)}" style="width:104px">
                                <option value="" ${cur === '' ? 'selected' : ''}>ارث‌بری</option>
                                <option value="allow" ${cur === 'allow' ? 'selected' : ''}>مجاز</option>
                                <option value="deny" ${cur === 'deny' ? 'selected' : ''}>ممنوع</option>
                            </select>
                        </div>`;
                    }).join('')}
                </div>
            </div>
        `).join('');
    },
    renderLookupRbacUserScopes() {
        const container = document.getElementById('rbac-user-scopes-container');
        if (!container) return;
        const scopeMap = {};
        (this.state.rbacUserOverrides?.scopes || []).forEach(s => { scopeMap[String(s.resource_key || '')] = s; });
        const users = Array.isArray(this.state.rbacUsersForScopes) ? this.state.rbacUsersForScopes : [];
        const userOptionsHtml = users.map(u => {
            const id = String(u.id || '');
            if (!id) return '';
            const name = u.display_name || `${u.first_name || ''} ${u.last_name || ''}`.trim() || u.email || id;
            return `<option value="${this.escapeHtml(id)}">${this.escapeHtml(name)}</option>`;
        }).join('');
        const resourceCatalog = this.state.rbacCatalog?.resources || {};
        const resources = Object.keys(resourceCatalog).length ? Object.keys(resourceCatalog) : ['dashboard', 'leads', 'deals', 'payments', 'kpi', 'performance'];
        const scopeTypes = Array.isArray(this.state.rbacCatalog?.scope_types) ? this.state.rbacCatalog.scope_types : ['none', 'self', 'selected_users', 'all'];
        const typeLabels = { none: 'بدون دسترسی داده', self: 'فقط خودش', all: 'همه کاربران', selected_users: 'کاربران منتخب' };
        container.innerHTML = resources.map(resourceKey => {
            const meta = resourceCatalog[resourceKey] || {};
            const s = scopeMap[resourceKey];
            const scopeType = s ? String(s.scope_type || 'self') : '';
            const selectedUsers = new Set(((s?.selected_user_ids) || []).map(v => String(v)));
            return `
                <div class="border border-gray-800 rounded-lg p-3 bg-gray-900/30" data-rbac-user-scope-resource="${resourceKey}">
                    <div class="text-xs text-gray-200 mb-2 font-bold">${this.escapeHtml(meta.title || resourceKey)}</div>
                    <select class="input bg-gray-800 text-xs mb-2" name="scope_type" onchange="app.toggleRbacUserScopeUsers(this)">
                        <option value="" ${scopeType === '' ? 'selected' : ''}>ارث‌بری از نقش</option>
                        ${scopeTypes.map(type => `<option value="${this.escapeHtml(type)}" ${scopeType === type ? 'selected' : ''}>${this.escapeHtml(typeLabels[type] || type)}</option>`).join('')}
                    </select>
                    <div class="rbac-user-scope-users-wrap ${scopeType === 'selected_users' ? '' : 'hidden'}">
                        <select class="input bg-gray-800 text-xs w-full" name="selected_user_ids" multiple size="5">
                            ${userOptionsHtml.replace(/<option value=\"([^\"]+)\">/g, (m, id) => `<option value="${id}" ${selectedUsers.has(String(id)) ? 'selected' : ''}>`)}
                        </select>
                    </div>
                </div>
            `;
        }).join('');
    },
    toggleRbacUserScopeUsers(selectEl) {
        const card = selectEl?.closest('[data-rbac-user-scope-resource]');
        if (!card) return;
        const wrap = card.querySelector('.rbac-user-scope-users-wrap');
        if (!wrap) return;
        wrap.classList.toggle('hidden', (selectEl.value || '') !== 'selected_users');
    },
    async saveLookupRbacUserCapabilities() {
        const userId = this.state.rbacUserOverrides?.user_id || document.getElementById('rbac-user-select')?.value || '';
        if (!userId) {
            Swal.fire('خطا', 'ابتدا یک کاربر انتخاب کنید', 'error');
            return;
        }
        const allow = [];
        const deny = [];
        document.querySelectorAll('.rbac-user-cap-select').forEach(el => {
            const v = el.value;
            const k = el.dataset.cap;
            if (!k) return;
            if (v === 'allow') allow.push(k);
            else if (v === 'deny') deny.push(k);
        });
        const res = await this.req('rbac_save_user_capabilities', {
            user_id: userId,
            allow_keys: JSON.stringify(allow),
            deny_keys: JSON.stringify(deny)
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ذخیره دسترسی کاربر', 'error');
            return;
        }
        await this.onLookupRbacUserChange();
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'دسترسی کاربر ذخیره شد', showConfirmButton: false, timer: 1500 });
    },
    async saveLookupRbacUserScopes() {
        const userId = this.state.rbacUserOverrides?.user_id || document.getElementById('rbac-user-select')?.value || '';
        if (!userId) {
            Swal.fire('خطا', 'ابتدا یک کاربر انتخاب کنید', 'error');
            return;
        }
        const cards = Array.from(document.querySelectorAll('[data-rbac-user-scope-resource]'));
        const scopes = [];
        cards.forEach(card => {
            const scopeType = card.querySelector('[name="scope_type"]')?.value || '';
            if (!scopeType) return; // "inherit" -> omit so the override is removed (replace semantics)
            const resourceKey = card.dataset.rbacUserScopeResource || '';
            const selectedUserIds = Array.from(card.querySelector('[name="selected_user_ids"]')?.selectedOptions || []).map(o => parseInt(o.value, 10)).filter(v => Number.isFinite(v) && v > 0);
            scopes.push({ resource_key: resourceKey, scope_type: scopeType, selected_user_ids: selectedUserIds });
        });
        const res = await this.req('rbac_save_user_scopes', {
            user_id: userId,
            scopes: JSON.stringify(scopes)
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در ذخیره Scope کاربر', 'error');
            return;
        }
        await this.onLookupRbacUserChange();
        this.state.rbacCurrentPermissionsLoaded = false;
        await this.loadCurrentRbacPermissions(true);
        Swal.fire({ toast: true, position: 'bottom-end', icon: 'success', title: 'Scope کاربر ذخیره شد', showConfirmButton: false, timer: 1500 });
    },
    resetDealServiceLookupAutofillState() {
        const fieldIds = ['deal-service-cost', 'deal-payable-amount', 'deal-price-list-code'];
        fieldIds.forEach((id) => {
            const el = document.getElementById(id);
            if (!el) return;
            delete el.dataset.autofillSource;
            delete el.dataset.autofillService;
            delete el.dataset.autofillValue;
            delete el.dataset.autofillDirty;
        });
        const titleField = document.getElementById('deal-title');
        if (titleField) {
            delete titleField.dataset.lastSelectedService;
        }
    },
    markDealFieldLookupAutofill(el, serviceValue, value) {
        if (!el) return;
        el.dataset.autofillSource = 'lookup';
        el.dataset.autofillService = serviceValue || '';
        el.dataset.autofillValue = String(value ?? '');
        el.dataset.autofillDirty = '0';
    },
    clearDealFieldLookupAutofill(el) {
        if (!el) return;
        delete el.dataset.autofillSource;
        delete el.dataset.autofillService;
        delete el.dataset.autofillValue;
        delete el.dataset.autofillDirty;
    },
    isDealFieldLookupAutofillReusable(el) {
        if (!el) return false;
        return el.dataset.autofillSource === 'lookup' && String(el.dataset.autofillDirty || '0') !== '1';
    },
    initDealServiceLookupAutofillTracking() {
        if (this.state._dealLookupAutofillTrackingInitialized) return;
        this.state._dealLookupAutofillTrackingInitialized = true;

        const markDirtyIfNeeded = (el, normalizer) => {
            if (!el) return;
            if (el.dataset.autofillSource !== 'lookup') return;
            const current = normalizer(el.value || '');
            const autoVal = String(el.dataset.autofillValue || '');
            el.dataset.autofillDirty = current === autoVal ? '0' : '1';
        };

        document.addEventListener('input', (e) => {
            const el = e.target;
            if (!el || !(el instanceof HTMLElement)) return;
            if (el.id === 'deal-payable-amount') {
                markDirtyIfNeeded(el, (v) => formatMoneyString(v));
                return;
            }
            if (el.id === 'deal-price-list-code') {
                markDirtyIfNeeded(el, (v) => String(v ?? '').trim());
            }
        });
    },
    setDealServiceCostLookupLock(locked) {
        const serviceCostField = document.getElementById('deal-service-cost');
        const payableAmountField = document.getElementById('deal-payable-amount');
        const priceListCodeField = document.getElementById('deal-price-list-code');
        [serviceCostField, payableAmountField, priceListCodeField].forEach((field) => {
            if (!field) return;
            field.readOnly = !!locked;
            field.dataset.lookupPriceLocked = locked ? '1' : '';
            field.classList.toggle('bg-gray-900', !!locked);
            field.classList.toggle('cursor-not-allowed', !!locked);
            field.classList.toggle('text-gray-300', !!locked);
        });
    },
    applyDealServiceLookupPricing() {
        const dealId = document.getElementById('deal-id')?.value?.trim() || '';

        const titleField = document.getElementById('deal-title');
        const serviceCostField = document.getElementById('deal-service-cost');
        const payableAmountField = document.getElementById('deal-payable-amount');
        const priceListCodeField = document.getElementById('deal-price-list-code');
        const hasDiscountField = document.getElementById('deal-has-discount');
        const discountTypeField = document.getElementById('deal-discount-type');
        const discountAmountField = document.getElementById('deal-discount-amount');
        if (!titleField || !serviceCostField || !payableAmountField || !priceListCodeField) return;

        const selectedService = (titleField.value || '').trim();
        const previousSelectedService = String(titleField.dataset.lastSelectedService || '').trim();
        const serviceChanged = previousSelectedService !== '' && previousSelectedService !== selectedService;
        const pricingLocked = String(payableAmountField.dataset.lookupPriceLocked || '') === '1'
            || payableAmountField.readOnly === true;
        const fixedPrice = this.getAllowedServiceFixedPrice(selectedService);
        const fixedPriceListCode = this.getAllowedServicePriceListCode(selectedService);
        const hasDiscount = String(hasDiscountField?.value || '0') === '1';
        const discountType = String(discountTypeField?.value || '').trim();
        const discountAmount = parseMoneyValue(discountAmountField?.value || 0);
        const effectiveDiscount = hasDiscount && discountType !== 'refund' ? discountAmount : 0;
        const computedPayable = fixedPrice ? Math.max(0, fixedPrice - effectiveDiscount) : 0;

        const serviceCostCurrent = parseMoneyValue(serviceCostField.value || '');
        const payableCurrent = parseMoneyValue(payableAmountField.value || '');
        const priceListCurrent = String(priceListCodeField.value || '').trim();

        const canFillServiceCost =
            serviceChanged ||
            !dealId ||
            serviceCostCurrent <= 0 ||
            this.isDealFieldLookupAutofillReusable(serviceCostField);
        const canFillPayable =
            serviceChanged ||
            !dealId ||
            payableCurrent <= 0 ||
            this.isDealFieldLookupAutofillReusable(payableAmountField);
        const canFillPriceListCode =
            serviceChanged ||
            !dealId ||
            priceListCurrent === '' ||
            this.isDealFieldLookupAutofillReusable(priceListCodeField);

        if (!fixedPrice) {
            if (this.isDealFieldLookupAutofillReusable(serviceCostField)) {
                serviceCostField.value = '';
                this.clearDealFieldLookupAutofill(serviceCostField);
            }
            if (this.isDealFieldLookupAutofillReusable(payableAmountField)) {
                payableAmountField.value = '';
                this.clearDealFieldLookupAutofill(payableAmountField);
            }
            this.setDealServiceCostLookupLock(false);
        }
        if (fixedPrice && canFillServiceCost) {
            const formatted = formatMoneyString(fixedPrice);
            serviceCostField.value = formatted;
            this.markDealFieldLookupAutofill(serviceCostField, selectedService, formatted);
            this.setDealServiceCostLookupLock(true);
        }
        if (fixedPrice && canFillPayable) {
            const formatted = formatMoneyString(computedPayable);
            payableAmountField.value = formatted;
            this.markDealFieldLookupAutofill(payableAmountField, selectedService, formatted);
        }
        if (!fixedPriceListCode && this.isDealFieldLookupAutofillReusable(priceListCodeField)) {
            priceListCodeField.value = '';
            this.clearDealFieldLookupAutofill(priceListCodeField);
        }
        if (fixedPriceListCode && canFillPriceListCode) {
            priceListCodeField.value = fixedPriceListCode;
            this.markDealFieldLookupAutofill(priceListCodeField, selectedService, fixedPriceListCode);
        }
        if (fixedPrice) {
            this.setDealServiceCostLookupLock(true);
        }
        titleField.dataset.lastSelectedService = selectedService;
    },
    getFallbackActivityTypes() {
        const items = this.getLookupItems('activity_types_fallback');
        if (!items.length) return [];
        return items.map(item => {
            let meta = {};
            if (item.meta_json) {
                try { meta = JSON.parse(item.meta_json); } catch (_) { meta = {}; }
            }
            return {
                Id: item.value ?? item.title,
                Title: item.title ?? item.value,
                Direction: meta.direction || 'outgoing',
                Category: meta.category || 'other',
                IsDisabled: false
            };
        });
    },

    applyLookupsToStaticSelects() {
        this.populateSelectOptions('lead-source', this.getLookupOptions('lead_sources'));
        this.populateSelectOptions('lead-content-topic', this.getLookupOptions('content_topics'));
        this.populateSelectOptions('deal-source', this.getLookupOptions('lead_sources'));

        this.renderDealTitleOptions();
        this.populateSelectOptions('deal-status', this.getLookupOptions('deal_statuses'));
        this.populateSelectOptions('deal-pipeline-stage', this.getLookupOptions('pipeline_stages'));
        this.populateSelectOptions('deals-pipeline-filter', [
            { value: 'all', label: 'همه مراحل' },
            ...this.getLookupOptions('pipeline_stages')
        ]);
        this.populateSelectOptions('deals-status-filter', [
            { value: 'all', label: 'همه وضعیت‌ها' },
            ...this.getLookupOptions('deal_statuses')
        ]);
        this.populateSelectOptions('activity-virtual-stage', this.getLookupOptions('activity_stages'));

        this.populateSelectOptions('lead-customer-level', this.getLookupOptions('customer_levels'));
        this.populateSelectOptions('leads-customer-level-filter', this.getLookupOptions('customer_levels'));
        this.populateSelectOptions('lead-activity-financial-level', this.getLookupOptions('financial_levels'));
        this.populateSelectOptions('lead-activity-asset', this.getLookupOptions('asset_ranges'));
        this.populateSelectOptions('lead-activity-income', this.getLookupOptions('income_ranges'));

        this.populateSelectOptions('deal-discount-type', this.getLookupOptions('discount_types'));
        this.populateSelectOptions('deal-discount-occasion', this.getLookupOptions('discount_occasions'));

        this.populateSelectOptions('activity-failure-reason', this.getLookupOptions('failure_reasons'));
        this.populateSelectOptions('deal-failure-reason', this.getLookupOptions('failure_reasons'));
        this.populateSelectOptions('failure-reason', this.getLookupOptions('failure_reasons'));

        this.populateSelectOptions('activity-reminder-hours', this.getLookupOptions('reminder_hours'));
        this.populateSelectOptions('edit-activity-reminder-hours', this.getLookupOptions('reminder_hours'));
        this.populateSelectOptions('activity-reminder-minutes', this.getLookupOptions('reminder_minutes'));
        this.populateSelectOptions('edit-activity-reminder-minutes', this.getLookupOptions('reminder_minutes'));

        this.populateSelectOptions('edit-role-select', this.getUserRoleOptions());

        const roleOptions = this.getUserRoleOptions();
        document.querySelectorAll('select[name="role"]').forEach(sel => {
            const current = sel.value;
            sel.innerHTML = '<option value="">Select role</option>';
            roleOptions.forEach(opt => {
                const option = document.createElement('option');
                option.value = opt.value;
                option.textContent = opt.label || opt.value;
                sel.appendChild(option);
            });
            if (current) sel.value = current;
        });

        this.handleSourceChange();
        this.handleDealSourceChange();
    },

    getUserRoleOptions() {
        const merged = new Map();
        (this.getLookupOptions('user_roles') || []).forEach(opt => {
            const value = (opt?.value || '').toString().trim();
            if (!value) return;
            merged.set(value, { value, label: opt.label || value });
        });

        const rbacRoles = (this.state?.rbacCatalog?.roles || []);
        rbacRoles.forEach(role => {
            const value = ((role && role.role_key) || '').toString().trim();
            if (!value) return;
            const title = ((role && role.title) || value).toString().trim();
            if (!merged.has(value)) {
                merged.set(value, { value, label: title });
            }
        });

        return Array.from(merged.values()).sort((a, b) => (a.label || a.value).localeCompare((b.label || b.value), 'fa'));
    },

    getUserRoleLabel(roleKey) {
        const key = (roleKey || '').toString().trim();
        if (!key) return '-';
        const staticMap = {
            admin: 'مدیر',
            agent: 'کارشناس',
            crm_specialist: 'کارشناس CRM',
            accountant: 'حسابدار'
        };
        if (staticMap[key]) return staticMap[key];
        const match = this.getUserRoleOptions().find(opt => opt.value === key);
        return match?.label || key;
    },

    handleSourceChange() {
        const source = document.getElementById('lead-source')?.value || '';
        const detailSelect = document.getElementById('lead-source-detail');
        if(!detailSelect) return;
        const previous = detailSelect.value || '';
        let options = this.getLookupChildOptions('lead_source_details', source);
        if (!options.length) {
            options = [{ value: 'general', label: 'عمومی' }];
        }
        detailSelect.innerHTML = '<option value="">جزئیات شیوه آشنایی</option>';
        options.forEach(opt => {
            const el = document.createElement('option');
            el.value = opt.value;
            el.textContent = opt.label;
            detailSelect.appendChild(el);
        });
        detailSelect.disabled = false;
        detailSelect.required = false;
        // Default to the placeholder (جزئیات شیوه آشنایی) instead of auto-selecting the
        // first option; keep a prior selection only when it still exists in the new list.
        detailSelect.value = previous && options.some(opt => String(opt.value) === String(previous)) ? previous : '';
    },

    handleDealSourceChange() {
        const source = document.getElementById('deal-source')?.value || '';
        const detailSelect = document.getElementById('deal-source-detail');
        if (!detailSelect) return;
        const previous = detailSelect.value || '';
        let options = this.getLookupChildOptions('lead_source_details', source);
        if (!options.length) {
            options = this.getLookupOptions('lead_source_details');
        }
        detailSelect.innerHTML = '<option value="">جزئیات شیوه آشنایی</option>';
        options.forEach(opt => {
            const el = document.createElement('option');
            el.value = opt.value;
            el.textContent = opt.label;
            detailSelect.appendChild(el);
        });
        detailSelect.disabled = false;
        detailSelect.required = false;
        detailSelect.value = previous && options.some(opt => String(opt.value) === String(previous)) ? previous : '';
    },

    handlePurchaseOtherNumberChange() {
        const configs = [
            {
                select: document.getElementById('activity-has-purchase'),
                field: document.getElementById('previous-purchase-number-field'),
                input: document.querySelector('input[name="previous_purchase_number"]')
            },
            {
                select: document.getElementById('deal-activity-has-purchase'),
                field: document.getElementById('deal-previous-purchase-number-field'),
                input: document.getElementById('deal-activity-previous-number')
            },
            {
                select: document.getElementById('lead-activity-has-purchase'),
                field: document.getElementById('lead-previous-purchase-number-field'),
                input: document.getElementById('lead-activity-previous-number')
            }
        ];
        configs.forEach(cfg => {
            if (!cfg.select || !cfg.field) return;
            const hasPurchase = cfg.select.value;
            if (hasPurchase === '1') {
                cfg.field.classList.remove('hidden');
                if (cfg.input) cfg.input.required = false;
            } else {
                cfg.field.classList.add('hidden');
                if (cfg.input) {
                    cfg.input.required = false;
                    cfg.input.value = '';
                }
            }

            // پس از اولین انتخاب، فیلد قفل شود
            if (hasPurchase !== '' && cfg.select.dataset.locked !== 'true') {
                cfg.select.dataset.locked = 'true';
                cfg.select.disabled = true;
                cfg.select.classList.add('bg-gray-900', 'cursor-not-allowed');
            }
        });
    },
    
    checkIfPurchaseQuestionAnswered(contactId) {
        // بررسی اینکه آیا لید قبلاً به این سوال پاسخ داده (و پاسخ "نامشخص" نباشد)
        if (!contactId || !this.state.currentLeadActivities) return false;
        
        for (let act of this.state.currentLeadActivities) {
            const note = act.result_note || '';
            if ((note.includes('🟢 این لید قبلاً خرید دیگری') || 
                 note.includes('⚪ این لید خرید قبلی با شماره دیگر نداشته است')) &&
                !note.includes('نامشخص')) {
                return true;
            }
        }
        return false;
    },
    
    handleActivityFormVisibility() {
        try {
            const virtualStage = document.getElementById('activity-virtual-stage')?.value;
            const contactId = document.getElementById('activity-contact-id')?.value;
            // The purchase question container has been removed from Activity Modal as per user request.
            // const purchaseField = document.getElementById('activity-has-purchase');
            // const purchaseFieldContainer = document.getElementById('purchase-question-container');

            // No longer checking for these elements in the activity modal context.
            // if (!purchaseField || !purchaseFieldContainer) {
            //     console.warn('Purchase field or container not found');
            //     return;
            // }

            // The logic to handle purchase question visibility is now primarily in openLeadModal and saveLead.

        } catch(error) {
            console.error('Error in handleActivityFormVisibility:', error);
        }
    },

    handleActivityDealSelection() {
        const dealSelect = document.getElementById('activity-deal-select');
        const pipelineContainer = document.getElementById('activity-pipeline-stage-container');
        const pipelineSelect = document.getElementById('activity-virtual-stage');
        const dealId = dealSelect?.value;

        if (!pipelineContainer || !pipelineSelect) return;

        if (dealId) {
            // Deal selected - show and require pipeline stage
            pipelineContainer.style.display = 'block';
            pipelineSelect.required = true;
            // Update label to show it's required
            const label = pipelineContainer.querySelector('label');
            if (label) {
                label.innerHTML = 'مرحله کاریز معامله پس از فعالیت <span class="text-red-500">*</span>';
            }
        } else {
            // No deal selected - hide pipeline stage
            pipelineContainer.style.display = 'none';
            pipelineSelect.required = false;
            pipelineSelect.value = '';
        }
    },
    
    handleActivityStageChange() {
        const stage = document.getElementById('activity-virtual-stage')?.value;
        const extra = document.getElementById('referral-extra');
        const noteInput = document.querySelector('textarea[name="referral_note"]');
        const failureExtra = document.getElementById('failure-reason-extra');
        const failureReason = document.getElementById('activity-failure-reason');
        const failureOtherDesc = document.getElementById('activity-failure-other-description');
        
        const isCrmReferral = stage === 'ارجاع به CRM';
        const isFailed = stage === 'deal_failed' || stage === 'معامله ناموفق' || stage === 'ناموفق';
        const stageMeta = this.getLookupMeta('activity_stages', stage);
        const showFailureFromMeta = stageMeta.show_failure_reason === true || stageMeta.showFailureReason === true;

        // مدیریت بخش ارجاع به CRM
        if(extra) {
            if(isCrmReferral) {
                extra.classList.remove('hidden');
                if (noteInput) noteInput.required = true;
            } else {
                extra.classList.add('hidden');
                if (noteInput) noteInput.required = false;
            }
        }
        
        // مدیریت بخش دلیل ناموفق
        if (failureExtra) {
            const shouldShowFailure = isCrmReferral || isFailed || showFailureFromMeta;
            if (shouldShowFailure) {
                failureExtra.classList.remove('hidden');
                if (failureReason) failureReason.required = true;
                if (failureOtherDesc) {
                    failureOtherDesc.required = true;
                    failureOtherDesc.placeholder = 'لطفاً توضیحات دلیل عدم موفقیت را وارد کنید';
                }
                // نمایش فیلد توضیحات
                const otherDiv = document.getElementById('activity-failure-other-reason');
                if (otherDiv) otherDiv.classList.remove('hidden');
            } else {
                failureExtra.classList.add('hidden');
                if (failureReason) {
                    failureReason.required = false;
                    failureReason.value = '';
                }
                if (failureOtherDesc) {
                    failureOtherDesc.required = false;
                    failureOtherDesc.value = '';
                }
            }
        }
        
        // فراخوانی handleActivityFormVisibility برای به‌روزرسانی وضعیت required فیلد خرید قبلی
        this.handleActivityFormVisibility();
    },
    
    toggleActivityFailureOtherReason() {
        const reason = document.getElementById('activity-failure-reason')?.value;
        const otherDiv = document.getElementById('activity-failure-other-reason');
        const otherDesc = document.getElementById('activity-failure-other-description');
        
        if (!otherDiv || !otherDesc) return;
        
        const virtualStage = document.getElementById('activity-virtual-stage')?.value;
        const isFailed = virtualStage === 'deal_failed' || virtualStage === 'معامله ناموفق';
        const show = isFailed; // در حالت ناموفق همیشه نمایش بده
        otherDiv.classList.toggle('hidden', !show);
        otherDesc.required = show;
        if (!show) {
            otherDesc.value = '';
        } else {
            otherDesc.placeholder = 'لطفاً توضیحات دلیل عدم موفقیت را وارد کنید';
        }
    },
    
	    async openLeadModal(lead = null) {
	        const isEdit = !!lead;
        if (!isEdit && !this.canActionClient('leads.create')) {
            Swal.fire('خطا', 'دسترسی به ایجاد لید ندارید', 'error');
            return;
        }
        if (isEdit && !this.canActionClient('leads.edit')) {
            Swal.fire('خطا', 'دسترسی به ویرایش لید ندارید', 'error');
            return;
        }
        if (isEdit && !this.isAdminOrCrmSpecialist() && !this.isLeadOwner(lead)) {
            Swal.fire('خطا', 'دسترسی به ویرایش این لید را ندارید', 'error');
            return;
        }
        const form = document.getElementById('lead-form');
        if(!form) return;
        const titleEl = document.getElementById('lead-modal-title');
        if (titleEl) {
            titleEl.textContent = isEdit ? 'ویرایش اطلاعات لید' : 'افزودن لید جدید';
        }
	        form.reset();
	        form.dataset.mode = isEdit ? 'edit' : 'create';
	        form.dataset.contactId = lead?.didar_contact_id || '';
	        const deleteBtn = document.getElementById('lead-delete-btn');
	        if (deleteBtn) {
	            const show = isEdit && this.canActionClient('leads.delete');
	            deleteBtn.classList.toggle('hidden', !show);
	            deleteBtn.disabled = !show;
	        }
        this.populateSelectOptions('lead-source', this.getLookupOptions('lead_sources'));
        this.populateSelectOptions('lead-content-topic', this.getLookupOptions('content_topics'));
        this.handleSourceChange();
        const leadActivitySection = document.getElementById('lead-activity-section');
        if (leadActivitySection) {
            leadActivitySection.classList.toggle('hidden', !isEdit);
        }
        
        // Ensure Didar users are loaded before populating the select
        if (this.isAdminOrCrmSpecialist() && (!this.state.didarUsers || this.state.didarUsers.length === 0)) {
            await this.loadDidarUsers();
        } else {
            this.populateLeadOwnerSelect();
        }
        
        if (isEdit && lead) {
            const fullName = `${lead.first_name || ''} ${lead.last_name || ''}`.trim();
            document.querySelector('#lead-form [name="full_name"]').value = fullName;
            const customerCodeField = document.getElementById('lead-customer-code');
            if (customerCodeField) customerCodeField.value = lead.customer_code || '';
            document.querySelector('#lead-form [name="national_id"]').value = lead.national_id || '';
            document.querySelector('#lead-form [name="mobile_phone"]').value = lead.mobile_phone || '';
            document.querySelector('#lead-form [name="secondary_mobile_phone"]').value = lead.secondary_mobile_phone || '';
            const phone3 = document.querySelector('#lead-form [name="mobile_phone_3"]');
            const phone4 = document.querySelector('#lead-form [name="mobile_phone_4"]');
            if (phone3) phone3.value = lead.mobile_phone_3 || '';
            if (phone4) phone4.value = lead.mobile_phone_4 || '';
            document.querySelector('#lead-form [name="city"]').value = lead.city || '';
            document.querySelector('#lead-form [name="job_title"]').value = lead.job_title || '';
            document.querySelector('#lead-form [name="background_info"]').value = lead.background_info || '';
            document.querySelector('#lead-form [name="owner_didar_id"]').value = lead.owner_didar_id || '';
            const prevOwnerSelect = document.getElementById('lead-previous-owner-select');
            if (prevOwnerSelect) prevOwnerSelect.value = lead.previous_owner_id || '';
            document.getElementById('lead-source').value = lead.acquaintance_source || '';
            this.handleSourceChange();
            document.getElementById('lead-source-detail').value = lead.acquaintance_detail || '';
            document.getElementById('lead-content-topic').value = lead.content_topic || '';

            // Activity-style extra fields
            const setVal = (id, val) => { const el = document.getElementById(id); if (el) el.value = val || ''; };
            // setVal('lead-activity-city', lead.city || ''); // Removed (Issue 4)
            // setVal('lead-activity-job', lead.job_title || ''); // Removed (Issue 4)
            setVal('lead-activity-job-desc', lead.job_description || '');
            setVal('lead-activity-extra-info', lead.extra_info || '');
            setVal('lead-activity-financial-level', lead.financial_level || '');
            setVal('lead-activity-asset', lead.asset_estimation || '');
            setVal('lead-activity-income', lead.income_estimation || '');
            const acqVal = lead.acquaintance_duration || '';
            const acqYears = document.getElementById('lead-activity-acq-years');
            const acqMonths = document.getElementById('lead-activity-acq-months');
            const acqLess = document.getElementById('lead-activity-acq-less');
            if (acqLess && acqYears && acqMonths) {
                if (acqVal && acqVal.includes('کمتر')) {
                    acqLess.checked = true;
                    acqYears.value = '0';
                    acqMonths.value = '0';
                } else {
                    const match = acqVal.match(/(\d+)\s*سال.*?(\d+)\s*ماه/);
                    acqYears.value = match ? match[1] : '0';
                    acqMonths.value = match ? match[2] : '0';
                    acqLess.checked = false;
                }
            }
            const purchaseSelect = document.getElementById('lead-activity-has-purchase');
            if (purchaseSelect) {
                if (lead.has_previous_purchase === 0 || lead.has_previous_purchase === 1 || lead.has_previous_purchase === '0' || lead.has_previous_purchase === '1') {
                    purchaseSelect.value = String(lead.has_previous_purchase);
                    // Issue 2: Hide or Lock?
                    // "The question ... should appear only once during first edit."
                    // If it has a value, we can lock it or hide it. 
                    // Let's lock it for now to be consistent with previous logic, 
                    // but the user requirement implies it might not need to be shown if answered.
                    // However, for profile completeness visibility, keeping it locked is better than hiding.
                    purchaseSelect.disabled = true;
                    purchaseSelect.dataset.locked = 'true';
                    purchaseSelect.classList.add('bg-gray-900', 'cursor-not-allowed');
                } else {
                    purchaseSelect.value = '';
                    purchaseSelect.disabled = false;
                    purchaseSelect.dataset.locked = '';
                    purchaseSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
                }
            }
        } else {
            const customerCodeField = document.getElementById('lead-customer-code');
            if (customerCodeField) customerCodeField.value = 'پس از ثبت خودکار ایجاد می‌شود';
            const prevOwnerSelect = document.getElementById('lead-previous-owner-select');
            if (prevOwnerSelect) prevOwnerSelect.value = '';
            ['lead-activity-job-desc','lead-activity-extra-info'].forEach(id => {
                const el = document.getElementById(id);
                if (el) el.value = '';
            });
            ['lead-activity-financial-level','lead-activity-asset','lead-activity-income','lead-activity-has-purchase'].forEach(id => {
                const el = document.getElementById(id);
                if (el) { el.value=''; el.disabled=false; el.dataset.locked=''; el.classList.remove('bg-gray-900','cursor-not-allowed'); }
            });
            const acqYears = document.getElementById('lead-activity-acq-years');
            const acqMonths = document.getElementById('lead-activity-acq-months');
            const acqLess = document.getElementById('lead-activity-acq-less');
            if (acqYears) acqYears.value = '0';
            if (acqMonths) acqMonths.value = '0';
            if (acqLess) acqLess.checked = false;
            const phone3 = document.querySelector('#lead-form [name="mobile_phone_3"]');
            const phone4 = document.querySelector('#lead-form [name="mobile_phone_4"]');
            if (phone3) phone3.value = '';
            if (phone4) phone4.value = '';
        }
        const customerCodeField = document.getElementById('lead-customer-code');
        if (customerCodeField) {
            const allowEditCustomerCode = this.isAdmin() && isEdit;
            customerCodeField.readOnly = !allowEditCustomerCode;
            customerCodeField.classList.toggle('cursor-not-allowed', !allowEditCustomerCode);
            customerCodeField.classList.toggle('bg-gray-900', !allowEditCustomerCode);
            customerCodeField.classList.toggle('text-gray-300', !allowEditCustomerCode);
            customerCodeField.classList.toggle('bg-gray-800', allowEditCustomerCode);
            customerCodeField.classList.toggle('text-white', allowEditCustomerCode);
        }
        const regDisplay = document.getElementById('lead-register-date-display');
        if (regDisplay) {
            if (isEdit && lead) {
                const regDate = lead.register_time_jalali || (lead.register_time ? toPersianDate(lead.register_time) : '');
                regDisplay.value = regDate || '---';
            } else {
                let today = '';
                if (typeof persianDate !== 'undefined') {
                    try {
                        today = new persianDate().format('YYYY/MM/DD');
                    } catch (e) {}
                }
                regDisplay.value = today || (toPersianDate(new Date().toISOString()) || '');
            }
        }
        
        this.applyLeadFormPermissions(lead, isEdit);
        this.applyRbacActionVisibility();
        
        this.openModal('modalLead');
    },

    /**
     * ذخیره فیلدهای سبک فعالیت از مودال لید در دیتابیس داخلی
     */
    async saveLeadActivityExtras(contactId, form) {
        if (!contactId || !form) return;
        const payload = { contact_id: contactId };
        const getVal = name => form.querySelector(`[name="${name}"]`)?.value?.trim() || '';

        // const city = getVal('activity_city'); // Removed (Issue 4)
        // const job = getVal('activity_job_title'); // Removed (Issue 4)
        const jobDesc = getVal('activity_job_desc');
        const extraInfo = getVal('activity_extra_info');
        const finLevel = getVal('activity_financial_level');
        const asset = getVal('activity_asset');
        const income = getVal('activity_income');
        const acqYears = form.querySelector('[name="activity_acq_years"]')?.value || '0';
        const acqMonths = form.querySelector('[name="activity_acq_months"]')?.value || '0';
        const acqLess = form.querySelector('[name="activity_acq_less"]')?.checked;
        const hasPurchaseEl = form.querySelector('[name="activity_has_purchase"]');
        const prevPurchase = getVal('activity_previous_number');

        // if (city) payload.city = city;
        // if (job) payload.job_title = job;
        if (jobDesc) payload.job_description = jobDesc;
        if (extraInfo) payload.extra_info = extraInfo;
        if (finLevel) payload.financial_level = finLevel;
        if (asset) payload.asset_estimation = asset;
        if (income) payload.income_estimation = income;

        if (acqLess) {
            payload.acquaintance_duration = 'کمتر از 1 ماه';
        } else if (acqYears || acqMonths) {
            payload.acquaintance_duration = `${acqYears || '0'} سال ${acqMonths || '0'} ماه`;
        }

        if (hasPurchaseEl && hasPurchaseEl.dataset.locked !== 'true' && hasPurchaseEl.value !== '') {
            payload.has_previous_purchase = hasPurchaseEl.value;
        } else if (hasPurchaseEl && hasPurchaseEl.dataset.locked === 'true') {
            payload.has_previous_purchase = hasPurchaseEl.value;
        }
        if (prevPurchase) {
            payload.previous_purchase_number = prevPurchase.replace(/\D/g, '').slice(-10);
        }

        if (Object.keys(payload).length <= 1) return;

        try {
            await this.req('save_deal_info', payload);
        } catch (e) {
            console.warn('saveLeadActivityExtras failed', e);
        }
    },
    
    applyLeadFormPermissions(lead, isEdit) {
        const form = document.getElementById('lead-form');
        if(!form) return;
        const isAdmin = this.isAdmin();
        const isCrmSpecialist = this.state.user?.role === 'crm_specialist';
        const isAdminOrCrm = this.isAdminOrCrmSpecialist();
        const isOwner = isEdit && this.isLeadOwner(lead);
        
        const primary = form.querySelector('[name="mobile_phone"]');
        const secondary = form.querySelector('[name="secondary_mobile_phone"]');
        const phone3 = form.querySelector('[name="mobile_phone_3"]');
        const phone4 = form.querySelector('[name="mobile_phone_4"]');
        const ownerSelect = form.querySelector('[name="owner_didar_id"]');
        const prevOwnerSelect = form.querySelector('[name="previous_owner_id"]');
        const sourceSelect = document.getElementById('lead-source');
        const detailSelect = document.getElementById('lead-source-detail');
        const topicSelect = document.getElementById('lead-content-topic');
        const adminFieldsDiv = document.getElementById('lead-admin-fields');
        const phoneFieldsDiv = primary?.closest('.grid');
        const phoneExtraDiv = phone3?.closest('.grid');
        
        const disable = (el, state) => { if(el) el.disabled = state; };
        const setRequired = (el, state) => { if(el) el.required = state; };
        const hide = (el, state) => { if(el) el.style.display = state ? 'none' : ''; };
        const lockIfFilled = (el, value) => {
            if (!el) return;
            const hasVal = value && String(value).trim() !== '';
            if (!isAdmin && hasVal) {
                if ('readOnly' in el) el.readOnly = true;
                if ('disabled' in el) el.disabled = true;
                el.classList.add('bg-gray-900','cursor-not-allowed');
            } else if (!isAdmin) {
                if ('readOnly' in el) el.readOnly = false;
                if ('disabled' in el) el.disabled = false;
                el.classList.remove('bg-gray-900','cursor-not-allowed');
            } else {
                // ادمین آزاد است؛ اما اگر مقدار دارد، قفل نرم
                if ('readOnly' in el) el.readOnly = false;
                if ('disabled' in el) el.disabled = false;
                el.classList.remove('bg-gray-900','cursor-not-allowed');
            }
        };
        
        // Set all admin fields as required
        setRequired(ownerSelect, true);
        setRequired(primary, true);
        // فیلدهای آشنایی: اختیاری هستند
        setRequired(sourceSelect, false);
        setRequired(detailSelect, false);
        setRequired(topicSelect, false);
        if (prevOwnerSelect) prevOwnerSelect.required = false;

        // Admin یا CRM Specialist: می‌تواند همه را ویرایش کند
        if (isAdminOrCrm) {
            hide(phoneFieldsDiv, false);
            hide(phoneExtraDiv, false);
            if (adminFieldsDiv) adminFieldsDiv.style.display = '';
            return;
        }

        // Agent (کاربر عادی): فیلدهای admin پنهان می‌شوند
        if (adminFieldsDiv) adminFieldsDiv.style.display = 'none';
        hide(phoneFieldsDiv, false);
        hide(phoneExtraDiv, false);
        
        // فیلدهای آشنایی در حالت edit غیرالزامی هستند
        if (isEdit) {
            setRequired(sourceSelect, false);
            setRequired(detailSelect, false);
            setRequired(topicSelect, false);
        }
        
        lockIfFilled(primary, lead?.mobile_phone);
        lockIfFilled(secondary, lead?.secondary_mobile_phone);
        lockIfFilled(phone3, lead?.mobile_phone_3);
        lockIfFilled(phone4, lead?.mobile_phone_4);
        lockIfFilled(form.querySelector('[name="national_id"]'), lead?.national_id);
        lockIfFilled(form.querySelector('[name="city"]'), lead?.city);
        lockIfFilled(form.querySelector('[name="job_title"]'), lead?.job_title);
        lockIfFilled(form.querySelector('[name="background_info"]'), lead?.background_info);

        // Prevent changing ownership/previous owner
        disable(ownerSelect, true);
        disable(prevOwnerSelect, true);
    },
    
    editLeadInfo() {
        if(!this.state.currentLead) return;
        if (!this.canActionClient('leads.edit')) {
            Swal.fire('خطا', 'دسترسی به ویرایش لید ندارید', 'error');
            return;
        }
        
        // CRM specialist is view-only
        const isAdmin = window.APP_USER?.role === 'admin' || window.APP_USER?.role === 'system';
        const isCrm = window.APP_USER?.role === 'crm_specialist';
        
        if (isCrm) {
            Swal.fire('خطا', 'کارشناس CRM فقط مجاز به مشاهده است', 'error');
            return;
        }
        
        if (!isAdmin && !this.isLeadOwner(this.state.currentLead)) {
            Swal.fire('خطا', 'دسترسی به ویرایش این لید را ندارید', 'error');
            return;
        }
        
        this.openLeadModal(this.state.currentLead);
    },

    async saveLead(e) {
        e.preventDefault();
        const form = e.target;
        const mode = form.dataset.mode || 'create';
        if (mode === 'create' && !this.canActionClient('leads.create')) {
            Swal.fire('خطا', 'دسترسی به ایجاد لید ندارید', 'error');
            return;
        }
        if (mode === 'edit' && !this.canActionClient('leads.edit')) {
            Swal.fire('خطا', 'دسترسی به ویرایش لید ندارید', 'error');
            return;
        }
        const data = Object.fromEntries(new FormData(form).entries());
        const isAdminUser = this.isAdmin();

        // شیوه آشنایی و موضوع محتوا هنگام افزودن لید جدید اجباری هستند. این فیلدها فقط
        // برای ادمین/کارشناس CRM نمایش داده می‌شوند (برای بقیه پنهان است)، پس فقط وقتی
        // بخش نمایش داده می‌شود اعتبارسنجی می‌کنیم. در حالت ویرایش نیازی به پر کردن مجدد
        // نیست؛ مقدار قبلی از قبل پیش‌فرض بارگذاری شده است.
        if (mode === 'create') {
            const adminFields = document.getElementById('lead-admin-fields');
            const adminFieldsVisible = !!adminFields && adminFields.style.display !== 'none';
            if (adminFieldsVisible) {
                if (!String(data.acquaintance_source || '').trim()) {
                    Swal.fire('خطا', 'لطفاً شیوه آشنایی را انتخاب کنید', 'error');
                    document.getElementById('lead-source')?.focus();
                    return;
                }
                if (!String(data.content_topic || '').trim()) {
                    Swal.fire('خطا', 'لطفاً موضوع محتوا را انتخاب کنید', 'error');
                    document.getElementById('lead-content-topic')?.focus();
                    return;
                }
            }
        }

        // اعتبارسنجی فیلدها
        const fullName = data.full_name || '';
        const nationalId = data.national_id || '';
        const mobilePhone = data.mobile_phone || '';
        const secondaryMobilePhone = data.secondary_mobile_phone || '';
        const mobilePhone3 = data.mobile_phone_3 || '';
        const mobilePhone4 = data.mobile_phone_4 || '';
        const city = data.city || '';
        const jobTitle = data.job_title || '';
        
        // اعتبارسنجی نام و نام خانوادگی
        if (fullName.trim()) {
            const nameValidation = validatePersianText(fullName, 'نام و نام خانوادگی');
            if (!nameValidation.valid) {
                Swal.fire('خطا', nameValidation.message, 'error');
                return;
            }
        }
        
        // اعتبارسنجی کد ملی
        if (nationalId.trim()) {
            const nationalIdValidation = validateNationalId(nationalId);
            if (!nationalIdValidation.valid) {
                Swal.fire('خطا', nationalIdValidation.message, 'error');
                return;
            }
            // فقط اعداد را ذخیره کن
            data.national_id = nationalId.trim().replace(/\D/g, '');
        }
        
        // اعتبارسنجی شماره تماس اصلی
        if (mobilePhone.trim()) {
            const mobileValidation = validatePhone(mobilePhone, 'شماره تماس اصلی');
            if (!mobileValidation.valid) {
                Swal.fire('خطا', mobileValidation.message, 'error');
                return;
            }
            // فقط اعداد را ذخیره کن
            data.mobile_phone = mobilePhone.trim().replace(/\D/g, '');
        }
        
        // اعتبارسنجی شماره تماس دوم
        if (secondaryMobilePhone.trim()) {
            const secondaryValidation = validatePhone(secondaryMobilePhone, 'شماره تماس دوم');
            if (!secondaryValidation.valid) {
                Swal.fire('خطا', secondaryValidation.message, 'error');
                return;
            }
            // فقط اعداد را ذخیره کن
            data.secondary_mobile_phone = secondaryMobilePhone.trim().replace(/\D/g, '');
        }
        if (mobilePhone3.trim()) {
            const mobile3Validation = validatePhone(mobilePhone3, 'شماره تماس سوم');
            if (!mobile3Validation.valid) {
                Swal.fire('خطا', mobile3Validation.message, 'error');
                return;
            }
            data.mobile_phone_3 = mobilePhone3.trim().replace(/\D/g, '');
        }
        if (mobilePhone4.trim()) {
            const mobile4Validation = validatePhone(mobilePhone4, 'شماره تماس چهارم');
            if (!mobile4Validation.valid) {
                Swal.fire('خطا', mobile4Validation.message, 'error');
                return;
            }
            data.mobile_phone_4 = mobilePhone4.trim().replace(/\D/g, '');
        }

        if (mode === 'edit' && isAdminUser && Object.prototype.hasOwnProperty.call(data, 'customer_code')) {
            const rawCustomerCode = String(data.customer_code || '').trim();
            if (rawCustomerCode !== '') {
                if (!/^\d+$/.test(rawCustomerCode)) {
                    Swal.fire('خطا', 'کد مشتری باید فقط عدد باشد.', 'error');
                    return;
                }
                data.customer_code = rawCustomerCode;
            }
        }

        // بررسی تکراری بودن شماره‌های تلفن
        const phoneNumbers = [];
        const phoneLabels = [];

        if (data.mobile_phone) {
            phoneNumbers.push(data.mobile_phone);
            phoneLabels.push('شماره تماس اصلی');
        }
        if (data.secondary_mobile_phone) {
            phoneNumbers.push(data.secondary_mobile_phone);
            phoneLabels.push('شماره تماس دوم');
        }
        if (data.mobile_phone_3) {
            phoneNumbers.push(data.mobile_phone_3);
            phoneLabels.push('شماره تماس سوم');
        }
        if (data.mobile_phone_4) {
            phoneNumbers.push(data.mobile_phone_4);
            phoneLabels.push('شماره تماس چهارم');
        }

        // بررسی تکراری بودن شماره‌ها
        for (let i = 0; i < phoneNumbers.length; i++) {
            for (let j = i + 1; j < phoneNumbers.length; j++) {
                if (phoneNumbers[i] === phoneNumbers[j]) {
                    Swal.fire('خطا', `شماره ${phoneLabels[i]} با ${phoneLabels[j]} تکراری است! لطفاً شماره‌های منحصر به فرد وارد کنید.`, 'error');
                    return;
                }
            }
        }

        // اعتبارسنجی شهر
        if (city.trim()) {
            const cityValidation = validatePersianText(city, 'شهر');
            if (!cityValidation.valid) {
                Swal.fire('خطا', cityValidation.message, 'error');
                return;
            }
        }
        
        // اعتبارسنجی شغل
        if (jobTitle.trim()) {
            const jobValidation = validatePersianText(jobTitle, 'شغل');
            if (!jobValidation.valid) {
                Swal.fire('خطا', jobValidation.message, 'error');
                return;
            }
        }
        
        if (mode === 'edit') {
            data.contact_id = form.dataset.contactId;
            if (!data.contact_id) {
                Swal.fire('خطا', 'شناسه لید نامعتبر است', 'error');
                return;
            }
            if (!this.isAdminOrCrmSpecialist() && (!this.state.currentLead || !this.isLeadOwner(this.state.currentLead))) {
                Swal.fire('خطا', 'دسترسی به ویرایش این لید را ندارید', 'error');
                return;
            }

            // Issue 5: Mandatory fields on First Edit
            // Check if name is generic (New Lead)
            const currentName = ((this.state.currentLead?.first_name || '') + ' ' + (this.state.currentLead?.last_name || '')).trim();
            const isFirstEdit = currentName === 'لید جدید' || currentName === 'New Lead';
            
            if (isFirstEdit) {
                if (!fullName || fullName === 'لید جدید' || fullName === 'New Lead') {
                    Swal.fire('خطا', 'لطفاً نام و نام خانوادگی لید را وارد کنید.', 'error');
                    return;
                }
                
                const hasPurchaseSelect = form.querySelector('[name="activity_has_purchase"]');
                if (hasPurchaseSelect && !hasPurchaseSelect.value) {
                     Swal.fire('خطا', 'لطفاً وضعیت خرید قبلی با شماره دیگر را مشخص کنید.', 'error');
                     return;
                }
            }
        } else if (!this.isAdminOrCrmSpecialist()) {
            Swal.fire('خطا', 'فقط مدیر و کارشناس CRM می‌توانند لید جدید اضافه کنند', 'error');
            return;
        }

        // Collect Additional Fields (Issue 3)
        // job_description
        const jobDesc = form.querySelector('[name="activity_job_desc"]')?.value;
        if (jobDesc) data.job_description = jobDesc;
        
        // extra_info
        const extraInfo = form.querySelector('[name="activity_extra_info"]')?.value;
        if (extraInfo) data.extra_info = extraInfo;
        
        // financial_level
        const finLevel = form.querySelector('[name="activity_financial_level"]')?.value;
        if (finLevel) data.financial_level = finLevel;
        
        // asset_estimation
        const assetEst = form.querySelector('[name="activity_asset"]')?.value;
        if (assetEst) data.asset_estimation = assetEst;
        
        // income_estimation
        const incomeEst = form.querySelector('[name="activity_income"]')?.value;
        if (incomeEst) data.income_estimation = incomeEst;
        
        // customer_level و customer_level_notes
        const customerLevelEl = form.querySelector('[name="customer_level"]');
        const customerLevelNotesEl = form.querySelector('[name="customer_level_notes"]');
        const customerLevel = customerLevelEl?.value?.trim() || '';
        const customerLevelNotes = customerLevelNotesEl?.value?.trim() || '';
        
        if (customerLevel) {
            data.customer_level = customerLevel;
            // اگر سطح مشتری انتخاب شده باشد، توضیحات الزامی است
            if (!customerLevelNotes) {
                Swal.fire('خطا', 'لطفاً توضیحات سطح مشتری را وارد کنید', 'error');
                return;
            }
            data.customer_level_notes = customerLevelNotes;
        }
        
        // acquaintance_duration
        const acqYears = form.querySelector('[name="activity_acq_years"]')?.value || '0';
        const acqMonths = form.querySelector('[name="activity_acq_months"]')?.value || '0';
        const acqLess = form.querySelector('[name="activity_acq_less"]')?.checked;
        
        if (acqLess) {
            data.acquaintance_duration = 'کمتر از 1 ماه';
        } else if (acqYears !== '0' || acqMonths !== '0') {
            data.acquaintance_duration = `${acqYears} سال ${acqMonths} ماه`;
        }
        
        // has_previous_purchase
        const hasPurchaseEl = form.querySelector('[name="activity_has_purchase"]');
        if (hasPurchaseEl && hasPurchaseEl.value !== '') {
            data.has_previous_purchase = hasPurchaseEl.value;
        }
        
        // previous_purchase_number
        const prevPurchaseNum = form.querySelector('[name="activity_previous_number"]')?.value;
        if (prevPurchaseNum) {
            data.previous_purchase_number = prevPurchaseNum.replace(/\D/g, '');
        }
        
        // ثبت تاریخ جلالی/میلادی برای مرتب‌سازی
        if (typeof persianDate !== 'undefined') {
            try {
                const p = new persianDate();
                const jalaliStr = p.format('YYYY/MM/DD');
                data.register_time_jalali = jalaliStr;
                const greg = persianToGregorian(jalaliStr);
                if (greg) {
                    data.register_time = greg;
                }
            } catch (e) {
                console.warn('Failed to set jalali date for lead', e);
            }
        }
        
        // Backend will automatically check for duplicate phone and create activity if needed
        // No need to check here - backend handles it gracefully
        
        const res = await this.req(mode === 'edit' ? 'update_lead' : 'create_lead', data);
        if(res.status === 'success') {
            // Duplicate flow
            if (res.is_duplicate) {
                console.log('Duplicate response:', res); // Debug log
                const matches = Array.isArray(res.matches) ? res.matches : [];
                // اولویت: استفاده از owner_name و owner_id از پاسخ backend
                const currentOwnerName = res.owner_name || 'نامشخص';
                const currentOwnerId = res.owner_id || '';
                const contactName = res.contact_name || '---';
                const matchedPhone = res.matched_phone || (data.mobile_phone || '---');
                const existingDeals = Array.isArray(res.deal_ids) ? res.deal_ids.length : 0;
                const nextDealIndex = existingDeals + 1;

                const result = await Swal.fire({
                    icon: 'warning',
                    title: 'شماره تکراری',
                    html: `
                        <div class="text-right">
                            <div class="mb-2"><strong>لید:</strong> ${contactName}</div>
                            <div class="mb-2"><strong>شماره تکراری:</strong> ${matchedPhone}</div>
                            <div class="mb-2"><strong>مسئول فعلی:</strong> ${currentOwnerName || res.owner_id}</div>
                            ${matches.length ? `
                              <div class="mt-2 text-sm">
                                ${matches.map(m => `<div>شماره ${m.phone} → ${m.contact_name || '---'} (${m.owner_name || m.owner_id || '---'})</div>`).join('')}
                              </div>` : ''}
                            <div class="mt-3 text-sm text-gray-300">مسئول قابل تغییر نیست؛ معامله جدید برای همین مسئول ثبت می‌شود.</div>
                            <div class="mt-2 text-sm">عنوان معامله بعدی بر اساس شماره اصلی ساخته می‌شود.</div>
                        </div>
                    `,
                    showCancelButton: true,
                    confirmButtonText: 'ثبت معامله جدید',
                    cancelButtonText: 'انصراف',
                    confirmButtonColor: '#3085d6',
                    cancelButtonColor: '#d33',
                    focusConfirm: false
                });

                if (result.isConfirmed) {
                    // فقط برای همان مسئول فعلی معامله جدید بساز؛ عنوان را از سرور بگیر
                    const targetContactId = res.didar_contact_id || res.contact_id;
                    let dealTitle = '';
                    try {
                        const titleRes = await this.req('generate_deal_title', {contact_id: targetContactId});
                        if (titleRes?.status === 'success' && titleRes.title) {
                            dealTitle = titleRes.title;
                        }
                    } catch (err) {
                        console.error('generate_deal_title failed:', err);
                    }
                    // fallback در صورت بروز خطا
                    if (!dealTitle) {
                        const ordinals = ['اول', 'دوم', 'سوم', 'چهارم', 'پنجم', 'ششم', 'هفتم', 'هشتم', 'نهم', 'دهم'];
                        const idx = nextDealIndex - 1;
                        const ordinal = ordinals[idx] || `${nextDealIndex}م`;
                        dealTitle = `معامله ${ordinal} ${res.matched_phone || ''}`.trim();
                    }
                    if (!targetContactId || !currentOwnerId) {
                        Swal.fire('خطا', 'اطلاعات لید تکراری یا مسئول یافت نشد', 'error');
                        return;
                    }

                    const dealRes = await this.req('save_deal', {
                        contact_id: targetContactId,
                        title: dealTitle,
                        pipeline_stage: 'معامله جدید',
                        deal_status: 'Pending',
                        acquaintance_source: data.acquaintance_source || res.person?.acquaintance_source || '',
                        acquaintance_detail: data.acquaintance_detail || res.person?.acquaintance_detail || '',
                        content_topic: data.content_topic || res.person?.content_topic || '',
                        from_duplicate_lead_modal: '1'
                    });
                    if (dealRes.status !== 'success') {
                        Swal.fire('خطا', dealRes.message || 'خطا در ثبت معامله', 'error');
                        return;
                    }

                    Swal.fire({
                        icon: 'success',
                        title: 'ثبت شد',
                        html: `این شماره قبلاً ثبت شده بود. معامله جدید با عنوان "${dealTitle}" برای مسئول فعلی ثبت شد.`,
                        confirmButtonText: 'باشه'
                    });

                    this.closeModal('modalLead');
                    form.reset();
                    form.dataset.contactId = '';
                    form.dataset.mode = 'create';
                    this.loadLeads();

                    if (targetContactId) {
                        try {
                            this.openPersonProfile(targetContactId);
                        } catch (e) {
                            console.warn('Failed to load duplicate lead profile', e);
                        }
                    }
                } else {
                    Swal.fire({
                        icon: 'info',
                        title: 'لغو شد',
                        text: 'ثبت لید جدید انجام نشد',
                        timer: 2000,
                        showConfirmButton: false
                    });
                }

                return;
            }

            // Non-duplicate flow
            // Note: `saveLeadActivityExtras` call removed because `update_lead` API now handles
            // saving additional fields (job_desc, extra_info, etc.) directly.
            // Refresh current profile and leads list without needing manual refresh
            if (this.state.currentLead && this.state.currentLead.didar_contact_id === data.contact_id) {
                await this.openPersonProfile(data.contact_id);
            }
            // Always refresh leads list to show ownership changes
            this.loadLeads();

            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'انجام شد', showConfirmButton:false, timer:2000});
            this.closeModal('modalLead');
            form.reset();
            form.dataset.contactId = '';
            form.dataset.mode = 'create';
            
            if (mode === 'edit' && res.person) {
                this.state.currentLead = res.person;
                this.openPersonProfile(res.person.didar_contact_id);
            }

            // Hide duplicate warning if shown
            const warningDiv = document.getElementById('duplicate-phone-warning');
            if (warningDiv) {
                warningDiv.classList.add('hidden');
            }
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    async createNewRequestActivity(contactId = null) {
        // Function functionality pending implementation or restoration
        console.warn('createNewRequestActivity called but logic is missing.');
        if (!contactId) {
             const warningDiv = document.getElementById('duplicate-phone-warning');
             if (warningDiv && !warningDiv.classList.contains('hidden')) {
                 // Try to find contact ID from warning context if needed
             }
        }
        // Placeholder
    },
    
    // LOAD LEADS (restored)
	    async loadLeads(page = 1) {
	        try {
	            const search = document.getElementById('leads-search')?.value || '';
	            const customerCodeSearch = document.getElementById('leads-customer-code-search')?.value || '';
	            const saleStatus = document.getElementById('leads-stage-filter')?.value || 'all';
	            const ownerId = document.getElementById('leads-owner-filter')?.value || '';
	            const productRaw = document.getElementById('leads-product-filter')?.value || '';
	            const customerLevel = document.getElementById('leads-customer-level-filter')?.value || '';
	            const registerFromRaw = document.getElementById('leads-register-from')?.value || '';
	            const registerToRaw = document.getElementById('leads-register-to')?.value || '';
	            const activityFromRaw = document.getElementById('leads-activity-from')?.value || '';
	            const activityToRaw = document.getElementById('leads-activity-to')?.value || '';

	            const payload = {
	                search,
	                customer_code_search: customerCodeSearch,
	                sale_status: saleStatus,
	                bucket: 'all',
	                page,
	                per_page: this.state.leadsPagination?.perPage || 20
	            };
	            if (ownerId) {
	                payload.owner_id = ownerId;
	            }
	            if (customerLevel) {
	                payload.customer_level = customerLevel;
	            }

                const registerFromJ = toEnglishDigits((registerFromRaw || '').trim());
                const registerToJ = toEnglishDigits((registerToRaw || '').trim());
                const activityFromJ = toEnglishDigits((activityFromRaw || '').trim());
                const activityToJ = toEnglishDigits((activityToRaw || '').trim());

                const registerFromG = registerFromJ ? (persianToGregorian(registerFromJ, '00:00') || '') : '';
                const registerToG = registerToJ ? (persianToGregorian(registerToJ, '23:59') || '') : '';
                const activityFromG = activityFromJ ? (persianToGregorian(activityFromJ, '00:00') || '') : '';
                const activityToG = activityToJ ? (persianToGregorian(activityToJ, '23:59') || '') : '';

                if (registerFromG) payload.register_date_from = registerFromG.split(' ')[0];
                if (registerToG) payload.register_date_to = registerToG.split(' ')[0];
                if (registerFromJ) payload.register_date_from_jalali = registerFromJ;
                if (registerToJ) payload.register_date_to_jalali = registerToJ;
                if (activityFromG) payload.activity_date_from = activityFromG.split(' ')[0];
                if (activityToG) payload.activity_date_to = activityToG.split(' ')[0];
                if (productRaw) {
                    if (String(productRaw).startsWith('prod::')) {
                        payload.product_id = String(productRaw).slice(6);
                    } else if (String(productRaw).startsWith('svc::')) {
                        payload.product_name = String(productRaw).slice(5);
                    } else if (/^\d+$/.test(productRaw)) {
                        payload.product_id = productRaw;
                    } else {
                        payload.product_name = productRaw;
                    }
                }

                const res = await this.req('get_leads', payload);
                if (res.status === 'success') {
                    this.state.leads = res.leads || [];
                    if (res.pagination) {
                        this.state.leadsPagination = {
                            currentPage: res.pagination.current_page,
                            perPage: res.pagination.per_page,
                            totalCount: res.pagination.total_count,
                            totalPages: res.pagination.total_pages
                        };
                    }
                    this.renderLeads();
                    this.renderLeadsPagination();
                    if (this.featureFlags.historyV1) this.replaceCurrentHistoryUiState('leads_pagination');
                } else {
                    console.error('Error loading leads:', res.message || 'Unknown error');
                    this.state.leads = [];
                    const container = document.getElementById('leads-list');
                    if (container) {
                        container.innerHTML = `<div class="text-center py-8 text-red-500">خطا در بارگذاری لیدها: ${this.escapeHtml(res.message || 'خطای نامشخص')}</div>`;
                    }
                }
        } catch (error) {
            console.error('Exception loading leads:', error);
            this.state.leads = [];
            const container = document.getElementById('leads-list');
            if (container) {
                container.innerHTML = `<div class="text-center py-8 text-red-500">خطا در بارگذاری لیدها: ${this.escapeHtml(error.message || 'خطای اتصال')}</div>`;
            }
        }
    },

    async loadBlataklifLeads(page = 1) {
        const search = document.getElementById('blataklif-leads-search')?.value || '';
        const saleStatus = document.getElementById('blataklif-leads-sale-status-filter')?.value || 'all';

        const payload = {
            search,
            sale_status: saleStatus,
            bucket: 'blataklif',
            page,
            per_page: this.state.blataklifLeadsPagination?.perPage || 50
        };

        const res = await this.req('get_leads', payload);
        if (res.status === 'success') {
            this.state.blataklifLeads = res.leads || [];
            if (res.pagination) {
                this.state.blataklifLeadsPagination = {
                    currentPage: res.pagination.current_page,
                    perPage: res.pagination.per_page,
                    totalCount: res.pagination.total_count,
                    totalPages: res.pagination.total_pages
                };
            }
            this.renderBlataklifLeads();
            this.renderBlataklifLeadsPagination();
        }
    },

    renderBlataklifLeads() {
        const container = document.getElementById('blataklif-leads-list');
        if (!container) return;

        const list = Array.isArray(this.state.blataklifLeads) ? this.state.blataklifLeads : [];
        if (list.length === 0) {
            container.innerHTML = '<div class="text-center text-gray-500 py-8">لیدی یافت نشد</div>';
            return;
        }

        container.innerHTML = list.map(lead => {
            const statusKey = lead.sale_status === 1 ? 'success' : (lead.sale_status === 0 ? 'failed' : 'unknown');
            const statusMeta = this.state.saleStatusMap[statusKey] || this.state.saleStatusMap.unknown;
            const regDate = lead.register_time_jalali || (lead.register_time ? toPersianDate(lead.register_time) : '');
            const products = this.escapeHtml(this.getLeadProductsDisplayValue(lead)) || '-';

            return `
                <div class="glass p-4 hover:bg-gray-800/50 transition cursor-pointer lead-card-desktop" onclick="app.openPersonProfile('${lead.didar_contact_id || lead.id || ''}')">
                    <div class="flex justify-between items-start mb-2">
                        <div>
                            <div class="font-bold text-white text-sm lead-name">${lead.first_name || ''} ${lead.last_name || ''}</div>
                            <div class="text-xs text-gray-400 mt-1">${lead.mobile_phone || '-'} ${lead.email ? '| ' + lead.email : ''}</div>
                            <div class="text-[11px] text-gray-500 mt-1">مسئول: ${lead.owner_name || '---'}</div>
                        </div>
                        <div class="flex flex-col items-end gap-1">
                            <span class="text-xs px-2 py-1 rounded ${statusMeta.bg} text-white">${statusMeta.icon} ${statusMeta.title}</span>
                        </div>
                    </div>
                    <div class="text-[11px] text-gray-400 mt-1 lead-products">محصولات: ${products}</div>
                    <div class="flex justify-between items-center mt-2 text-xs text-gray-500">
                        <span>تاریخ ثبت: ${regDate || '-'}</span>
                        <span>${statusMeta.title}</span>
                    </div>
                </div>
            `;
        }).join('');
    },

    renderBlataklifLeadsPagination() {
        const container = document.getElementById('blataklif-leads-pagination');
        if (!container) return;

        const p = this.state.blataklifLeadsPagination || { currentPage: 1, perPage: 50, totalCount: 0, totalPages: 0 };
        if (p.totalPages <= 1) {
            container.innerHTML = '';
            return;
        }

        const maxVisiblePages = 5;
        let startPage = Math.max(1, p.currentPage - Math.floor(maxVisiblePages / 2));
        let endPage = Math.min(p.totalPages, startPage + maxVisiblePages - 1);
        if (endPage - startPage + 1 < maxVisiblePages) {
            startPage = Math.max(1, endPage - maxVisiblePages + 1);
        }

        let paginationHTML = '<div class="flex justify-center items-center gap-2 mt-4">';

        if (p.currentPage > 1) {
            paginationHTML += `<button onclick="app.loadBlataklifLeads(${p.currentPage - 1})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">قبلی</button>`;
        } else {
            paginationHTML += `<button disabled class="px-3 py-1 bg-gray-800 text-gray-600 rounded text-sm cursor-not-allowed">قبلی</button>`;
        }

        if (startPage > 1) {
            paginationHTML += `<button onclick="app.loadBlataklifLeads(1)" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">1</button>`;
            if (startPage > 2) paginationHTML += '<span class="text-gray-500">...</span>';
        }

        for (let i = startPage; i <= endPage; i++) {
            const activeClass = i === p.currentPage ? 'bg-yellow-600 text-white' : 'bg-gray-700 hover:bg-gray-600 text-white';
            paginationHTML += `<button onclick="app.loadBlataklifLeads(${i})" class="px-3 py-1 ${activeClass} rounded text-sm transition">${i}</button>`;
        }

        if (endPage < p.totalPages) {
            if (endPage < p.totalPages - 1) paginationHTML += '<span class="text-gray-500">...</span>';
            paginationHTML += `<button onclick="app.loadBlataklifLeads(${p.totalPages})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">${p.totalPages}</button>`;
        }

        if (p.currentPage < p.totalPages) {
            paginationHTML += `<button onclick="app.loadBlataklifLeads(${p.currentPage + 1})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">بعدی</button>`;
        } else {
            paginationHTML += `<button disabled class="px-3 py-1 bg-gray-800 text-gray-600 rounded text-sm cursor-not-allowed">بعدی</button>`;
        }

        paginationHTML += `<span class="text-xs text-gray-400 ml-4">نمایش ${(p.currentPage - 1) * p.perPage + 1} تا ${Math.min(p.currentPage * p.perPage, p.totalCount)} از ${p.totalCount}</span>`;
        paginationHTML += '</div>';
        container.innerHTML = paginationHTML;
    },

	    renderLeads() {
	        const container = document.getElementById('leads-list');
	        if(!container) return;
        
        if(this.state.leads.length === 0) {
            const searchValue = document.getElementById('leads-search')?.value || '';
            
            if (searchValue.trim()) {
                container.innerHTML = `
                    <div class="text-center py-12">
                        <div class="mb-4">
                            <i class="fa-solid fa-search text-4xl text-gray-600"></i>
                        </div>
                        <p class="text-gray-400 font-semibold text-lg">لیدی با این مشخصات یافت نشد</p>
                        <p class="text-gray-500 text-sm mt-3">جستجو: <span class="text-yellow-500 font-mono">${this.escapeHtml(searchValue)}</span></p>
                        <p class="text-gray-600 text-xs mt-4">
                            💡 احتمالاً این لید برای مسئول دیگری است یا هنوز ثبت نشده است
                        </p>
                        <button onclick="document.getElementById('leads-search').value = ''; app.loadLeads();" class="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded transition">
                            پاک کردن جستجو
                        </button>
                    </div>
                `;
            } else {
                container.innerHTML = '<div class="text-center text-gray-500 py-8">لیدی یافت نشد</div>';
            }
            return;
        }
        
	        container.innerHTML = this.state.leads.map(lead => {
	            const statusKey = lead.sale_status === 1 ? 'success' : (lead.sale_status === 0 ? 'failed' : 'unknown');
	            const statusMeta = this.state.saleStatusMap[statusKey] || this.state.saleStatusMap.unknown;
	            const regDate = lead.register_time_jalali || (lead.register_time ? toPersianDate(lead.register_time) : '');
	            const products = this.escapeHtml(this.getLeadProductsDisplayValue(lead)) || '-';
	            
	            return `
	                <div class="glass p-4 hover:bg-gray-800/50 transition cursor-pointer" onclick="app.openPersonProfile('${lead.didar_contact_id || lead.id || ''}')">
	                    <div class="flex justify-between items-start mb-2">
	                        <div>
                            <div class="font-bold text-white text-sm">${lead.first_name || ''} ${lead.last_name || ''}</div>
                            <div class="text-xs text-gray-400 mt-1">${lead.mobile_phone || '-'} ${lead.email ? '| ' + lead.email : ''}</div>
                            <div class="text-[11px] text-gray-500 mt-1">مسئول: ${lead.owner_name || '---'}</div>
	                        </div>
	                        <div class="flex flex-col items-end gap-1">
	                            <span class="text-xs px-2 py-1 rounded ${statusMeta.bg} text-white">${statusMeta.icon} ${statusMeta.title}</span>
	                        </div>
	                    </div>
	                    <div class="text-[11px] text-gray-400 mt-1">محصولات: ${products}</div>
	                    <div class="flex justify-between items-center mt-2 text-xs text-gray-500">
	                        <span>تاریخ ثبت: ${regDate || '-'}</span>
                        <span>${statusMeta.title}</span>
                    </div>
                </div>
            `;
        }).join('');
    },

    // DELETE LEAD (admin only)
	    async deleteLead(contactId) {
        if (!this.canActionClient('leads.delete')) {
            Swal.fire('خطا', 'دسترسی به حذف لید ندارید', 'error');
            return;
        }

        if (!contactId) {
            Swal.fire('خطا', 'شناسه لید مشخص نشده است', 'error');
            return;
        }

        const result = await Swal.fire({
            title: 'آیا مطمئن هستید؟',
            text: 'این لید و اطلاعات مرتبط (فعالیت‌ها و معاملات محلی) حذف خواهد شد',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#3085d6',
            confirmButtonText: 'بله، حذف کن',
            cancelButtonText: 'لغو',
            reverseButtons: true
        });

        if (!result.isConfirmed) {
            return;
        }

        Swal.fire({
            title: 'در حال حذف...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('delete_lead', { contact_id: contactId });
            Swal.close();
            if (res.status === 'success') {
                Swal.fire('موفق', res.message || 'لید با موفقیت حذف شد', 'success');
                // Refresh leads list
                if (document.getElementById('leads-list')) {
                    this.loadLeads(1);
                }
                // Refresh deals list if open
                if (document.getElementById('deals-container')) {
                    this.loadDeals();
                }
                // Close profile modal if this lead is open
                if (window.currentContactId && window.currentContactId === contactId) {
                    this.closeModal('modalProfile');
                    window.currentContactId = null;
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در حذف لید', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در حذف لید: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    renderLeadsPagination() {
        const container = document.getElementById('leads-pagination');
        if(!container) return;
        
        const p = this.state.leadsPagination;
        
        // Calculate page range to show
        const maxVisiblePages = 5;
        let startPage = Math.max(1, p.currentPage - Math.floor(maxVisiblePages / 2));
        let endPage = Math.min(p.totalPages, startPage + maxVisiblePages - 1);
        
        // Adjust if we're near the end
        if (endPage - startPage < maxVisiblePages - 1) {
            startPage = Math.max(1, endPage - maxVisiblePages + 1);
        }
        // Fix for negative startPage if totalPages is small
        startPage = Math.max(1, startPage);
        
        let paginationHTML = '<div class="flex items-center justify-center gap-2 mt-4 flex-wrap">';
        
        // Info text
        const from = p.totalCount > 0 ? (p.currentPage - 1) * p.perPage + 1 : 0;
        const to = Math.min(p.currentPage * p.perPage, p.totalCount);
        paginationHTML += `<span class="text-xs text-gray-400 ml-4 order-last md:order-first w-full md:w-auto text-center">نمایش ${from} تا ${to} از ${p.totalCount}</span>`;
        
        // Previous button
        if (p.currentPage > 1) {
            paginationHTML += `<button onclick="app.loadLeads(${p.currentPage - 1})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">قبلی</button>`;
        } else {
            paginationHTML += `<button disabled class="px-3 py-1 bg-gray-800 text-gray-600 rounded text-sm cursor-not-allowed">قبلی</button>`;
        }
        
        // First page
        if (startPage > 1) {
            paginationHTML += `<button onclick="app.loadLeads(1)" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">1</button>`;
            if (startPage > 2) {
                paginationHTML += `<span class="text-gray-500">...</span>`;
            }
        }
        
        // Page numbers
        for (let i = startPage; i <= endPage; i++) {
            if (i === p.currentPage) {
                paginationHTML += `<button class="px-3 py-1 bg-blue-600 text-white rounded text-sm font-bold">${i}</button>`;
            } else {
                paginationHTML += `<button onclick="app.loadLeads(${i})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">${i}</button>`;
            }
        }
        
        // Last page
        if (endPage < p.totalPages) {
            if (endPage < p.totalPages - 1) {
                paginationHTML += `<span class="text-gray-500">...</span>`;
            }
            paginationHTML += `<button onclick="app.loadLeads(${p.totalPages})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">${p.totalPages}</button>`;
        }
        
        // Next button
        if (p.currentPage < p.totalPages) {
            paginationHTML += `<button onclick="app.loadLeads(${p.currentPage + 1})" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 text-white rounded text-sm transition">بعدی</button>`;
        } else {
            paginationHTML += `<button disabled class="px-3 py-1 bg-gray-800 text-gray-600 rounded text-sm cursor-not-allowed">بعدی</button>`;
        }
        
        paginationHTML += '</div>';
        container.innerHTML = paginationHTML;
    },


    // DEALS
    async loadDeals() {
	        const container = document.getElementById('deals-container');
	        if(!container) return;
	        container.innerHTML = '<div class="text-center py-20 col-span-full"><div class="loading-spinner mb-4 mx-auto"></div><p class="text-gray-400">در حال دریافت معاملات...</p></div>';
	        
	        const pipelineStageFilter = document.getElementById('deals-pipeline-filter')?.value || 'all';
	        const statusFilterRaw = document.getElementById('deals-status-filter')?.value || 'all';
	        const ownerId = document.getElementById('deals-owner-filter')?.value || '';
            const search = document.getElementById('deals-search')?.value || '';
            const customerCodeSearch = document.getElementById('deals-customer-code-search')?.value || '';
            
            const dateFromJ = (document.getElementById('deals-register-from')?.value || '').trim();
            const dateToJ = (document.getElementById('deals-register-to')?.value || '').trim();
            const dateFromG = dateFromJ ? (persianToGregorian(this.toEnglishDigits(dateFromJ), '00:00') || '') : '';
            const dateToG = dateToJ ? (persianToGregorian(this.toEnglishDigits(dateToJ), '23:59') || '') : '';
            const registerFrom = dateFromG ? String(dateFromG).split(' ')[0] : '';
            const registerTo = dateToG ? String(dateToG).split(' ')[0] : '';

            const actFromJ = (document.getElementById('deals-activity-from')?.value || '').trim();
            const actToJ = (document.getElementById('deals-activity-to')?.value || '').trim();
            const actFromG = actFromJ ? (persianToGregorian(this.toEnglishDigits(actFromJ), '00:00') || '') : '';
            const actToG = actToJ ? (persianToGregorian(this.toEnglishDigits(actToJ), '23:59') || '') : '';
            const activityFrom = actFromG ? String(actFromG).split(' ')[0] : '';
            const activityTo = actToG ? String(actToG).split(' ')[0] : '';
            
	        const statusMap = { pending: 'Pending', won: 'Won', lost: 'Lost', all: 'all' };
	        const statusFilter = statusMap[statusFilterRaw] || 'all';
	        
            const payload = { 
                pipeline_stage: pipelineStageFilter, 
                status: statusFilter,
                search: search,
                customer_code_search: customerCodeSearch,
                legacy: '0'
            };
	        if (ownerId) payload.owner_id = ownerId;
            if (registerFrom) payload.register_date_from = registerFrom;
            if (registerTo) payload.register_date_to = registerTo;
            if (activityFrom) payload.activity_date_from = activityFrom;
            if (activityTo) payload.activity_date_to = activityTo;
            
	        const res = await this.req('get_deals', payload);
	        container.innerHTML = '';
	        
	        if(res.status === 'success' && res.deals && res.deals.length) {
                res.deals.forEach(d => {
                    const price = d.estimated_price ? parseInt(d.estimated_price).toLocaleString() : '0';
                    
                    const statusConfig = {
                        'Won': { text: 'موفق', badge: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30' },
                        'Lost': { text: 'ناموفق', badge: 'bg-red-500/20 text-red-400 border-red-500/30' },
                        'Pending': { text: 'جاری', badge: 'bg-blue-500/20 text-blue-400 border-blue-500/30' }
                    };
                    const status = statusConfig[d.status] || statusConfig['Pending'];
                    
                    const pipelineStage = d.pipeline_stage || 'معامله جدید';
                    const stageBadge = (()=>{
                        if (pipelineStage === 'برگزاری دوره') return '<span class="px-1.5 py-0.5 rounded text-[9px] bg-amber-500/20 text-amber-300 border border-amber-500/30 ml-1">دوره</span>';
                        if (pipelineStage === 'پشتیبانی') return '<span class="px-1.5 py-0.5 rounded text-[9px] bg-indigo-500/20 text-indigo-200 border border-indigo-500/30 ml-1">پشتیبانی</span>';
                        if (pipelineStage === 'مرجوعی' || (d.status === 'Lost' && (d.refund === '1' || d.refund_reason))) return '<span class="px-1.5 py-0.5 rounded text-[9px] bg-red-500/20 text-red-300 border border-red-500/30 ml-1">مرجوعی</span>';
                        return '';
                    })();

                    const productBadges = (d.products || []).map(p => 
                        `<span class="px-2 py-0.5 rounded-md text-[9px] bg-gray-700/50 text-gray-300 border border-gray-600/30 whitespace-nowrap">${this.escapeHtml(p.name)}</span>`
                    ).join('');

                    const contactId = d.contact_didar_id || d.contact_id || d.contactId || (d.contact ? d.contact.didar_contact_id : '') || d.local_person_id;
                    const dealId = d.didar_deal_id || d.id;
                    
                    container.innerHTML += `
                    <div class="glass p-5 rounded-2xl border border-gray-700/50 hover:border-yellow-500/40 transition-all duration-500 group flex flex-col h-full relative overflow-hidden">
                        <!-- Top Row: Status & Price -->
                        <div class="flex justify-between items-start mb-4">
                            <span class="text-[9px] px-2.5 py-1 rounded-full font-bold border ${status.badge}">
                                ${status.text}
                            </span>
                            <div class="text-left">
                                <span class="block text-[8px] text-gray-500 mb-0.5">مبلغ معامله</span>
                                <span class="text-xs font-black text-yellow-500 font-mono">${price} <span class="text-[9px] font-normal opacity-70">T</span></span>
                            </div>
                        </div>

                        <!-- Customer Info -->
                        <div class="mb-4 cursor-pointer" onclick="app.openDealModal('${dealId}')">
                            <h4 class="text-white font-bold text-sm mb-1 group-hover:text-yellow-500 transition-colors truncate" title="${this.escapeHtml(d.contact_name || '')}">
                                ${this.escapeHtml(d.contact_name || 'نامشخص')}
                            </h4>
                            <div class="flex items-center gap-2 text-[10px] text-gray-400">
                                <i class="fa-solid fa-briefcase text-[8px] opacity-50"></i>
                                <span class="truncate opacity-80" title="${this.escapeHtml(d.title || '')}">${this.escapeHtml(d.title || 'بدون عنوان')}</span>
                            </div>
                        </div>

                        <!-- Details Section -->
                        <div class="space-y-2 mb-4">
                            <!-- Pipeline Stage: Full Width for better visibility -->
                            <div class="bg-gray-900/60 p-2.5 rounded-xl border border-gray-800/50 flex flex-col gap-1">
                                <span class="text-[8px] text-gray-500 font-bold flex items-center gap-1">
                                    <i class="fa-solid fa-layer-group opacity-50"></i>
                                    مرحله کاریز
                                </span>
                                <div class="flex items-center gap-2">
                                    <span class="text-[11px] text-gray-200 font-bold">${pipelineStage}</span>
                                    ${stageBadge}
                                </div>
                            </div>
                            
                            <!-- Sales Owner -->
                            <div class="bg-gray-900/40 p-2 rounded-xl border border-gray-800/50 flex items-center justify-between">
                                <span class="text-[8px] text-gray-500 font-bold">مسئول فروش</span>
                                <div class="flex items-center gap-1.5">
                                    <div class="w-4 h-4 rounded-full bg-blue-500/20 flex items-center justify-center text-[8px] text-blue-400 font-bold border border-blue-500/30">
                                        ${(d.owner_name || '?').charAt(0)}
                                    </div>
                                    <span class="text-[10px] text-gray-300 font-bold">${this.escapeHtml(d.owner_name || '---')}</span>
                                </div>
                            </div>
                        </div>

                        <!-- Products List -->
                        ${productBadges ? `
                        <div class="mb-4 px-1">
                            <div class="flex flex-wrap gap-1.5">
                                ${productBadges}
                            </div>
                        </div>` : ''}

                        <!-- Footer Actions -->
                        <div class="mt-auto pt-4 border-t border-gray-800/50 flex justify-between items-center gap-2">
                            <div class="flex items-center gap-1 text-[9px] text-gray-500 italic">
                                <i class="fa-solid fa-calendar-day opacity-50"></i>
                                ${d.register_time ? toPersianDate(d.register_time) : '---'}
                            </div>
                            <div class="flex gap-1.5">
                                <button onclick="app.openDealModal('${dealId}')" 
                                    class="text-[10px] bg-amber-500/10 hover:bg-amber-500 text-amber-500 hover:text-black px-3 py-1.5 rounded-xl border border-amber-500/20 transition-all font-bold flex items-center gap-1">
                                    <i class="fa-solid fa-edit text-[9px]"></i>
                                    ویرایش
                                </button>
                                <button onclick="app.openPersonProfile('${contactId}')" 
                                    class="text-[10px] bg-blue-600/10 hover:bg-blue-600 text-blue-400 hover:text-white px-3 py-1.5 rounded-xl border border-blue-500/20 transition-all font-bold flex items-center gap-1">
                                    <i class="fa-solid fa-user text-[9px]"></i>
                                    مشاهده لید
                                </button>
                            </div>
                        </div>
                    </div>`;
                });
	        } else {
	            container.innerHTML = `
                <div class="col-span-full py-20 text-center">
                    <i class="fa-solid fa-sack-dollar text-5xl text-gray-800 mb-4 block"></i>
                    <p class="text-gray-500">معامله‌ای یافت نشد</p>
                </div>`;
	        }
            if (this.featureFlags.historyV1) this.replaceCurrentHistoryUiState('deals_filters');
	    },

        // Helper for quick filters
        setQuickStatusFilter(status) {
            const statusFilter = document.getElementById('deals-status-filter');
            if (statusFilter) {
                statusFilter.value = status;
                
                // Update chip active state
                document.querySelectorAll('.quick-filter-chip').forEach(chip => {
                    if (chip.getAttribute('data-status') === status) {
                        chip.classList.add('active');
                    } else {
                        chip.classList.remove('active');
                    }
                });
                
                this.loadDeals();
            }
        },
    
    // PERSONS
    async loadPersons() {
        const res = await this.req('get_leads', {});
        if(res.status === 'success') {
            this.state.leads = res.leads || [];
            this.renderLeads();
        }
    },

    async loadLegacyDeals() {
        const container = document.getElementById('legacy-deals-container');
        if (!container) return;
        container.innerHTML = '<div class="text-center py-20 col-span-full"><div class="loading-spinner mb-4 mx-auto"></div><p class="text-gray-400">\u062f\u0631 \u062d\u0627\u0644 \u0628\u0627\u0631\u06af\u0630\u0627\u0631\u06cc \u0645\u0639\u0627\u0645\u0644\u0627\u062a \u06af\u0630\u0634\u062a\u0647...</p></div>';

        const statusFilterRaw = document.getElementById('legacy-deals-status-filter')?.value || 'all';
        const ownerId = document.getElementById('legacy-deals-owner-filter')?.value || '';
        const search = document.getElementById('legacy-deals-search')?.value || '';

        const statusMap = { pending: 'Pending', won: 'Won', lost: 'Lost', all: 'all' };
        const statusFilter = statusMap[statusFilterRaw] || 'all';

        const payload = {
            status: statusFilter,
            search: search,
            legacy: '1'
        };
        if (ownerId) payload.owner_id = ownerId;

        const res = await this.req('get_deals', payload);
        container.innerHTML = '';

        if (res.status === 'success' && res.deals && res.deals.length) {
            res.deals.forEach(d => {
                const price = d.estimated_price ? parseInt(d.estimated_price).toLocaleString() : '0';

                const statusConfig = {
                    'Won': { text: '\u0645\u0648\u0641\u0642', badge: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30' },
                    'Lost': { text: '\u0646\u0627\u0645\u0648\u0641\u0642', badge: 'bg-red-500/20 text-red-400 border-red-500/30' },
                    'Pending': { text: '\u062f\u0631 \u062c\u0631\u06cc\u0627\u0646', badge: 'bg-blue-500/20 text-blue-400 border-blue-500/30' }
                };
                const status = statusConfig[d.status] || statusConfig['Pending'];

                const pipelineStage = d.pipeline_stage || d.pipeline_stage_title || '\u0645\u0631\u062d\u0644\u0647 \u0646\u0627\u0645\u0634\u062e\u0635';
                const dealTitle = d.title || d.pipeline_stage_title || d.pipeline_stage || d.contact_name || '\u0646\u0627\u0645\u0634\u062e\u0635';
                const contactId = d.contact_didar_id || d.contact_id || d.contactId || (d.contact ? d.contact.didar_contact_id : '') || d.local_person_id;
                const dealId = d.didar_deal_id || d.id;

                container.innerHTML += `
                <div class="glass p-5 rounded-2xl border border-gray-700/50 hover:border-yellow-500/40 transition-all duration-500 group flex flex-col h-full relative overflow-hidden">
                    <div class="flex justify-between items-start mb-4">
                        <div class="flex items-center gap-2">
                            <span class="text-[9px] px-2.5 py-1 rounded-full font-bold border ${status.badge}">
                                ${status.text}
                            </span>
                            <span class="text-[9px] px-2 py-1 rounded-full border border-purple-500/30 bg-purple-500/10 text-purple-300">\u0645\u0639\u0627\u0645\u0644\u0647 \u06af\u0630\u0634\u062a\u0647</span>
                        </div>
                        <div class="text-left">
                            <span class="block text-[8px] text-gray-500 mb-0.5">\u0627\u0631\u0632\u0634 \u0645\u0639\u0627\u0645\u0644\u0647</span>
                            <span class="text-xs font-black text-yellow-500 font-mono">${price} <span class="text-[9px] font-normal opacity-70">T</span></span>
                        </div>
                    </div>

                    <div class="mb-4 cursor-pointer" onclick="app.openDealModal('${dealId}')">
                        <h4 class="text-white font-bold text-sm mb-1 group-hover:text-yellow-500 transition-colors truncate" title="${this.escapeHtml(d.contact_name || '')}">
                            ${this.escapeHtml(d.contact_name || '\u0646\u0627\u0645\u0634\u062e\u0635')}
                        </h4>
                        <div class="flex items-center gap-2 text-[10px] text-gray-400">
                            <i class="fa-solid fa-briefcase text-[8px] opacity-50"></i>
                            <span class="truncate opacity-80" title="${this.escapeHtml(dealTitle)}">${this.escapeHtml(dealTitle)}</span>
                        </div>
                    </div>

                    <div class="space-y-2 mb-4">
                        <div class="bg-gray-900/60 p-2.5 rounded-xl border border-gray-800/50 flex flex-col gap-1">
                            <span class="text-[8px] text-gray-500 font-bold flex items-center gap-1">
                                <i class="fa-solid fa-layer-group opacity-50"></i>
                                \u0645\u0631\u062d\u0644\u0647 \u0641\u0631\u0648\u0634
                            </span>
                            <div class="flex items-center gap-2">
                                <span class="text-[11px] text-gray-200 font-bold">${this.escapeHtml(pipelineStage)}</span>
                            </div>
                        </div>

                        <div class="bg-gray-900/40 p-2 rounded-xl border border-gray-800/50 flex items-center justify-between">
                            <span class="text-[8px] text-gray-500 font-bold">\u0645\u0633\u0626\u0648\u0644</span>
                            <div class="flex items-center gap-1.5">
                                <div class="w-4 h-4 rounded-full bg-blue-500/20 flex items-center justify-center text-[8px] text-blue-400 font-bold border border-blue-500/30">
                                    ${(d.owner_name || '?').charAt(0)}
                                </div>
                                <span class="text-[10px] text-gray-300 font-bold">${this.escapeHtml(d.owner_name || '---')}</span>
                            </div>
                        </div>
                    </div>

                    <div class="mt-auto pt-4 border-t border-gray-800/50 flex justify-between items-center gap-2">
                        <div class="flex items-center gap-1 text-[9px] text-gray-500 italic">
                            <i class="fa-solid fa-calendar-day opacity-50"></i>
                            <span>${d.register_time ? toPersianDateTime(d.register_time) : '\u0646\u0627\u0645\u0634\u062e\u0635'}</span>
                        </div>
                        ${contactId ? `<button class="text-[10px] text-yellow-500 hover:text-yellow-300" onclick="event.stopPropagation(); app.openPersonProfile('${contactId}')">\u0645\u0634\u0627\u0647\u062f\u0647 \u0644\u06cc\u062f</button>` : ''}
                    </div>
                </div>
                `;
            });
        } else {
            container.innerHTML = '<div class="text-center py-12 text-gray-500">\u0647\u06cc\u0686 \u0645\u0639\u0627\u0645\u0644\u0647 \u06af\u0630\u0634\u062a\u0647\u200c\u0627\u06cc \u0648\u062c\u0648\u062f \u0646\u062f\u0627\u0631\u062f.</div>';
        }
    },

    // PERSON PROFILE
    openPersonProfile: async function(contactId) {
        const profileToken = this.createContextToken('profile', { contactId: String(contactId || '') });
        this.resetProfileModalState({ forOpen: true });
        const previousContactId = window.currentContactId;
        console.log('[CONTACT_SWITCH] Switching contact profile', {
            timestamp: new Date().toISOString(),
            previousContactId: previousContactId,
            newContactId: contactId,
            contactChanged: previousContactId !== contactId,
            url: window.location.href,
            userAgent: navigator.userAgent
        });

        window.currentContactId = contactId;
        document.getElementById('activity-contact-id').value = contactId;
        const dealContactId = document.getElementById('deal-contact-id');
        if (dealContactId) {
            dealContactId.value = contactId;
        }
        // Clear deal context when switching to a different contact to avoid cross-lead updates.
        if (previousContactId && previousContactId !== contactId) {
            const dealIdInput = document.getElementById('deal-id');
            const dealModal = document.getElementById('modalDeal');
            const isDealModalOpen = dealModal && !dealModal.classList.contains('hidden');
            
            if (dealIdInput) {
                // If deal modal is open and contact changed, clear deal-id to prevent errors
                if (isDealModalOpen && dealIdInput.value) {
                    dealIdInput.value = '';
                    const modalTitle = document.getElementById('deal-modal-title');
                    if (modalTitle) modalTitle.textContent = 'ثبت معامله جدید';
                    console.log('[CONTACT_SWITCH] Cleared deal-id because deal modal is open and contact changed');
                } else {
                    dealIdInput.value = '';
                }
            }
            const dealActDeal = document.getElementById('deal-activity-deal-id');
            if (dealActDeal) dealActDeal.value = '';
            const activityDealId = document.getElementById('activity-deal-id');
            if (activityDealId) activityDealId.value = '';
            this.state.currentDeal = null;
            console.log('[CONTACT_SWITCH] Cleared deal context to prevent cross-lead updates', {
                timestamp: new Date().toISOString(),
                previousContactId: previousContactId,
                newContactId: contactId,
                dealModalWasOpen: isDealModalOpen
            });
        }
        
        const res = await this.req('get_person_profile', {contact_id: contactId}, {
            requestKey: `profile:${String(contactId || '')}`,
            silentAbort: true
        });
        if (res?.aborted) return;
        if (this.dropIfStaleContext('profile', profileToken, { contactId: String(contactId || '') })) return;
        if(res.status === 'success') {
            const person = res.person;
            const fullName = `${person.first_name || ''} ${person.last_name || ''}`.trim() || 'بدون نام';
            this.state.currentLead = person;
            
            // ذخیره activities برای بررسی بعدی
            this.state.currentLeadActivities = res.activities || [];
            const stageSelect = document.getElementById('activity-virtual-stage');
            if (stageSelect) {
                stageSelect.value = '';
            }
            if (Array.isArray(res.deals)) {
                this.state.currentDeals = res.deals;
                this.populateActivityDealSelect(res.deals);
            } else {
                this.state.currentDeals = [];
                const activityDealId = document.getElementById('activity-deal-id');
                if (activityDealId) activityDealId.value = '';
                const stageDisplay = document.getElementById('activity-current-stage-display');
                if (stageDisplay) stageDisplay.innerHTML = '<span class="text-sm text-gray-500">مرحله تعیین نشده</span>';
            }
            const referralNoteInput = document.querySelector('textarea[name="referral_note"]');
            if (referralNoteInput) referralNoteInput.value = '';
            
            const referralPhones = [person.mobile_phone, person.secondary_mobile_phone].filter(Boolean).join(' ، ');
            const referralPhonesEl = document.getElementById('referral-phones');
            if (referralPhonesEl) referralPhonesEl.textContent = referralPhones || '-';
            
            document.getElementById('prof-name').textContent = fullName;
            document.getElementById('prof-mobile').textContent = person.mobile_phone || '-';
            const profEmail = document.getElementById('prof-email');
            if (profEmail) profEmail.textContent = person.email || '-';
            
            // Info tab
            document.getElementById('prof-info-name').textContent = fullName;
            const customerCodeEl = document.getElementById('prof-info-customer-code');
            if (customerCodeEl) customerCodeEl.textContent = String(person.customer_code || '-');
            document.getElementById('prof-info-mobile').textContent = person.mobile_phone || '-';
            document.getElementById('prof-info-mobile2').textContent = person.secondary_mobile_phone || '-';
            const mobile3El = document.getElementById('prof-info-mobile3');
            const mobile4El = document.getElementById('prof-info-mobile4');
            if (mobile3El) mobile3El.textContent = person.mobile_phone_3 || '-';
            if (mobile4El) mobile4El.textContent = person.mobile_phone_4 || '-';
            const profInfoEmail = document.getElementById('prof-info-email');
            if (profInfoEmail) profInfoEmail.textContent = person.email || '-';
            document.getElementById('prof-info-stage').innerHTML = '';
            document.getElementById('prof-info-owner').textContent = person.owner_name || '---';
            const prevOwnerEl = document.getElementById('prof-info-prev-owner');
            if (prevOwnerEl) prevOwnerEl.textContent = person.previous_owner_name || '-';
            const regDateDisplay = person.register_time_jalali || (person.register_time ? toPersianDate(person.register_time) : '-');
            const regEl = document.getElementById('prof-info-register');
            if (regEl) regEl.textContent = regDateDisplay || '-';
            const createdByContainer = document.getElementById('prof-info-created-by-container');
            const createdViaContainer = document.getElementById('prof-info-created-via-container');
            const createdByEl = document.getElementById('prof-info-created-by');
            const createdViaEl = document.getElementById('prof-info-created-via');
            const leadCreatedBy = String(person.created_by_name || '').trim();
            const leadCreatedVia = this.translateCreatedVia(person.created_via || '');
            if (createdByEl) createdByEl.textContent = leadCreatedBy || '-';
            if (createdViaEl) createdViaEl.textContent = leadCreatedVia || '-';
            if (createdByContainer) createdByContainer.classList.toggle('hidden', !this.isAdmin());
            if (createdViaContainer) createdViaContainer.classList.toggle('hidden', !this.isAdmin());
            const saleEl = document.getElementById('prof-info-sale-status');
            if (saleEl) {
                saleEl.textContent = String(person.sale_status) === '1' ? 'موفق' : 'ناموفق';
            }
            const nationalEl = document.getElementById('prof-info-national-id');
            if (nationalEl) nationalEl.textContent = person.national_id || '-';
            document.getElementById('prof-info-city').textContent = person.city || '-';
            document.getElementById('prof-info-job').textContent = person.job_title || '-';
            const jobDescEl = document.getElementById('prof-info-job-desc');
            if (jobDescEl) jobDescEl.textContent = person.job_description || '-';
            document.getElementById('prof-info-source').textContent = this.getLeadSourceLabel(person.acquaintance_source);
            document.getElementById('prof-info-source-detail').textContent = this.getLeadDetailLabel(person.acquaintance_detail);
            document.getElementById('prof-info-topic').textContent = this.getContentTopicLabel(person.content_topic);
            document.getElementById('prof-info-products').textContent = this.getLeadProductsDisplayValue(person);
            const acqEl = document.getElementById('prof-info-acq-duration');
            if (acqEl) acqEl.textContent = person.acquaintance_duration || '-';
            const extraEl = document.getElementById('prof-info-extra-info');
            if (extraEl) extraEl.textContent = person.extra_info || '-';

            const financialLabel = (val) => {
                switch (val) {
                    case 'high': return 'بالا';
                    case 'medium_high': return 'متوسط رو به بالا';
                    case 'medium_low': return 'متوسط رو به پایین';
                    case 'low': return 'پایین';
                    default: return '-';
                }
            };
            const rangeLabel = (val, type) => {
                const map = type === 'asset' ? {
                    '0-50': '0 تا 50 میلیون تومان',
                    '50-200': '50 تا 200 میلیون تومان',
                    '200-500': '200 تا 500 میلیون تومان',
                    '500-1000': '500 تا 1000 میلیون تومان',
                    '1000-plus': 'بیش از 1 میلیارد تومان'
                } : {
                    '0-20': '0 تا 20 میلیون تومان',
                    '20-100': '20 تا 100 میلیون تومان',
                    '100-200': '100 تا 200 میلیون تومان',
                    '200-500': '200 تا 500 میلیون تومان',
                    '500-plus': 'بیش از 500 میلیون تومان'
                };
                return map[val] || '-';
            };

        // Customer level
        const customerLevelEl = document.getElementById('prof-info-customer-level');
        if (customerLevelEl) {
            const levelMap = {
                'طلایی': 'طلایی',
                'نقره‌ای': 'نقره‌ای',
                'برنز': 'برنز'
            };
            customerLevelEl.textContent = levelMap[person.customer_level] || '-';
        }
        const customerLevelNotesEl = document.getElementById('prof-info-customer-level-notes');
        if (customerLevelNotesEl) {
            customerLevelNotesEl.textContent = person.customer_level_notes || '';
        }

        const finEl = document.getElementById('prof-info-financial-level');
        if (finEl) finEl.textContent = financialLabel(person.financial_level);
        const assetEl = document.getElementById('prof-info-asset');
        if (assetEl) assetEl.textContent = rangeLabel(person.asset_estimation, 'asset');
        const incomeEl = document.getElementById('prof-info-income');
        if (incomeEl) incomeEl.textContent = rangeLabel(person.income_estimation, 'income');
            const purchaseBadge = document.getElementById('prof-info-purchase-status');
            if (purchaseBadge) {
                if (person.has_previous_purchase === 1 || person.has_previous_purchase === '1') {
                    purchaseBadge.textContent = 'خرید قبلی دارد';
                } else if (person.has_previous_purchase === 0 || person.has_previous_purchase === '0') {
                    purchaseBadge.textContent = 'خرید قبلی ندارد';
                } else {
                    purchaseBadge.textContent = '-';
                }
            }

            // Requested services: try person first, then latest deal
            let reqServices = person.requested_services || '';
            if (typeof reqServices === 'string') {
                try {
                    const parsed = JSON.parse(reqServices);
                    if (Array.isArray(parsed)) reqServices = parsed;
                } catch (e) {
                    // keep string
                }
            }
            if (!reqServices || (Array.isArray(reqServices) && reqServices.length === 0)) {
                const currentDeal = (this.state.currentDeals || [])[0] || this.state.currentDeal;
                if (currentDeal && currentDeal.requested_services) {
                    try {
                        const parsed = JSON.parse(currentDeal.requested_services);
                        if (Array.isArray(parsed)) reqServices = parsed;
                    } catch (e) {
                        reqServices = currentDeal.requested_services;
                    }
                }
            }
            const reqEl = document.getElementById('prof-info-requested-services');
            if (reqEl) {
                if (Array.isArray(reqServices)) {
                    reqEl.textContent = reqServices.length ? reqServices.join('، ') : '-';
                } else if (reqServices) {
                    reqEl.textContent = reqServices;
                } else {
                    reqEl.textContent = '-';
                }
            }
            
            // مخفی کردن فیلد شیوه آشنایی برای کاربران غیر ادمین و غیر CRM
            const sourceContainer = document.getElementById('prof-info-source-container');
            if (sourceContainer) {
                if (this.isAdminOrCrmSpecialist()) {
                    sourceContainer.classList.remove('hidden');
                } else {
                    sourceContainer.classList.add('hidden');
                }
            }
            document.getElementById('prof-info-background').textContent = person.background_info || '-';
            const editBtn = document.getElementById('lead-edit-btn');
            if (editBtn) {
                // RBAC-driven edit visibility; CRM specialist remains view-only via capability config.
                const canEditByPermission = this.canActionClient('leads.edit');
                const canEdit = canEditByPermission && (this.isAdminOrCrmSpecialist() || this.isLeadOwner(person));
                editBtn.classList.toggle('hidden', !canEdit);
            }
            
            // Render deals
        const dealsDiv = document.getElementById('prof-deals');
        dealsDiv.innerHTML = '';
        const currentDeals = Array.isArray(res.deals) ? res.deals : [];
        const legacyDeals = Array.isArray(res.legacy_deals) ? res.legacy_deals : [];

        const renderDealCard = (d, isLegacy = false) => {
            const price = d.estimated_price ? parseInt(d.estimated_price).toLocaleString() : '0';
            const statusColor = d.status === 'Won' ? 'border-green-500' : (d.status === 'Lost' ? 'border-red-500' : 'border-blue-500');
            const statusText = d.status === 'Won' ? '\u0645\u0648\u0641\u0642' : (d.status === 'Lost' ? '\u0646\u0627\u0645\u0648\u0641\u0642' : '\u062f\u0631 \u062c\u0631\u06cc\u0627\u0646');
            const dealId = d.didar_deal_id || d.id;
            const legacyBadge = isLegacy
                ? '<span class="inline-block ml-2 px-2 py-0.5 rounded bg-purple-500/20 text-purple-300 text-[10px]">\u0645\u0639\u0627\u0645\u0644\u0647 \u06af\u0630\u0634\u062a\u0647 \u062f\u06cc\u062f\u0627\u0631</span>'
                : '';

            const deleteBtn = this.isAdmin() ? `
                    <button onclick="event.stopPropagation(); app.deleteDeal('${dealId}')"
                            class="text-red-500 hover:text-red-700 text-xs px-2 py-1 rounded transition"
                            title="\u062d\u0630\u0641 \u0645\u0639\u0627\u0645\u0644\u0647">
                        <i class="fa-solid fa-trash"></i>
                    </button>` : '';

            return `
                    <div class="bg-gray-700/30 p-3 rounded border-l-4 ${statusColor} mb-2 hover:bg-gray-700/50 transition relative">
                        ${deleteBtn ? `<div class="absolute top-2 left-2 z-10">${deleteBtn}</div>` : ''}
                        <div class="cursor-pointer" onclick="app.openDealModal('${dealId}')">
                            <div class="flex justify-between items-start">
                                <div class="flex-1">
                                    <span class="text-sm text-white font-bold">${d.title || '-'}</span>
                                    ${legacyBadge}
                                    <div class="text-[10px] text-gray-500 mt-1">
                                        <span class="inline-block px-2 py-0.5 rounded ${d.status === 'Won' ? 'bg-green-500/20 text-green-400' : (d.status === 'Lost' ? 'bg-red-500/20 text-red-400' : 'bg-blue-500/20 text-blue-400')}">${statusText}</span>
                                        ${d.register_time ? ' | ' + toPersianDate(d.register_time) : ''}
                                    </div>
                                </div>
                                <span class="text-xs text-yellow-500 font-mono font-bold ml-2">${price} T</span>
                            </div>
                        </div>
                    </div>`;
        };

        if (currentDeals.length) {
            currentDeals.forEach(d => {
                dealsDiv.innerHTML += renderDealCard(d, false);
            });
        }

        if (legacyDeals.length) {
            if (currentDeals.length) {
                dealsDiv.innerHTML += '<div class="text-xs text-gray-400 mt-4 mb-2">\u0645\u0639\u0627\u0645\u0644\u0627\u062a \u06af\u0630\u0634\u062a\u0647 \u062f\u06cc\u062f\u0627\u0631</div>';
            }
            legacyDeals.forEach(d => {
                dealsDiv.innerHTML += renderDealCard(d, true);
            });
        }

        if (!currentDeals.length && !legacyDeals.length) {
            dealsDiv.innerHTML = '<p class="text-sm text-gray-500">\u0647\u06cc\u0686 \u0645\u0639\u0627\u0645\u0644\u0647\u200c\u0627\u06cc \u0628\u0631\u0627\u06cc \u0646\u0645\u0627\u06cc\u0634 \u0648\u062c\u0648\u062f \u0646\u062f\u0627\u0631\u062f.</p>';
        }

        // Render activities
        const actDiv = document.getElementById('prof-activities');
        actDiv.innerHTML = '';
        if(res.activities && res.activities.length) {
            res.activities.forEach(a => {
                // Extract virtual stage and purchase info from result_note
                let displayNote = a.result_note || a.note || 'بدون توضیحات';
                let stageBadge = '';
                let purchaseBadge = '';
                
                // Check for virtual stage in stage field or result_note
                if (a.stage) {
                    const stageTitles = {
                        'contact_no_answer': 'تماس عدم پاسخ',
                        'contact_followup': 'تماس پیگیری',
                        'deal_registered': 'ثبت معامله و لینک پرداخت',
                        'payment': 'پرداخت',
                        'payment_followup': 'پیگیری مانده حساب',
                        'course_delivered': 'برگزاری دوره',
                        'support': 'پشتیبانی'
                    };
                    const stageTitle = stageTitles[a.stage];
                    if (stageTitle) {
                        stageBadge = `<span class="inline-block bg-purple-600 text-xs px-2 py-0.5 rounded mb-2">📍 ${stageTitle}</span>`;
                    }
                }
                
                // Check for purchase info in result_note
                if (displayNote.includes('🟢 این لید قبلاً خرید دیگری')) {
                    purchaseBadge = `<span class="inline-block bg-green-600 text-xs px-2 py-0.5 rounded mb-2 mr-2">🟢 خرید قبلی با شماره دیگر: بله</span>`;
                    // Remove from display note
                    displayNote = displayNote.replace(/🟢 این لید قبلاً خرید دیگری با شماره دیگری داشته است\./g, '').trim();
                    displayNote = displayNote.replace(/⚪ این لید خرید قبلی با شماره دیگر نداشته است\./g, '').trim();
                    displayNote = displayNote.replace(/⚪ خرید قبلی با شماره دیگر: نامشخص/g, '').trim();
                    displayNote = displayNote.replace(/📍 مرحله کاریز:.*/g, '').trim();
                } else if (displayNote.includes('⚪ این لید خرید قبلی') || displayNote.includes('⚪ خرید قبلی با شماره دیگر')) {
                    if (displayNote.includes('نامشخص')) {
                        purchaseBadge = `<span class="inline-block bg-yellow-600 text-xs px-2 py-0.5 rounded mb-2 mr-2">⚪ خرید قبلی با شماره دیگر: نامشخص</span>`;
                    } else {
                        purchaseBadge = `<span class="inline-block bg-gray-600 text-xs px-2 py-0.5 rounded mb-2 mr-2">⚪ خرید قبلی با شماره دیگر: خیر</span>`;
                    }
                    // Remove from display note
                    displayNote = displayNote.replace(/⚪ این لید خرید قبلی با شماره دیگر نداشته است\./g, '').trim();
                    displayNote = displayNote.replace(/⚪ خرید قبلی با شماره دیگر: نامشخص/g, '').trim();
                    displayNote = displayNote.replace(/📍 مرحله کاریز:.*/g, '').trim();
                }
                
                const activityId = a.didar_activity_id || a.id;
                const deleteBtn = this.isAdmin() ? `
                    <button onclick="event.stopPropagation(); app.deleteActivity('${activityId}')" 
                            class="text-red-500 hover:text-red-700 text-xs px-2 py-1 rounded transition" 
                            title="حذف فعالیت">
                        <i class="fa-solid fa-trash"></i>
                    </button>` : '';
                
                // Check if activity is scheduled - check multiple conditions
                const hasDueDate = a.due_date && a.due_date.trim() !== '';
                const isScheduledFlag = a.is_scheduled === 1 || a.is_scheduled === true || a.is_scheduled === '1';
                const isScheduled = isScheduledFlag || (!a.is_done && hasDueDate);
                const isEdited = a.last_update_time && a.last_update_time !== a.register_date && a.last_update_time !== a.created_at;
                
                // Parse and format dates
                let dueDate = '';
                if (hasDueDate) {
                    try {
                        dueDate = toPersianDateTime(a.due_date);
                    } catch (e) {
                        console.warn('Error parsing due_date:', a.due_date, e);
                        dueDate = a.due_date; // Fallback to raw value
                    }
                }
                
                const registerDate = a.register_date ? toPersianDateTime(a.register_date) : '';
                const durationMinutes = Math.max(0, parseInt(a.duration ?? a.activity_duration ?? 0, 10) || 0);
                
                let statusBadge = '';
                if (a.is_done === 1 || a.is_done === true || a.is_done === '1') {
                    statusBadge = '<span class="text-[10px] bg-green-500/20 text-green-300 px-2 py-0.5 rounded">✓ انجام شده</span>';
                } else if (isScheduled) {
                    statusBadge = '<span class="text-[10px] bg-yellow-500/20 text-yellow-300 px-2 py-0.5 rounded">⏰ برنامه‌ریزی شده</span>';
                } else {
                    statusBadge = '<span class="text-[10px] bg-blue-500/20 text-blue-300 px-2 py-0.5 rounded">📝 ثبت شده</span>';
                }
                
                const editBtn = !a.is_done && isScheduled ? `
                    <button onclick="event.stopPropagation(); app.openEditScheduledActivityModal('${activityId}')" 
                            class="text-blue-500 hover:text-blue-700 text-xs px-2 py-1 rounded transition" 
                            title="ویرایش فعالیت">
                        <i class="fa-solid fa-edit"></i>
                    </button>` : '';
                
                const clickHandler = !a.is_done && isScheduled ? `onclick="app.openEditScheduledActivityModal('${activityId}')"` : '';
                const cursorClass = !a.is_done && isScheduled ? 'cursor-pointer hover:bg-gray-700/30 transition' : '';
                
                actDiv.innerHTML += `
                <div class="relative pr-8 pb-4">
                    <div class="absolute top-1 right-0 w-4 h-4 bg-slate-700 rounded-full border-2 ${a.is_done ? 'border-green-500' : isScheduled ? 'border-yellow-500' : 'border-blue-500'} z-10"></div>
                    ${editBtn || deleteBtn ? `<div class="absolute top-1 left-1 z-10 flex gap-1">${editBtn}${deleteBtn}</div>` : ''}
                    ${isEdited ? `<div class="absolute top-1 left-12 z-10"><span class="text-[9px] bg-purple-500/20 text-purple-300 px-1.5 py-0.5 rounded" title="ویرایش شده"><i class="fa-solid fa-pencil"></i></span></div>` : ''}
                    <div class="bg-gray-800 p-3 rounded-lg ${cursorClass}" ${clickHandler}>
                        <div class="flex justify-between mb-1">
                            <div class="flex items-center gap-2">
                                <span class="text-xs font-bold text-blue-300">
                                    <i class="fa-solid fa-${a.is_done ? 'check-circle' : isScheduled ? 'clock' : 'calendar-check'} ml-1"></i>
                                    ${a.activity_type_title || 'فعالیت'}
                                </span>
                                ${statusBadge}
                            </div>
                            <span class="text-[9px] text-gray-500">${registerDate}</span>
                        </div>
                        <div class="mb-2">
                            ${stageBadge || ''}
                            ${purchaseBadge || ''}
                        </div>
                        <div class="text-[10px] text-gray-400 space-y-0.5 mb-2">
                            ${registerDate ? `<div><i class="fa-solid fa-calendar-plus ml-1"></i> تاریخ ثبت: ${registerDate}</div>` : ''}
                            ${dueDate ? `<div><i class="fa-solid fa-calendar-clock ml-1"></i> تاریخ/ساعت برنامه‌ریزی: ${dueDate}</div>` : ''}
                            ${isScheduled && !dueDate ? `<div><i class="fa-solid fa-clock ml-1"></i> فعالیت برنامه‌ریزی شده (تاریخ مشخص نشده)</div>` : ''}
                            <div><i class="fa-solid fa-hourglass-half ml-1"></i> مدت فعالیت: ${durationMinutes > 0 ? `${this.formatNumber(durationMinutes)} دقیقه` : 'ثبت نشده'}</div>
                        </div>
                        <p class="text-xs text-gray-300 leading-relaxed">${displayNote || '<span class="text-gray-500 italic">بدون توضیحات</span>'}</p>
                    </div>
                </div>`;
            });
            } else {
                actDiv.innerHTML = '<p class="text-sm text-gray-500">هیچ فعالیتی ثبت نشده است.</p>';
            }
            
            // Load activity types for modal
            this.loadActivityTypesForModal();
            
            // Render satisfactions if available
            if (res.satisfactions) {
                this.renderSatisfactions(res.satisfactions);
            }
            
            this.switchProfileTab('info');
            this.openModal('modalProfile', { source: 'user', entityId: String(contactId || '') });
            
            // بررسی نمایش فیلد خرید قبلی بعد از باز شدن مودال
            setTimeout(() => {
                this.handleActivityFormVisibility();
            }, 200);
        }
    },
    
    renderSatisfactions(satisfactions) {
        const container = document.getElementById('prof-satisfactions');
        if (!container) return;
        
        if (satisfactions && satisfactions.length > 0) {
            container.innerHTML = satisfactions.map(sat => {
                const stars = '⭐'.repeat(sat.satisfaction_score || 0);
                const date = sat.register_date ? toPersianDateTime(sat.register_date) : '';
                const registeredBy = sat.registered_by_name || 'نامشخص';
                const note = sat.satisfaction_note ? `<p class="text-sm text-gray-300 mt-2">${this.escapeHtml(sat.satisfaction_note)}</p>` : '';
                
                return `
                    <div class="glass p-4 rounded-lg">
                        <div class="flex justify-between items-start mb-2">
                            <div>
                                <div class="text-lg mb-1">${stars}</div>
                                <div class="text-xs text-gray-400">ثبت شده توسط: ${this.escapeHtml(registeredBy)}</div>
                            </div>
                            <div class="text-xs text-gray-500">${date}</div>
                        </div>
                        ${note}
                    </div>
                `;
            }).join('');
        } else {
            container.innerHTML = '<p class="text-sm text-gray-500 text-center py-4">هیچ رضایتی ثبت نشده است.</p>';
        }
    },
    
    openSatisfactionModal() {
        const contactId = window.currentContactId;
        if (!contactId) {
            Swal.fire('خطا', 'لید انتخاب نشده است', 'error');
            return;
        }
        document.getElementById('satisfaction-contact-id').value = contactId;
        document.querySelectorAll('input[name="satisfaction_score"]').forEach(r => r.checked = false);
        document.getElementById('satisfaction-score-text').textContent = 'امتیاز را انتخاب کنید';
        document.querySelector('textarea[name="satisfaction_note"]').value = '';
        this.openModal('modalSatisfaction');
    },
    
    selectSatisfactionScore(score) {
        document.getElementById(`score-${score}`).checked = true;
        const texts = {
            1: 'خیلی ضعیف',
            2: 'ضعیف',
            3: 'متوسط',
            4: 'خوب',
            5: 'عالی'
        };
        document.getElementById('satisfaction-score-text').textContent = texts[score] || '';
    },
    
    async saveSatisfaction(e) {
        e.preventDefault();
        const form = e.target;
        const data = Object.fromEntries(new FormData(form));
        
        if (!data.satisfaction_score) {
            Swal.fire('خطا', 'لطفاً امتیاز رضایت را انتخاب کنید', 'error');
            return;
        }
        
        Swal.fire({
            title: 'در حال ثبت...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('save_satisfaction', data);
            if (res.status === 'success') {
                Swal.fire({toast: true, position: 'bottom-end', icon: 'success', title: 'رضایت ثبت شد', showConfirmButton: false, timer: 2000});
                this.closeModal('modalSatisfaction');
                form.reset();
                
                // Reload person profile to show new satisfaction
                if (window.currentContactId) {
                    await this.openPersonProfile(window.currentContactId);
                    this.switchProfileTab('satisfactions');
                }
            } else {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
            }
        } catch (error) {
            console.error('Error in saveSatisfaction:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در ثبت رضایت: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    openDidarSearchModal() {
        if (!this.isDidarUiEnabled()) {
            this.showDidarUiDisabledMessage();
            return;
        }
        document.getElementById('didar-search-phone').value = '';
        document.getElementById('didar-search-keywords').value = '';
        document.getElementById('didar-search-results').innerHTML = '<p class="text-center text-gray-500 py-8">برای جستجو، شماره تلفن یا کلمات کلیدی را وارد کنید</p>';
        this.openModal('modalDidarSearch');
    },

    async openLocalMobileLookup() {
        const { value } = await Swal.fire({
            title: 'استعلام سریع شماره موبایل',
            input: 'text',
            inputPlaceholder: '۱۰ رقم موبایل بدون صفر اول (مثل: 9123456789)',
            inputAttributes: {
                maxlength: 10,
                oninput: function() {
                    // فقط اجازه تایپ اعداد بده و اعداد فارسی/عربی را به انگلیسی تبدیل کن
                    this.value = this.value.replace(/[^\d]/g, '').replace(/[۰-۹]/g, function(d) {
                        return String.fromCharCode(d.charCodeAt(0) - 1728);
                    }).replace(/[٠-٩]/g, function(d) {
                        return String.fromCharCode(d.charCodeAt(0) - 1584);
                    });
                }
            },
            showCancelButton: true,
            confirmButtonText: 'جستجو',
            cancelButtonText: 'لغو',
            inputValidator: (v) => {
                const raw = (v || '').trim();
                if (!raw) return 'شماره موبایل را وارد کنید';
                const digits = raw.replace(/[^\d]/g, '');
                if (digits.length !== 10) return 'شماره موبایل باید دقیقاً 10 رقم باشد (بدون صفر اول)';
                if (!digits.startsWith('9')) return 'شماره موبایل باید با رقم 9 شروع شود';
                return null;
            }
        });
        if (!value) return;

        const digits = toEnglishDigits(String(value).trim()).replace(/[^\d]/g, '');

        Swal.fire({
            title: 'در حال بررسی...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('lookup_mobile', { mobile_phone: digits });
            Swal.close();

            if (res.status !== 'success') {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
                return;
            }

            const matches = Array.isArray(res.matches) ? res.matches : [];
            if (matches.length === 0) {
                Swal.fire('یافت نشد', 'این شماره در دیتابیس محلی موجود نیست.', 'info');
                return;
            }

            const html = matches.map(m => {
                const displayName = m.display_name || 'بدون نام';
                const ownerName = m.owner_name || 'نامشخص';
                const previousOwnerName = m.previous_owner_name || '-';
                const contactId = m.didar_contact_id || '-';

                return `
                    <div class="text-right p-3 bg-gray-800 rounded mb-2">
                        <div class="text-sm text-white font-bold">${this.escapeHtml(displayName)}</div>
                        <div class="text-xs text-gray-300 mt-1">شناسه: ${this.escapeHtml(String(contactId))}</div>
                        <div class="text-xs text-gray-300 mt-1">مسئول فعلی: ${this.escapeHtml(ownerName)}</div>
                        <div class="text-xs text-gray-400 mt-1">مسئول قبلی: ${this.escapeHtml(previousOwnerName)}</div>
                    </div>
                `;
            }).join('');

            Swal.fire({
                title: `یافت شد (${matches.length})`,
                html,
                confirmButtonText: 'بستن'
            });
        } catch (e) {
            console.error('Local mobile lookup failed:', e);
            Swal.close();
            Swal.fire('خطا', 'خطا در ارتباط با سرور', 'error');
        }
    },

    openBulkPhoneLookupModal() {
        this.bulkPhoneLookupState = { file: null, results: null };
        document.getElementById('bulk-lookup-step-1').classList.remove('hidden');
        document.getElementById('bulk-lookup-step-2').classList.add('hidden');
        document.getElementById('bulk-lookup-btn-run').classList.add('hidden');
        document.getElementById('bulk-lookup-btn-reset').classList.add('hidden');
        document.getElementById('bulk-lookup-file-selected').classList.add('hidden');
        document.getElementById('bulk-lookup-file-input').value = '';
        this.openModal('bulkPhoneLookupModal');
    },

    handleBulkLookupFileSelect(event) {
        const file = event.target.files?.[0];
        if (!file) return;

        const ext = (file.name.split('.').pop() || '').toLowerCase();
        if (!['xlsx', 'xls', 'csv'].includes(ext)) {
            Swal.fire('خطا', 'فقط فایل‌های Excel یا CSV قابل قبول هستند', 'error');
            return;
        }
        if (file.size > 5 * 1024 * 1024) {
            Swal.fire('خطا', 'حجم فایل نباید بیشتر از 5 مگابایت باشد', 'error');
            return;
        }

        this.bulkPhoneLookupState = this.bulkPhoneLookupState || {};
        this.bulkPhoneLookupState.file = file;
        document.getElementById('bulk-lookup-file-name').textContent = file.name;
        document.getElementById('bulk-lookup-file-selected').classList.remove('hidden');
        document.getElementById('bulk-lookup-btn-run').classList.remove('hidden');
    },

    async runBulkPhoneLookup() {
        const state = this.bulkPhoneLookupState || {};
        if (!state.file) {
            Swal.fire('خطا', 'ابتدا فایل را انتخاب کنید', 'error');
            return;
        }

        Swal.fire({ title: 'در حال استعلام...', allowOutsideClick: false, didOpen: () => Swal.showLoading() });

        try {
            const formData = new FormData();
            formData.append('file', state.file);

            const response = await fetch('?api=bulk_phone_lookup', {
                method: 'POST',
                body: formData,
                credentials: 'same-origin'
            });
            const res = await response.json();
            Swal.close();

            if (res.status !== 'success') {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
                return;
            }

            this.bulkPhoneLookupState.results = res.results || [];
            this._renderBulkLookupResults(res);
        } catch (e) {
            console.error('Bulk phone lookup failed:', e);
            Swal.close();
            Swal.fire('خطا', 'خطا در ارتباط با سرور', 'error');
        }
    },

    _renderBulkLookupResults(res) {
        document.getElementById('bulk-lookup-found-count').textContent = res.found_count ?? 0;
        document.getElementById('bulk-lookup-notfound-count').textContent = res.not_found_count ?? 0;
        document.getElementById('bulk-lookup-error-count').textContent = res.error_count ?? 0;

        const tbody = document.getElementById('bulk-lookup-results-table');
        tbody.innerHTML = '';

        const results = Array.isArray(res.results) ? res.results : [];
        results.forEach(r => {
            if (r.error) {
                const tr = document.createElement('tr');
                tr.className = 'bg-red-900/20';
                tr.innerHTML = `
                    <td class="border border-gray-700 p-2">${r.row}</td>
                    <td class="border border-gray-700 p-2 font-mono">${this.escapeHtml(r.phone)}</td>
                    <td class="border border-gray-700 p-2 text-red-400 text-center" colspan="4">${this.escapeHtml(r.error)}</td>
                `;
                tbody.appendChild(tr);
                return;
            }

            if (!r.found) {
                const tr = document.createElement('tr');
                tr.className = 'bg-gray-800';
                tr.innerHTML = `
                    <td class="border border-gray-700 p-2">${r.row}</td>
                    <td class="border border-gray-700 p-2 font-mono">${this.escapeHtml(r.phone)}</td>
                    <td class="border border-gray-700 p-2 text-gray-400 text-center" colspan="4">موجود نیست</td>
                `;
                tbody.appendChild(tr);
                return;
            }

            const matches = Array.isArray(r.matches) ? r.matches : [];
            matches.forEach((m, idx) => {
                const tr = document.createElement('tr');
                tr.className = 'bg-green-900/20';
                tr.innerHTML = `
                    <td class="border border-gray-700 p-2">${idx === 0 ? r.row : ''}</td>
                    <td class="border border-gray-700 p-2 font-mono">${idx === 0 ? this.escapeHtml(r.phone) : ''}</td>
                    <td class="border border-gray-700 p-2 text-center"><span class="text-green-400 font-bold">موجود</span></td>
                    <td class="border border-gray-700 p-2">${this.escapeHtml(m.display_name || '-')}</td>
                    <td class="border border-gray-700 p-2">${this.escapeHtml(m.owner_name || '-')}</td>
                    <td class="border border-gray-700 p-2 text-gray-400">${this.escapeHtml(m.previous_owner_name || '-')}</td>
                `;
                tbody.appendChild(tr);
            });
        });

        document.getElementById('bulk-lookup-step-1').classList.add('hidden');
        document.getElementById('bulk-lookup-step-2').classList.remove('hidden');
        document.getElementById('bulk-lookup-btn-run').classList.add('hidden');
        document.getElementById('bulk-lookup-btn-reset').classList.remove('hidden');
    },

    resetBulkPhoneLookup() {
        this.bulkPhoneLookupState = { file: null, results: null };
        document.getElementById('bulk-lookup-step-1').classList.remove('hidden');
        document.getElementById('bulk-lookup-step-2').classList.add('hidden');
        document.getElementById('bulk-lookup-btn-run').classList.add('hidden');
        document.getElementById('bulk-lookup-btn-reset').classList.add('hidden');
        document.getElementById('bulk-lookup-file-selected').classList.add('hidden');
        document.getElementById('bulk-lookup-file-input').value = '';
    },

    exportBulkLookupResults(mode) {
        const results = (this.bulkPhoneLookupState || {}).results;
        if (!results || results.length === 0) return;

        let rows = [];
        const header = ['ردیف', 'شماره', 'وضعیت', 'نام', 'مسئول فعلی', 'مسئول قبلی', 'شناسه دیدار'];

        results.forEach(r => {
            if (r.error) {
                if (mode === 'all') {
                    rows.push([r.row, r.phone, 'نامعتبر', '', '', '', '']);
                }
                return;
            }

            if (!r.found) {
                if (mode === 'all' || mode === 'not_found') {
                    rows.push([r.row, r.phone, 'موجود نیست', '', '', '', '']);
                }
                return;
            }

            if (mode === 'all' || mode === 'found') {
                const matches = Array.isArray(r.matches) ? r.matches : [{}];
                matches.forEach(m => {
                    rows.push([
                        r.row,
                        r.phone,
                        'موجود',
                        m.display_name || '',
                        m.owner_name || '',
                        m.previous_owner_name || '',
                        m.didar_contact_id || ''
                    ]);
                });
            }
        });

        if (rows.length === 0) {
            Swal.fire('توجه', 'داده‌ای برای خروجی وجود ندارد', 'info');
            return;
        }

        const csvLines = [header, ...rows].map(row =>
            row.map(cell => {
                const val = String(cell ?? '');
                return val.includes(',') || val.includes('"') || val.includes('\n')
                    ? '"' + val.replace(/"/g, '""') + '"'
                    : val;
            }).join(',')
        );

        const bom = '﻿';
        const blob = new Blob([bom + csvLines.join('\n')], { type: 'text/csv;charset=utf-8;' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'bulk_phone_lookup_' + mode + '.csv';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    },

    async searchDidar(e) {
        if (!this.isDidarUiEnabled()) {
            if (e && typeof e.preventDefault === 'function') e.preventDefault();
            this.showDidarUiDisabledMessage();
            return;
        }
        e.preventDefault();
        const form = e.target;
        const phone = form.phone.value.trim();
        const keywords = form.keywords.value.trim();
        
        if (!phone && !keywords) {
            Swal.fire('خطا', 'لطفاً شماره تلفن یا کلمات کلیدی را وارد کنید', 'error');
            return;
        }
        
        Swal.fire({
            title: 'در حال جستجو...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('search_didar', { phone, keywords });
            Swal.close();
            
            if (res.status === 'success') {
                this.renderDidarSearchResults(res.results || []);
            } else {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
            }
        } catch (error) {
            console.error('Error in searchDidar:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در جستجو: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    renderDidarSearchResults(results) {
        const container = document.getElementById('didar-search-results');
        
        // Store results in state for later use
        this.state.didarSearchResults = results || [];
        
        if (!results || results.length === 0) {
            container.innerHTML = '<p class="text-center text-gray-500 py-8">نتیجه‌ای یافت نشد</p>';
            return;
        }
        
        container.innerHTML = results.map(person => {
            const name = person.DisplayName || (person.FirstName + ' ' + person.LastName) || 'بدون نام';
            const mobile = person.MobilePhone || '-';
            const email = person.Email || '-';
            const contactId = person.Id || '';
            
            return `
                <div class="glass p-4 rounded-lg hover:bg-gray-800/50 cursor-pointer" onclick="app.viewDidarPersonDetails('${contactId}')">
                    <div class="flex justify-between items-start">
                        <div class="flex-1">
                            <div class="font-bold text-white mb-1">${this.escapeHtml(name)}</div>
                            <div class="text-sm text-gray-400">موبایل: ${this.escapeHtml(mobile)}</div>
                            ${email !== '-' ? `<div class="text-sm text-gray-400">ایمیل: ${this.escapeHtml(email)}</div>` : ''}
                        </div>
                        <button class="btn-primary px-3 py-1 rounded text-xs">مشاهده جزئیات</button>
                    </div>
                </div>
            `;
        }).join('');
    },
    
    async viewDidarPersonDetails(contactId) {
        if (!this.isDidarUiEnabled()) {
            this.showDidarUiDisabledMessage();
            return;
        }
        // First, try to get person from search results
        let person = null;
        if (this.state.didarSearchResults && Array.isArray(this.state.didarSearchResults)) {
            person = this.state.didarSearchResults.find(p => p.Id === contactId);
        }
        
        // If found in search results, use it directly and fetch deals/activities
        if (person) {
            Swal.fire({
                title: 'در حال بارگذاری معاملات و فعالیت‌ها...',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });
            
            try {
                const res = await this.req('get_didar_person', { contact_id: contactId, person_from_search: 'true' });
                Swal.close();
                
                console.log('get_didar_person response:', res);
                console.log('Deals count:', res.deals?.length || 0);
                console.log('Activities count:', res.activities?.length || 0);
                
                if (res.status === 'success') {
                    // Use person from search results (more complete) but use deals/activities from API
                    console.log('Rendering with deals:', res.deals?.length || 0, 'activities:', res.activities?.length || 0);
                    this.renderDidarPersonDetails(person, res.deals || [], res.activities || []);
                    this.openModal('modalDidarPerson');
                    // No warning needed since person data is available from search results
                } else {
                    // If API fails, still show person data from search
                    this.renderDidarPersonDetails(person, [], []);
                    this.openModal('modalDidarPerson');
                    Swal.fire('هشدار', 'اطلاعات کامل دریافت نشد، اما اطلاعات پایه نمایش داده می‌شود', 'warning');
                }
            } catch (error) {
                console.error('Error in viewDidarPersonDetails:', error);
                Swal.close();
                // Still show person data from search even if API fails
                this.renderDidarPersonDetails(person, [], []);
                this.openModal('modalDidarPerson');
                Swal.fire('هشدار', 'اطلاعات کامل دریافت نشد، اما اطلاعات پایه نمایش داده می‌شود', 'warning');
            }
        } else {
            // If not in search results, try API (which will search for the person)
            Swal.fire({
                title: 'در حال بارگذاری...',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });
            
            try {
                const res = await this.req('get_didar_person', { contact_id: contactId });
                Swal.close();
                
                console.log('get_didar_person response (no search):', res);
                console.log('Deals count:', res.deals?.length || 0);
                console.log('Activities count:', res.activities?.length || 0);
                
                if (res.status === 'success' && res.person) {
                    console.log('Rendering with deals:', res.deals?.length || 0, 'activities:', res.activities?.length || 0);
                    this.renderDidarPersonDetails(res.person, res.deals || [], res.activities || []);
                    this.openModal('modalDidarPerson');
                } else if (res.status === 'partial') {
                    // Person not found but deals/activities available
                    console.log('Partial response - showing deals/activities anyway');
                    if (res.deals && res.deals.length > 0 || res.activities && res.activities.length > 0) {
                        this.renderDidarPersonDetails(null, res.deals || [], res.activities || []);
                        this.openModal('modalDidarPerson');
                    } else {
                        Swal.fire('خطا', 'اطلاعات شخص یافت نشد. لطفاً دوباره جستجو کنید.', 'error');
                    }
                } else {
                    Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
                }
            } catch (error) {
                console.error('Error in viewDidarPersonDetails:', error);
                Swal.close();
                Swal.fire('خطا', 'خطا در دریافت اطلاعات: ' + (error.message || 'خطای نامشخص'), 'error');
            }
        }
    },
    
    renderDidarPersonDetails(person, deals, activities) {
        console.log('renderDidarPersonDetails called with:', {
            person: person ? 'exists' : 'null',
            dealsCount: deals?.length || 0,
            activitiesCount: activities?.length || 0
        });
        
        const container = document.getElementById('didar-person-details');
        const nameEl = document.getElementById('didar-person-name');
        const customerCodeEl = document.getElementById('didar-person-customer-code');
        
        if (!container) {
            console.error('didar-person-details container not found!');
            return;
        }
        
        if (!person) {
            console.warn('Person is null, using placeholder');
        }
        
        const name = person ? (person.DisplayName || (person.FirstName + ' ' + person.LastName) || 'بدون نام') : 'اطلاعات شخص در دسترس نیست';
        if (nameEl) nameEl.textContent = name;
        if (customerCodeEl) customerCodeEl.textContent = String(person?.code || person?.Code || person?.CustomerCode || '-');
        
        // Store activities in state for filtering
        this.state.didarActivities = activities || [];
        this.state.didarActivitiesFiltered = activities || [];
        
        console.log('About to render HTML with', deals?.length || 0, 'deals and', activities?.length || 0, 'activities');
        
        container.innerHTML = `
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
                <div class="glass p-4">
                    <div class="text-xs text-gray-400 mb-1">نام</div>
                    <div class="text-white font-bold">${this.escapeHtml(name)}</div>
                </div>
                <div class="glass p-4">
                    <div class="text-xs text-gray-400 mb-1">موبایل</div>
                    <div class="text-white font-bold">${this.escapeHtml(person?.MobilePhone || '-')}</div>
                </div>
                <div class="glass p-4">
                    <div class="text-xs text-gray-400 mb-1">ایمیل</div>
                    <div class="text-white font-bold">${this.escapeHtml(person?.Email || '-')}</div>
                </div>
                <div class="glass p-4">
                    <div class="text-xs text-gray-400 mb-1">شهر</div>
                    <div class="text-white font-bold">${this.escapeHtml(person?.City || '-')}</div>
                </div>
                <div class="glass p-4">
                    <div class="text-xs text-gray-400 mb-1">شغل</div>
                    <div class="text-white font-bold">${this.escapeHtml(person?.JobTitle || '-')}</div>
                </div>
                <div class="glass p-4">
                    <div class="text-xs text-gray-400 mb-1">مسئول</div>
                    <div class="text-white font-bold">${this.escapeHtml(person?.Owner?.DisplayName || '-')}</div>
                </div>
            </div>
            
            <div class="mb-6">
                <h4 class="font-bold text-white mb-3">معاملات (${deals?.length || 0})</h4>
                <div class="space-y-2">
                    ${deals && deals.length > 0 ? deals.map(deal => {
                        const dealId = deal.Id || deal.id || deal.Code || '';
                        const status = deal.Status || 'Pending';
                        const isClosed = status === 'Won' || status === 'Lost';
                        const reactivateBtn = this.isAdmin() && isClosed ? `
                            <button onclick="event.stopPropagation(); app.reactivateDealFromProfile('${dealId}')" class="text-blue-500 hover:text-blue-700 text-xs px-2 py-1 rounded transition ml-2" title="فعال‌سازی مجدد">
                                <i class="fa-solid fa-undo"></i>
                            </button>` : '';
                        return `
                            <div class="glass p-3 rounded relative">
                                ${reactivateBtn ? `<div class="absolute top-2 left-2">${reactivateBtn}</div>` : ''}
                                <div class="font-bold text-white ${reactivateBtn ? 'mr-8' : ''}">${this.escapeHtml(deal.Title || 'بدون عنوان')}</div>
                                <div class="text-sm text-gray-400">کد: ${deal.Code || '-'} | وضعیت: ${status === 'Won' ? 'موفق' : (status === 'Lost' ? 'ناموفق' : 'جاری')}</div>
                            </div>
                        `;
                    }).join('') : '<p class="text-gray-500 text-sm">معامله‌ای ثبت نشده</p>'}
                </div>
            </div>
            
            <div>
                <div class="flex justify-between items-center mb-3">
                    <h4 class="font-bold text-white">تاریخچه فعالیت‌ها و یادداشت‌ها (${activities?.length || 0})</h4>
                    <div class="flex gap-2">
                        <input type="text" id="activity-search-input" placeholder="جستجو در فعالیت‌ها..." 
                            class="input text-sm w-48" onkeyup="app.filterActivities(this.value)">
                        <select id="activity-filter-type" class="input text-sm w-32" onchange="app.filterActivities()">
                            <option value="">همه انواع</option>
                        </select>
                        <select id="activity-filter-status" class="input text-sm w-32" onchange="app.filterActivities()">
                            <option value="">همه وضعیت‌ها</option>
                            <option value="done">انجام شده</option>
                            <option value="pending">در انتظار</option>
                        </select>
                    </div>
                </div>
                <div id="activities-list" class="space-y-3 max-h-96 overflow-y-auto">
                    ${this.renderActivitiesList(activities)}
                </div>
            </div>
        `;
        
        // Store activities in state for filtering
        this.state.didarActivities = activities || [];
        this.state.didarActivitiesFiltered = activities || [];
        
        // Load activity types for filter dropdown
        this.loadActivityTypesForDidarFilter();
    },
    
    async viewDidarDeals(contactId) {
        if (!this.isDidarUiEnabled()) {
            this.showDidarUiDisabledMessage();
            return;
        }
        if (!contactId) {
            Swal.fire('خطا', 'شناسه شخص مشخص نشده است', 'error');
            return;
        }
        
        console.log('viewDidarDeals called with contactId:', contactId, 'type:', typeof contactId);
        
        Swal.fire({
            title: 'در حال بارگذاری معاملات...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('get_legacy_deals', { contact_id: contactId });
            Swal.close();
            
            console.log('get_legacy_deals response:', res);
            
            if (res.status === 'success') {
                // Store deals in state for later use (to avoid API inconsistency)
                this.state.didarDeals = res.deals || [];
                this.renderDidarDeals(this.state.didarDeals);
                this.openModal('modalDidarDeals');
            } else {
                Swal.fire('خطا', res.message || 'خطا در دریافت معاملات', 'error');
            }
        } catch (error) {
            console.error('Error in viewDidarDeals:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در دریافت معاملات: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    renderDidarDeals(deals) {
        const container = document.getElementById('didar-deals-list');

        if (!container) {
            console.error('didar-deals-list container not found!');
            return;
        }

        if (!deals || deals.length === 0) {
            container.innerHTML = '<p class="text-center text-gray-500 py-8">معامله‌ای برای نمایش وجود ندارد</p>';
            return;
        }

        // Sort by register time (newest first)
        const sorted = [...deals].sort((a, b) => {
            const dateA = new Date(a.RegisterTime || a.register_time || a.register_date || 0);
            const dateB = new Date(b.RegisterTime || b.register_time || b.register_date || 0);
            return dateB - dateA;
        });

        container.innerHTML = sorted.map(deal => {
            const status = deal.Status || deal.status || 'Pending';
            const statusText = status === 'Won' ? 'موفق' : (status === 'Lost' ? 'ناموفق' : 'در جریان');
            const statusColor = status === 'Won' ? 'border-green-500 bg-green-500/10' :
                              (status === 'Lost' ? 'border-red-500 bg-red-500/10' : 'border-blue-500 bg-blue-500/10');
            const priceValue = deal.Price || deal.estimated_price || deal.final_price || 0;
            const price = priceValue ? parseInt(priceValue).toLocaleString() : '0';
            const registerDateValue = deal.RegisterTime || deal.register_time || deal.register_date;
            const registerDate = registerDateValue ? toPersianDateTime(registerDateValue) : '';

            const dealId = deal.Id || deal.didar_deal_id || deal.id || '';
            const title = deal.Title || deal.title || 'عنوان معامله';
            const code = deal.Code || deal.code || deal.didar_deal_id || '-';
            const description = deal.Description || deal.description || '';

            if (!dealId) {
                return `
                <div class="glass p-4 rounded-lg border-l-4 ${statusColor}">
                    <div class="flex justify-between items-start">
                        <div class="flex-1">
                            <div class="font-bold text-white mb-1">${this.escapeHtml(title)}</div>
                            <div class="text-sm text-gray-400">
                                <span>کد: ${this.escapeHtml(code)}</span>
                                <span class="mx-2">|</span>
                                <span class="px-2 py-0.5 rounded ${status === 'Won' ? 'bg-green-500/20 text-green-400' : (status === 'Lost' ? 'bg-red-500/20 text-red-400' : 'bg-blue-500/20 text-blue-400')}">${statusText}</span>
                                ${registerDate ? `<span class=\"mx-2\">|</span><span>${registerDate}</span>` : ''}
                            </div>
                            ${description ? `<div class=\"text-sm text-gray-300 mt-2\">${this.escapeHtml(description)}</div>` : ''}
                        </div>
                        <div class="text-left ml-4">
                            <div class="text-lg font-bold text-yellow-500">${price}</div>
                            <div class="text-xs text-gray-500">ارزش معامله</div>
                        </div>
                    </div>
                </div>
            `;
            }

            return `
                <div class="glass p-4 rounded-lg border-l-4 ${statusColor} cursor-pointer hover:bg-gray-800/50 transition" onclick="app.openDealModal('${this.escapeHtml(dealId)}')">
                    <div class="flex justify-between items-start">
                        <div class="flex-1">
                            <div class="font-bold text-white mb-1">${this.escapeHtml(title)}</div>
                            <div class="text-sm text-gray-400">
                                <span>کد: ${this.escapeHtml(code)}</span>
                                <span class="mx-2">|</span>
                                <span class="px-2 py-0.5 rounded ${status === 'Won' ? 'bg-green-500/20 text-green-400' : (status === 'Lost' ? 'bg-red-500/20 text-red-400' : 'bg-blue-500/20 text-blue-400')}">${statusText}</span>
                                ${registerDate ? `<span class=\"mx-2\">|</span><span>${registerDate}</span>` : ''}
                            </div>
                            ${description ? `<div class=\"text-sm text-gray-300 mt-2\">${this.escapeHtml(description)}</div>` : ''}
                        </div>
                        <div class="text-left ml-4">
                            <div class="text-lg font-bold text-yellow-500">${price}</div>
                            <div class="text-xs text-gray-500">ارزش معامله</div>
                        </div>
                    </div>
                </div>
            `;
        }).join('');
    },
    
    async viewDealDetails(dealId) {
        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }
        
        // First, try to find deal in cached state (from deals list)
        let deal = null;
        if (this.state.didarDeals && this.state.didarDeals.length > 0) {
            deal = this.state.didarDeals.find(d => (d.Id || d.id) === dealId);
            if (deal) {
                console.log('Found deal in cache, using cached data:', {
                    Id: deal.Id,
                    Code: deal.Code,
                    Title: deal.Title
                });
            }
        }
        
        // If deal found in cache, use it directly and only fetch activities
        if (deal) {
            Swal.fire({
                title: 'در حال بارگذاری فعالیت‌ها...',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });
            
            try {
                // Only fetch activities for this deal
                const activitiesRes = await this.req('get_deal_activities', { deal_id: dealId });
                Swal.close();
                
                if (activitiesRes.status === 'success') {
                    this.renderDealDetails(deal, activitiesRes.activities || []);
                    this.openModal('modalDealDetails');
                } else {
                    // Even if activities fail, show deal details without activities
                    this.renderDealDetails(deal, []);
                    this.openModal('modalDealDetails');
                }
            } catch (error) {
                console.error('Error fetching activities:', error);
                Swal.close();
                // Show deal details even if activities fetch fails
                this.renderDealDetails(deal, []);
                this.openModal('modalDealDetails');
            }
        } else {
            // Deal not in cache, fetch full details from API
            Swal.fire({
                title: 'در حال بارگذاری جزئیات معامله...',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });
            
            try {
                const res = await this.req('get_deal_details', { deal_id: dealId });
                Swal.close();
                
                if (res.status === 'success') {
                    this.renderDealDetails(res.deal || {}, res.activities || []);
                    this.openModal('modalDealDetails');
                } else {
                    Swal.fire('خطا', res.message || 'خطا در دریافت جزئیات معامله', 'error');
                }
            } catch (error) {
                console.error('Error in viewDealDetails:', error);
                Swal.close();
                Swal.fire('خطا', 'خطا در دریافت جزئیات معامله: ' + (error.message || 'خطای نامشخص'), 'error');
            }
        }
    },
    
    renderDealDetails(deal, activities) {
        // Update title
        const titleEl = document.getElementById('deal-details-title');
        if (titleEl) {
            titleEl.textContent = `جزئیات معامله: ${this.escapeHtml(deal.Title || 'بدون عنوان')}`;
        }
        
        // Render deal info
        const infoContainer = document.getElementById('deal-details-content');
        if (!infoContainer) {
            console.error('deal-details-content container not found!');
            return;
        }
        
        const status = deal.Status || 'Pending';
        const statusText = status === 'Won' ? 'موفق' : (status === 'Lost' ? 'ناموفق' : 'جاری');
        const statusColor = status === 'Won' ? 'text-green-400' : (status === 'Lost' ? 'text-red-400' : 'text-blue-400');
        const price = deal.Price ? parseInt(deal.Price).toLocaleString() : '0';
        const finalPrice = deal.FinalAdjustmentPrice ? parseInt(deal.FinalAdjustmentPrice).toLocaleString() : price;
        const registerDate = deal.RegisterTime ? toPersianDateTime(deal.RegisterTime) : '';
        const wonDate = deal.ChangeToWonTime ? toPersianDateTime(deal.ChangeToWonTime) : '';
        const lostDate = deal.ChangeToLossTime ? toPersianDateTime(deal.ChangeToLossTime) : '';
        const lastUpdate = deal.LastUpdateTime ? toPersianDateTime(deal.LastUpdateTime) : '';
        
        const contact = deal.Contact || deal.Person || {};
        const owner = deal.Owner || {};
        const items = deal.Items || [];
        const customerCode = person?.customer_code || person?.CustomerCode || person?.code || person?.Code || contact?.customer_code || contact?.CustomerCode || deal.CustomerCode || '-';
        
        infoContainer.innerHTML = `
            <!-- Basic Info -->
            <div class="glass p-6 rounded-lg">
                <h4 class="font-bold text-white text-lg mb-4">اطلاعات پایه</h4>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div>
                        <div class="text-xs text-gray-400 mb-1">عنوان</div>
                        <div class="text-white font-bold">${this.escapeHtml(deal.Title || 'بدون عنوان')}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">کد معامله</div>
                        <div class="text-white">${deal.Code || '-'}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">وضعیت</div>
                        <div class="${statusColor} font-bold">${statusText}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">مبلغ</div>
                        <div class="text-yellow-500 font-bold">${price} تومان</div>
                    </div>
                    ${finalPrice !== price ? `
                    <div>
                        <div class="text-xs text-gray-400 mb-1">مبلغ نهایی (پس از تعدیل)</div>
                        <div class="text-yellow-500 font-bold">${finalPrice} تومان</div>
                    </div>
                    ` : ''}
                    <div>
                        <div class="text-xs text-gray-400 mb-1">احتمال موفقیت</div>
                        <div class="text-white">${deal.Probability || 0}%</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">تاریخ ثبت</div>
                        <div class="text-white">${registerDate || '-'}</div>
                    </div>
                    ${wonDate ? `
                    <div>
                        <div class="text-xs text-gray-400 mb-1">تاریخ موفقیت</div>
                        <div class="text-green-400">${wonDate}</div>
                    </div>
                    ` : ''}
                    ${lostDate ? `
                    <div>
                        <div class="text-xs text-gray-400 mb-1">تاریخ ناموفق</div>
                        <div class="text-red-400">${lostDate}</div>
                    </div>
                    ` : ''}
                    ${lastUpdate ? `
                    <div>
                        <div class="text-xs text-gray-400 mb-1">آخرین به‌روزرسانی</div>
                        <div class="text-white">${lastUpdate}</div>
                    </div>
                    ` : ''}
                </div>
                ${deal.Description ? `
                <div class="mt-4">
                    <div class="text-xs text-gray-400 mb-1">توضیحات</div>
                    <div class="text-white text-sm whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(deal.Description))}</div>
                </div>
                ` : ''}
            </div>
            
            <!-- Contact Info -->
            <div class="glass p-6 rounded-lg">
                <h4 class="font-bold text-white text-lg mb-4">اطلاعات تماس</h4>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div>
                        <div class="text-xs text-gray-400 mb-1">نام</div>
                        <div class="text-white">${this.escapeHtml(contact.DisplayName || '-')}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">کد تماس</div>
                        <div class="text-white">${contact.Code || '-'}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">کد مشتری</div>
                        <div class="text-white font-bold">${this.escapeHtml(customerCode)}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">موبایل</div>
                        <div class="text-white">${contact.MobilePhone || '-'}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">ایمیل</div>
                        <div class="text-white">${contact.Email || '-'}</div>
                    </div>
                </div>
            </div>
            
            <!-- Owner Info -->
            <div class="glass p-6 rounded-lg">
                <h4 class="font-bold text-white text-lg mb-4">مسئول معامله</h4>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                    <div>
                        <div class="text-xs text-gray-400 mb-1">نام</div>
                        <div class="text-white">${this.escapeHtml(owner.DisplayName || '-')}</div>
                    </div>
                    <div>
                        <div class="text-xs text-gray-400 mb-1">ایمیل</div>
                        <div class="text-white">${owner.Email || '-'}</div>
                    </div>
                </div>
            </div>
            
            <!-- Deal Items -->
            ${items.length > 0 ? `
            <div class="glass p-6 rounded-lg">
                <h4 class="font-bold text-white text-lg mb-4">آیتم‌های معامله</h4>
                <div class="space-y-3">
                    ${items.map(item => `
                        <div class="bg-gray-800/50 p-4 rounded-lg">
                            <div class="flex justify-between items-start">
                                <div class="flex-1">
                                    <div class="font-bold text-white mb-1">${this.escapeHtml(item.Title || item.TitleForInvoice || 'بدون عنوان')}</div>
                                    <div class="text-sm text-gray-400">
                                        <span>تعداد: ${item.Quantity || 1}</span>
                                        ${item.Unit ? `<span class="mx-2">|</span><span>واحد: ${item.Unit}</span>` : ''}
                                    </div>
                                    ${item.Description ? `<div class="text-sm text-gray-300 mt-2">${this.escapeHtml(this.stripHtml(item.Description))}</div>` : ''}
                                </div>
                                <div class="text-left ml-4">
                                    <div class="text-yellow-500 font-bold">${item.PriceAfterDiscount ? parseInt(item.PriceAfterDiscount).toLocaleString() : (item.Price ? parseInt(item.Price).toLocaleString() : '0')} تومان</div>
                                    ${item.Discount && item.Discount > 0 ? `
                                    <div class="text-xs text-gray-400 line-through">${parseInt(item.Price || 0).toLocaleString()} تومان</div>
                                    <div class="text-xs text-green-400">تخفیف: ${item.DiscountPercent ? item.DiscountPercent + '%' : parseInt(item.Discount).toLocaleString() + ' تومان'}</div>
                                    ` : ''}
                                </div>
                            </div>
                        </div>
                    `).join('')}
                </div>
            </div>
            ` : ''}
        `;
        
        // Render activities
        const activitiesContainer = document.getElementById('deal-activities-content');
        if (activitiesContainer) {
            if (activities && activities.length > 0) {
                this.renderDidarActivities(activities, activitiesContainer);
            } else {
                activitiesContainer.innerHTML = '<p class="text-center text-gray-500 py-8">فعالیتی برای این معامله ثبت نشده است</p>';
            }
        }
    },
    
    showDealDetailsTab(tab) {
        // Hide all tabs
        document.querySelectorAll('.deal-details-tab').forEach(el => el.classList.add('hidden'));
        
        // Remove active class from all tab buttons
        document.querySelectorAll('[id^="deal-tab-"]').forEach(el => {
            el.classList.remove('text-white', 'border-blue-500');
            el.classList.add('text-gray-400', 'border-transparent');
        });
        
        // Show selected tab
        const tabContent = document.getElementById(`deal-details-${tab}`);
        const tabButton = document.getElementById(`deal-tab-${tab}`);
        
        if (tabContent) {
            tabContent.classList.remove('hidden');
        }
        
        if (tabButton) {
            tabButton.classList.remove('text-gray-400', 'border-transparent');
            tabButton.classList.add('text-white', 'border-blue-500');
        }
    },
    
    async viewDidarActivities(contactId) {
        if (!this.isDidarUiEnabled()) {
            this.showDidarUiDisabledMessage();
            return;
        }
        if (!contactId) {
            Swal.fire('خطا', 'شناسه شخص مشخص نشده است', 'error');
            return;
        }
        
        console.log('viewDidarActivities called with contactId:', contactId, 'type:', typeof contactId);
        
        Swal.fire({
            title: 'در حال بارگذاری فعالیت‌ها...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('get_lead_didar_data', { contact_id: contactId });
            Swal.close();
            
            console.log('get_lead_didar_data response:', res);
            
            if (res.status === 'success') {
                this.renderDidarActivities(res.activities || []);
                this.openModal('modalDidarActivities');
            } else {
                Swal.fire('خطا', res.message || 'خطا در دریافت فعالیت‌ها', 'error');
            }
        } catch (error) {
            console.error('Error in viewDidarActivities:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در دریافت فعالیت‌ها: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    renderDidarActivities(activities, container = null) {
        // If container not provided, use default
        if (!container) {
            container = document.getElementById('didar-activities-list');
        }
        
        if (!container) {
            console.error('Activities container not found!');
            return;
        }
        
        if (!activities || activities.length === 0) {
            container.innerHTML = '<p class="text-center text-gray-500 py-8">فعالیتی یافت نشد</p>';
            return;
        }
        
        const resolveUserName = (didarId) => {
            const users = this.state.didarUsers || [];
            if (!didarId || !users.length) return '';
            const user = users.find(u =>
                u.owner_id === didarId ||
                u.didar_user_id === didarId ||
                u.id === didarId
            );
            return user?.display_name || '';
        };

        // Sort by RegisterDate (newest first)
        const sorted = [...activities].sort((a, b) => {
            const dateA = a.RegisterDate || a.register_date ? new Date(a.RegisterDate || a.register_date) : new Date(0);
            const dateB = b.RegisterDate || b.register_date ? new Date(b.RegisterDate || b.register_date) : new Date(0);
            return dateB - dateA;
        });

        container.innerHTML = sorted.map(act => {
            const isDone = act.IsDone === true || act.IsDone === 1 || act.is_done === 1 || act.is_done === true;
            const isNote = (act.ActivityTypeId || act.activity_type_id) === '00000000-0000-0000-0000-000000000000';
            const activityType = act.ActivityType?.Title || act.ActivityTypeTitle || act.activity_type_title || (isNote ? 'یادداشت' : 'فعالیت');

            let raw = act.raw_json;
            if (typeof raw === 'string') {
                try {
                    raw = JSON.parse(raw);
                } catch (_) {
                    raw = null;
                }
            }

            const creator = act.Creator?.DisplayName
                || act.CreatorName
                || act.creator_name
                || raw?.creator_name
                || resolveUserName(act.creator_didar_id)
                || resolveUserName(act.owner_didar_id)
                || 'نامشخص';

            const owner = act.Owner?.DisplayName
                || act.OwnerName
                || act.owner_name
                || raw?.owner_name
                || resolveUserName(act.owner_didar_id)
                || 'نامشخص';

            const note = act.Note || act.note || '';
            const resultNote = act.ResultNote || act.result_note || '';
            const registerDateRaw = act.RegisterDate || act.register_date || act.registerDate || '';
            const doneDateRaw = act.DoneDate || act.done_date || act.doneDate || '';
            const dueDateRaw = act.DueDate || act.due_date || act.dueDate || '';
            const registerDate = registerDateRaw ? toPersianDateTime(registerDateRaw) : '';
            const doneDate = doneDateRaw ? toPersianDateTime(doneDateRaw) : '';
            const dueDate = dueDateRaw ? toPersianDateTime(dueDateRaw) : '';
            const isScheduled = act.is_scheduled === 1 || act.is_scheduled === true || (!isDone && dueDate);
            const registerDateCompare = act.register_date || act.RegisterDate || '';
            const isEdited = act.last_update_time && act.last_update_time !== registerDateCompare;
            
            // Determine status and date to show
            let statusBadge = '';
            let dateInfo = '';
            
            if (isDone) {
                statusBadge = '<span class="text-xs bg-green-500/20 text-green-300 px-2 py-0.5 rounded">✓ انجام شده</span>';
                if (doneDate) {
                    dateInfo = `<div class="text-xs text-green-400 mt-1"><i class="fa-solid fa-check-circle ml-1"></i> تاریخ انجام: ${doneDate}</div>`;
                }
            } else if (isScheduled) {
                statusBadge = '<span class="text-xs bg-yellow-500/20 text-yellow-300 px-2 py-0.5 rounded">⏰ برنامه‌ریزی شده</span>';
                if (dueDate) {
                    dateInfo = `<div class="text-xs text-yellow-400 mt-1"><i class="fa-solid fa-clock ml-1"></i> موعد: ${dueDate}</div>`;
                }
            } else {
                statusBadge = '<span class="text-xs bg-blue-500/20 text-blue-300 px-2 py-0.5 rounded">📝 ثبت شده</span>';
            }
            
            const activityId = act.Id || act.didar_activity_id || act.id;
            const deleteBtn = this.isAdmin() ? `
                <button onclick="event.stopPropagation(); app.deleteActivity('${activityId}')" 
                        class="text-red-500 hover:text-red-700 text-xs px-2 py-1 rounded transition" 
                        title="حذف فعالیت">
                    <i class="fa-solid fa-trash"></i>
                </button>` : '';
            
            const editBtn = !isDone && isScheduled ? `
                <button onclick="event.stopPropagation(); app.openEditScheduledActivityModal('${activityId}')" 
                        class="text-blue-500 hover:text-blue-700 text-xs px-2 py-1 rounded transition" 
                        title="ویرایش فعالیت">
                    <i class="fa-solid fa-edit"></i>
                </button>` : '';
            
            return `
                <div class="glass p-4 rounded-lg border-l-4 ${isDone ? 'border-green-500' : isScheduled ? 'border-yellow-500' : 'border-blue-500'} relative cursor-default">
                    ${deleteBtn || editBtn ? `<div class="absolute top-2 left-2 z-10 flex gap-1">${editBtn}${deleteBtn}</div>` : ''}
                    ${isEdited ? `<div class="absolute top-2 right-2 z-10"><span class="text-xs bg-purple-500/20 text-purple-300 px-2 py-0.5 rounded" title="ویرایش شده"><i class="fa-solid fa-pencil ml-1"></i> ویرایش شده</span></div>` : ''}
                    <div class="flex justify-between items-start mb-2">
                        <div class="flex-1">
                            <div class="flex items-center gap-2 mb-1">
                                <span class="text-sm font-bold ${isNote ? 'text-purple-300' : 'text-blue-300'}">
                                    <i class="fa-solid fa-${isNote ? 'sticky-note' : 'calendar-check'} ml-1"></i>
                                    ${this.escapeHtml(activityType)}
                                </span>
                                ${statusBadge}
                            </div>
                            <div class="text-xs text-gray-400 mb-2 space-y-1">
                                ${registerDate ? `<div><i class="fa-solid fa-calendar-plus ml-1"></i> تاریخ ثبت: ${registerDate}</div>` : ''}
                                ${dueDate ? `<div><i class="fa-solid fa-calendar-clock ml-1"></i> تاریخ/ساعت برنامه‌ریزی: ${dueDate}</div>` : ''}
                                ${isScheduled && !dueDate ? `<div><i class="fa-solid fa-clock ml-1"></i> فعالیت برنامه‌ریزی شده (تاریخ مشخص نشده)</div>` : ''}
                                ${doneDate && isDone ? `<div><i class="fa-solid fa-check-double ml-1"></i> تاریخ انجام: ${doneDate}</div>` : ''}
                            </div>
                            <div class="text-xs text-gray-500 mb-2">
                                <span>ایجاد کننده: ${this.escapeHtml(creator)}</span>
                                ${owner !== creator ? `<span class="mx-2">|</span><span>مسئول: ${this.escapeHtml(owner)}</span>` : ''}
                            </div>
                            ${dateInfo}
                        </div>
                    </div>
                    
                    ${note ? `
                        <div class="mb-2">
                            <div class="text-xs text-gray-400 mb-1"><i class="fa-solid fa-note-sticky ml-1"></i> یادداشت:</div>
                            <div class="text-sm text-gray-300 bg-gray-800/50 p-2 rounded whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(note))}</div>
                        </div>
                    ` : ''}
                    
                    ${resultNote ? `
                        <div class="mb-2">
                            <div class="text-xs text-gray-400 mb-1"><i class="fa-solid fa-clipboard-check ml-1"></i> نتیجه:</div>
                            <div class="text-sm text-gray-300 bg-gray-800/50 p-2 rounded whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(resultNote))}</div>
                        </div>
                    ` : ''}
                    
                    ${!note && !resultNote ? `
                        <div class="text-xs text-gray-500 italic">بدون توضیحات</div>
                    ` : ''}
                </div>
            `;
        }).join('');
    },
    
    renderActivitiesList(activities) {
        if (!activities || activities.length === 0) {
            return '<p class="text-gray-500 text-sm text-center py-4">فعالیتی ثبت نشده است</p>';
        }
        
        const resolveUserName = (didarId) => {
            const users = this.state.didarUsers || [];
            if (!didarId || !users.length) return '';
            const user = users.find(u =>
                u.owner_id === didarId ||
                u.didar_user_id === didarId ||
                u.id === didarId
            );
            return user?.display_name || '';
        };

        // Sort by RegisterDate (newest first)
        const sorted = [...activities].sort((a, b) => {
            const dateA = a.RegisterDate || a.register_date ? new Date(a.RegisterDate || a.register_date) : new Date(0);
            const dateB = b.RegisterDate || b.register_date ? new Date(b.RegisterDate || b.register_date) : new Date(0);
            return dateB - dateA;
        });

        return sorted.map(act => {
            const isDone = act.IsDone === true || act.IsDone === 1 || act.is_done === 1 || act.is_done === true;
            const isNote = (act.ActivityTypeId || act.activity_type_id) === '00000000-0000-0000-0000-000000000000';
            const activityType = act.ActivityType?.Title || act.ActivityTypeTitle || act.activity_type_title || (isNote ? 'یادداشت' : 'فعالیت');

            let raw = act.raw_json;
            if (typeof raw === 'string') {
                try {
                    raw = JSON.parse(raw);
                } catch (_) {
                    raw = null;
                }
            }

            const creator = act.Creator?.DisplayName
                || act.CreatorName
                || act.creator_name
                || raw?.creator_name
                || resolveUserName(act.creator_didar_id)
                || resolveUserName(act.owner_didar_id)
                || 'نامشخص';

            const owner = act.Owner?.DisplayName
                || act.OwnerName
                || act.owner_name
                || raw?.owner_name
                || resolveUserName(act.owner_didar_id)
                || 'نامشخص';

            const note = act.Note || act.note || '';
            const resultNote = act.ResultNote || act.result_note || '';
            const registerDateRaw = act.RegisterDate || act.register_date || act.registerDate || '';
            const doneDateRaw = act.DoneDate || act.done_date || act.doneDate || '';
            const registerDate = registerDateRaw ? toPersianDateTime(registerDateRaw) : '';
            const doneDate = doneDateRaw ? toPersianDateTime(doneDateRaw) : '';
            
            // Check for due date - try multiple possible field names
            let dueDate = '';
            if (act.DueDate || act.due_date) {
                try {
                    dueDate = toPersianDateTime(act.DueDate || act.due_date);
                } catch (e) {
                    dueDate = act.DueDate || act.due_date; // Fallback to raw value
                }
            }

            
            // Check if activity is scheduled
            const hasDueDate = dueDate && dueDate.trim() !== '';
            const isScheduledFlag = act.is_scheduled === 1 || act.is_scheduled === true || act.is_scheduled === '1';
            const isScheduled = isScheduledFlag || (!isDone && hasDueDate);
            
            // Determine status badge
            let statusBadge = '';
            if (isDone) {
                statusBadge = '<span class="text-xs bg-green-500/20 text-green-300 px-2 py-0.5 rounded">✓ انجام شده</span>';
            } else if (isScheduled) {
                statusBadge = '<span class="text-xs bg-yellow-500/20 text-yellow-300 px-2 py-0.5 rounded">⏰ برنامه‌ریزی شده</span>';
            } else {
                statusBadge = '<span class="text-xs bg-blue-500/20 text-blue-300 px-2 py-0.5 rounded">📝 ثبت شده</span>';
            }
            
            const activityId = act.Id || act.didar_activity_id || act.id;
            
            return `
                <div class="glass p-4 rounded-lg border-r-4 ${isDone ? 'border-green-500' : isScheduled ? 'border-yellow-500' : 'border-blue-500'} cursor-default">
                    <div class="flex justify-between items-start mb-2">
                        <div class="flex-1">
                            <div class="flex items-center gap-2 mb-1">
                                <span class="text-xs font-bold ${isNote ? 'text-purple-300' : 'text-blue-300'}">
                                    <i class="fa-solid fa-${isNote ? 'sticky-note' : (isDone ? 'check-circle' : isScheduled ? 'clock' : 'calendar-check')} ml-1"></i>
                                    ${this.escapeHtml(activityType)}
                                </span>
                                ${statusBadge}
                            </div>
                            <div class="font-bold text-white text-sm mb-1">${this.escapeHtml(act.Title || 'بدون عنوان')}</div>
                        </div>
                        <div class="text-left text-xs text-gray-400">
                            ${registerDate ? `<div><i class="fa-solid fa-calendar-plus ml-1"></i> ثبت: ${registerDate}</div>` : ''}
                            ${doneDate ? `<div class="text-green-400 mt-1"><i class="fa-solid fa-check-circle ml-1"></i> انجام: ${doneDate}</div>` : ''}
                            ${dueDate && isScheduled ? `<div class="text-yellow-400 mt-1"><i class="fa-solid fa-calendar-clock ml-1"></i> موعد: ${dueDate}</div>` : ''}
                            ${isScheduled && !dueDate ? `<div class="text-yellow-400 mt-1"><i class="fa-solid fa-clock ml-1"></i> برنامه‌ریزی شده (تاریخ مشخص نشده)</div>` : ''}
                        </div>
                    </div>
                    
                    ${note ? `
                        <div class="mb-2">
                            <div class="text-xs text-gray-400 mb-1">یادداشت:</div>
                            <div class="text-sm text-gray-300 bg-gray-800/50 p-2 rounded whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(note))}</div>
                        </div>
                    ` : ''}
                    
                    ${resultNote ? `
                        <div class="mb-2">
                            <div class="text-xs text-gray-400 mb-1">نتیجه:</div>
                            <div class="text-sm text-gray-300 bg-gray-800/50 p-2 rounded whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(resultNote))}</div>
                        </div>
                    ` : ''}
                    
                    <div class="flex justify-between items-center mt-2 pt-2 border-t border-gray-700">
                        <div class="text-xs text-gray-500">
                            <span>ثبت‌کننده: <span class="text-gray-400">${this.escapeHtml(creator)}</span></span>
                            ${owner !== creator ? `<span class="mr-3">مسئول: <span class="text-gray-400">${this.escapeHtml(owner)}</span></span>` : ''}
                        </div>
                        ${act.Duration ? `<div class="text-xs text-gray-500">مدت: ${act.Duration} دقیقه</div>` : ''}
                    </div>
                </div>
            `;
        }).join('');
    },
    
    filterActivities(searchText = null, filterType = null, filterStatus = null) {
        if (!this.state.didarActivities) return;
        
        const search = searchText !== null ? searchText : (document.getElementById('activity-search-input')?.value || '');
        const type = filterType !== null ? filterType : (document.getElementById('activity-filter-type')?.value || '');
        const status = filterStatus !== null ? filterStatus : (document.getElementById('activity-filter-status')?.value || '');
        
        let filtered = [...this.state.didarActivities];
        
        // Filter by search text
        if (search) {
            const searchLower = search.toLowerCase();
            filtered = filtered.filter(act => {
                const title = (act.Title || '').toLowerCase();
                const note = (act.Note || '').toLowerCase();
                const resultNote = (act.ResultNote || '').toLowerCase();
                const activityType = (act.ActivityType?.Title || act.ActivityTypeTitle || '').toLowerCase();
                const creator = (act.Creator?.DisplayName || act.CreatorName || '').toLowerCase();
                return title.includes(searchLower) || 
                       note.includes(searchLower) || 
                       resultNote.includes(searchLower) ||
                       activityType.includes(searchLower) ||
                       creator.includes(searchLower);
            });
        }
        
        // Filter by activity type
        if (type) {
            filtered = filtered.filter(act => {
                const actTypeId = act.ActivityTypeId || '';
                return actTypeId === type;
            });
        }
        
        // Filter by status
        if (status) {
            if (status === 'done') {
                filtered = filtered.filter(act => act.IsDone === true || act.IsDone === 1);
            } else if (status === 'pending') {
                filtered = filtered.filter(act => !act.IsDone || act.IsDone === 0 || act.IsDone === false);
            }
        }
        
        this.state.didarActivitiesFiltered = filtered;
        
        const listContainer = document.getElementById('activities-list');
        if (listContainer) {
            listContainer.innerHTML = this.renderActivitiesList(filtered);
        }
    },
    
    async loadActivityTypesForDidarFilter() {
        try {
            const res = await this.req('get_activity_types');
            if (res.types && Array.isArray(res.types)) {
                const select = document.getElementById('activity-filter-type');
                if (select) {
                    // Clear existing options except first one
                    select.innerHTML = '<option value="">همه انواع</option>';
                    res.types.forEach(type => {
                        const option = document.createElement('option');
                        option.value = type.Id;
                        option.textContent = type.Title;
                        select.appendChild(option);
                    });
                }
            }
        } catch (error) {
            console.error('Error loading activity types for filter:', error);
        }
    },
    
    switchProfileTab(tab) {
        document.querySelectorAll('.profile-tab').forEach(t => {
            t.classList.remove('active', 'text-yellow-500', 'border-b-2', 'border-yellow-500');
            t.classList.add('text-gray-400');
        });
        document.querySelectorAll('.profile-tab-content').forEach(c => c.classList.add('hidden'));
        
        const tabEl = document.querySelector(`.profile-tab[onclick*="${tab}"]`);
        if(tabEl) {
            tabEl.classList.add('active', 'text-yellow-500', 'border-b-2', 'border-yellow-500');
            tabEl.classList.remove('text-gray-400');
        }
        
        const contentEl = document.getElementById(`profile-tab-${tab}`);
        if(contentEl) contentEl.classList.remove('hidden');
        
        // Load support logs when switching to support tab
        if (tab === 'support' && window.currentContactId) {
            this.loadSupportLogs(window.currentContactId);
        }
    },
    
    async loadActivityTypesForModal() {
        const selects = [
            document.getElementById('activity-type-select'),
            document.getElementById('deal-activity-type-select')
        ].filter(Boolean);
        if (selects.length === 0) return;

        selects.forEach(sel => sel.innerHTML = '<option value="">-- انتخاب نوع فعالیت --</option>');

        // Load activity types if not already loaded
        if (!this.state.activityTypes || this.state.activityTypes.length === 0) {
            try {
                await this.loadActivityTypes();
            } catch (error) {
                console.error('Error loading activity types:', error);
                // Fallback to hardcoded types if API fails
                this.state.activityTypes = [
                    { Id: '2a335dc1-4834-484b-b6dc-9ddd97eef67e', Title: 'تماس ورودی', Direction: 'incoming', Category: 'call', IsDisabled: false },
                    { Id: '790079e8-74ac-4760-9527-097627d3e7ec', Title: 'تماس خروجی', Direction: 'outgoing', Category: 'call', IsDisabled: false },
                    { Id: '3b446dc2-5945-595c-c7ed-0eee08ff78fd', Title: 'چت ورودی', Direction: 'incoming', Category: 'chat', IsDisabled: false },
                    { Id: '4c557ed3-6a56-606d-d8fe-1fff19gg89ge', Title: 'چت خروجی', Direction: 'outgoing', Category: 'chat', IsDisabled: false }
                ];
            }
        }

        if (!this.state.activityTypes || this.state.activityTypes.length === 0) {
            const fallback = this.getFallbackActivityTypes();
            if (fallback.length) {
                this.state.activityTypes = fallback;
            }
        }

        const types = (this.state.activityTypes && this.state.activityTypes.length > 0)
            ? this.state.activityTypes.filter(t => !t.IsDisabled)
            : [
                { Id: '2a335dc1-4834-484b-b6dc-9ddd97eef67e', Title: 'تماس ورودی', Direction: 'incoming', Category: 'call' },
                { Id: '790079e8-74ac-4760-9527-097627d3e7ec', Title: 'تماس خروجی', Direction: 'outgoing', Category: 'call' },
                { Id: '3b446dc2-5945-595c-c7ed-0eee08ff78fd', Title: 'چت ورودی', Direction: 'incoming', Category: 'chat' },
                { Id: '4c557ed3-6a56-606d-d8fe-1fff19gg89ge', Title: 'چت خروجی', Direction: 'outgoing', Category: 'chat' }
            ];

        const buildOptions = (sel) => {
            if (!sel) return;
            sel.innerHTML = '<option value="">-- انتخاب نوع فعالیت --</option>';
            types.forEach(type => {
                const opt = document.createElement('option');
                opt.value = type.Id;
                opt.textContent = type.Title || type.Id;
                opt.dataset.direction = type.Direction || 'outgoing';
                sel.appendChild(opt);
            });
        };

        selects.forEach(buildOptions);

        const handleChange = (sel) => {
            sel.addEventListener('change', () => {
                const selectedOption = sel.options[sel.selectedIndex];
                const directionInput = sel.id === 'deal-activity-type-select'
                    ? document.getElementById('deal-activity-direction')
                    : document.getElementById('activity-direction');
                if (selectedOption && directionInput) {
                    directionInput.value = selectedOption.dataset.direction || 'outgoing';
                }
            });
        };

        selects.forEach(handleChange);

        // Reset purchase question visibility
        const purchaseSelect = document.getElementById('activity-has-purchase');
        if (purchaseSelect) {
            purchaseSelect.value = '';
        }
        const dealPurchaseSelect = document.getElementById('deal-activity-has-purchase');
        if (dealPurchaseSelect) {
            dealPurchaseSelect.value = '';
        }
        this.handlePurchaseOtherNumberChange();
    },
    
    // SYNC
    async sync() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند همگام‌سازی را انجام دهد', 'error');
            return;
        }
        
        Swal.fire({
            title: 'همگام‌سازی',
            text: 'در حال همگام‌سازی با دیدار...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        const res = await this.req('sync', {entity_type: 'all'});
        Swal.close();
        
        if(res.status === 'success') {
            Swal.fire({
                icon: 'success',
                title: 'موفق',
                text: res.message || 'همگام‌سازی با موفقیت انجام شد',
                timer: 2000
            });
            this.refresh();
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },

    // SAVE USER
    async saveUser(e) {
        e.preventDefault();
        const form = e.target;
        const data = Object.fromEntries(new FormData(form));
        const isCreate = !data.user_id;
        if (isCreate || data.password || data.password_confirm) {
            if (!data.password || !data.password_confirm) {
                Swal.fire('خطا', 'رمز عبور و تکرار آن الزامی است', 'error');
                return;
            }
            if (data.password !== data.password_confirm) {
                Swal.fire('خطا', 'رمز عبور و تکرار آن یکسان نیستند', 'error');
                return;
            }
        }
        const res = await this.req('save_user', data);
        if(res.status === 'success') {
            this.closeModal('modalUser');
            this.loadUsers();
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'ذخیره شد', showConfirmButton:false, timer:2000});
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    openUserModal(userId = null) {
        const form = document.querySelector('#modalUser form');
        const title = document.getElementById('modal-user-title');
        form.reset();
        document.getElementById('user-form-id').value = userId || '';
        const didarField = document.getElementById('user-didar-select');
        if (didarField) didarField.value = '';
        if (title) title.textContent = userId ? 'ویرایش کاربر' : 'تعریف کاربر جدید';
        
        if(userId) {
            this.req('get_user', { user_id: userId }).then(res => {
                if (res.status !== 'success' || !res.user) {
                    Swal.fire('خطا', res.message || 'خطا در بارگذاری کاربر', 'error');
                    return;
                }
                const user = res.user;
                form.querySelector('input[name="email"]').value = user.email || '';
                form.querySelector('input[name="first_name"]').value = user.first_name || '';
                form.querySelector('input[name="last_name"]').value = user.last_name || '';
                form.querySelector('input[name="display_name"]').value = user.display_name || '';
                form.querySelector('input[name="username"]').value = user.username || '';
                form.querySelector('select[name="role"]').value = user.role || 'agent';
                form.querySelector('select[name="is_active"]').value = String(user.is_active ?? 1);
                if (didarField) didarField.value = user.didar_user_id || '';
            });
            const password = form.querySelector('input[name="password"]');
            const passwordConfirm = form.querySelector('input[name="password_confirm"]');
            if (password) password.required = false;
            if (passwordConfirm) passwordConfirm.required = false;
        } else {
            const password = form.querySelector('input[name="password"]');
            const passwordConfirm = form.querySelector('input[name="password_confirm"]');
            if (password) password.required = true;
            if (passwordConfirm) passwordConfirm.required = true;
        }
        
        this.openModal('modalUser');
    },

    async transferUserData(userId, userName) {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم مجاز است', 'error');
            return;
        }
        const users = Array.isArray(this.state.lastUsersList) ? this.state.lastUsersList : [];
        const fromUser = users.find(u => String(u.id) === String(userId));
        if (!fromUser || !fromUser.didar_user_id) {
            Swal.fire('خطا', 'کاربر مبدا معتبر نیست', 'error');
            return;
        }
        const options = users
            .filter(u => u.didar_user_id && String(u.id) !== String(userId))
            .map(u => `<option value="${this.escapeHtml(u.didar_user_id)}">${this.escapeHtml(u.display_name || `${u.first_name || ''} ${u.last_name || ''}`)}${u.username ? ` (${this.escapeHtml(u.username)})` : ''}</option>`)
            .join('');
        const ownerOptions = users
            .filter(u => u.didar_user_id)
            .map(u => `<option value="${this.escapeHtml(u.didar_user_id)}">${this.escapeHtml(u.display_name || `${u.first_name || ''} ${u.last_name || ''}`)}${u.username ? ` (${this.escapeHtml(u.username)})` : ''}</option>`)
            .join('');
        const productOptions = (Array.isArray(this.state.products) ? this.state.products : [])
            .map(p => `<option value="${this.escapeHtml(p.id)}">${this.escapeHtml(p.name || '')}</option>`)
            .join('');
        const serviceOptions = this.getLookupOptions('allowed_services')
            .map(item => `<option value="${this.escapeHtml(item.value)}">${this.escapeHtml(item.label || item.value)}</option>`)
            .join('');
        const topicOptions = this.getLookupOptions('content_topics')
            .map(item => `<option value="${this.escapeHtml(item.value)}">${this.escapeHtml(item.label || item.value)}</option>`)
            .join('');
        const levelOptions = this.getLookupOptions('customer_levels')
            .map(item => `<option value="${this.escapeHtml(item.value)}">${this.escapeHtml(item.label || item.value)}</option>`)
            .join('');
        const previewAsk = await Swal.fire({
            title: 'انتقال دیتا',
            html: `
                <div class="text-right space-y-3 text-sm">
                    <div class="text-gray-300">کاربر مبدا: <b>${this.escapeHtml(userName)}</b></div>
                    <label class="block text-xs text-gray-400">کاربر مقصد</label>
                    <select id="transfer-user-target" class="swal2-input" style="width:100%;margin:0">${options}</select>
                    <label class="block text-xs text-gray-400">نوع انتقال</label>
                    <select id="transfer-user-mode" class="swal2-input" style="width:100%;margin:0">
                        <option value="filtered">فقط لیدهای فیلترشده</option>
                        <option value="full">انتقال کامل همه دیتاهای مالکیتی</option>
                    </select>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-2">
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">مسئول قبلی</label>
                            <select id="transfer-prev-owner" class="swal2-input" style="width:100%;margin:0"><option value="">همه</option>${ownerOptions}</select>
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">وضعیت فروش</label>
                            <select id="transfer-sale-status" class="swal2-input" style="width:100%;margin:0">
                                <option value="all">همه</option>
                                <option value="success">موفق</option>
                                <option value="failed">ناموفق</option>
                            </select>
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">محصول خریداری شده</label>
                            <select id="transfer-product-id" class="swal2-input" style="width:100%;margin:0"><option value="">همه</option>${productOptions}</select>
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">خدمت درخواستی</label>
                            <select id="transfer-requested-service" class="swal2-input" style="width:100%;margin:0"><option value="">همه</option>${serviceOptions}</select>
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">موضوع محتوا</label>
                            <select id="transfer-content-topic" class="swal2-input" style="width:100%;margin:0"><option value="">همه</option>${topicOptions}</select>
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">سطح مشتری</label>
                            <select id="transfer-customer-level" class="swal2-input" style="width:100%;margin:0"><option value="">همه</option>${levelOptions}</select>
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">تعداد انتقال</label>
                            <input id="transfer-lead-limit" type="number" min="1" step="1" class="swal2-input" style="width:100%;margin:0" placeholder="مثلاً 100">
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">جستجو</label>
                            <input id="transfer-search" class="swal2-input" style="width:100%;margin:0" placeholder="نام، موبایل، کد مشتری">
                        </div>
                    </div>
                    <div class="text-xs text-amber-300">در حالت فیلترشده فقط لیدها منتقل می‌شوند و مالک‌های قبلی در تاریخچه باقی می‌مانند.</div>
                </div>`,
            width: 860,
            showCancelButton: true,
            confirmButtonText: 'پیش‌نمایش',
            cancelButtonText: 'انصراف',
            preConfirm: () => {
                const value = document.getElementById('transfer-user-target')?.value || '';
                if (!value) {
                    Swal.showValidationMessage('کاربر مقصد را انتخاب کنید');
                    return false;
                }
                return {
                    targetDidarId: value,
                    mode: document.getElementById('transfer-user-mode')?.value || 'filtered',
                    previousOwnerId: document.getElementById('transfer-prev-owner')?.value || '',
                    saleStatus: document.getElementById('transfer-sale-status')?.value || 'all',
                    productId: document.getElementById('transfer-product-id')?.value || '',
                    requestedService: document.getElementById('transfer-requested-service')?.value || '',
                    contentTopic: document.getElementById('transfer-content-topic')?.value || '',
                    customerLevel: document.getElementById('transfer-customer-level')?.value || '',
                    leadLimit: document.getElementById('transfer-lead-limit')?.value || '',
                    search: document.getElementById('transfer-search')?.value || ''
                };
            }
        });
        if (!previewAsk.isConfirmed) return;

        const transferPayload = {
            from_user_didar_id: fromUser.didar_user_id,
            to_user_didar_id: previewAsk.value.targetDidarId,
            preview: 1,
            leads_only: previewAsk.value.mode === 'filtered' ? 1 : 0
        };
        if (previewAsk.value.mode === 'filtered') {
            Object.assign(transferPayload, {
                previous_owner_id: previewAsk.value.previousOwnerId,
                sale_status: previewAsk.value.saleStatus,
                product_id: previewAsk.value.productId,
                requested_service: previewAsk.value.requestedService,
                content_topic: previewAsk.value.contentTopic,
                customer_level: previewAsk.value.customerLevel,
                lead_limit: previewAsk.value.leadLimit,
                search: previewAsk.value.search
            });
        }
        const preview = await this.req('transfer_user_data', transferPayload);
        if (preview.status !== 'success') {
            Swal.fire('خطا', preview.message || 'خطا در پیش‌نمایش انتقال', 'error');
            return;
        }
        const counts = preview.counts || {};
        const details = preview.details || {};
        const targetUser = preview.to_user || {};
        const html = Object.keys(counts).map(key => {
            const item = counts[key] || {};
            const detail = details[key] || {};
            const ids = Array.isArray(detail.identifiers) ? detail.identifiers : [];
            return `<div class="border border-slate-700 rounded p-2">
                <div>${this.escapeHtml(item.label)}: <b>${this.formatNumber(item.count || 0)}</b>${item.matched_count ? ` <span class="text-xs text-gray-400">(از ${this.formatNumber(item.matched_count)} مورد مطابق فیلتر)</span>` : ''}</div>
                <div class="text-xs text-gray-500 mt-1 break-all">${ids.length ? this.escapeHtml(ids.join('، ')) : 'شناسه‌ای وجود ندارد'}</div>
            </div>`;
        }).join('');
        const notes = Array.isArray(preview.notes) ? preview.notes : [];
        const confirm = await Swal.fire({
            title: 'تأیید انتقال',
            html: `<div class="text-right text-sm space-y-2"><div>از <b>${this.escapeHtml(userName)}</b> به <b>${this.escapeHtml(targetUser.display_name || targetUser.email || '')}</b></div>${html || '<div>موردی برای انتقال پیدا نشد</div>'}${notes.length ? `<div class="text-xs text-amber-300 pt-2">${notes.map(n => this.escapeHtml(n)).join('<br>')}</div>` : ''}</div>`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'اجرای انتقال',
            cancelButtonText: 'انصراف'
        });
        if (!confirm.isConfirmed) return;

        delete transferPayload.preview;
        const res = await this.req('transfer_user_data', transferPayload);
        if (res.status === 'success') {
            const doneCounts = res.counts || {};
            const doneDetails = res.details || {};
            const doneNotes = Array.isArray(res.notes) ? res.notes : [];
            const doneHtml = Object.keys(doneCounts).map(key => {
                const item = doneCounts[key] || {};
                const detail = doneDetails[key] || {};
                const ids = Array.isArray(detail.identifiers) ? detail.identifiers : [];
                return `<div class="border border-slate-700 rounded p-2">
                    <div>${this.escapeHtml(item.label)}: <b>${this.formatNumber(item.updated || 0)}</b></div>
                    <div class="text-xs text-gray-500 mt-1 break-all">${ids.length ? this.escapeHtml(ids.join('، ')) : 'شناسه‌ای وجود ندارد'}</div>
                </div>`;
            }).join('');
            Swal.fire({
                icon: 'success',
                title: 'انتقال انجام شد',
                html: `<div class="text-right text-sm space-y-2">${doneHtml || (res.message || 'انتقال انجام شد')}${doneNotes.length ? `<div class="text-xs text-slate-400 pt-2">${doneNotes.map(n => this.escapeHtml(n)).join('<br>')}</div>` : ''}</div>`,
                width: 760
            });
            this.loadUsers();
        } else {
            Swal.fire('خطا', res.message || 'خطا در انتقال دیتا', 'error');
        }
    },

    async deleteUser(userId, userName) {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم مجاز است', 'error');
            return;
        }
        const users = Array.isArray(this.state.lastUsersList) ? this.state.lastUsersList : [];
        const user = users.find(u => String(u.id) === String(userId));
        if (!user) {
            Swal.fire('خطا', 'کاربر یافت نشد', 'error');
            return;
        }
        const preview = await this.req('get_user_transfer_report', {
            user_didar_id: user.didar_user_id
        }).catch(() => null);
        const counts = preview && preview.status === 'success' ? (preview.counts || {}) : {};
        const details = preview && preview.status === 'success' ? (preview.details || {}) : {};
        const html = Object.keys(counts).map(key => {
            const item = counts[key] || {};
            const detail = details[key] || {};
            const ids = Array.isArray(detail.identifiers) ? detail.identifiers : [];
            return `<div class="border border-slate-700 rounded p-2">
                <div>${this.escapeHtml(item.label)}: <b>${this.formatNumber(item.count || 0)}</b></div>
                <div class="text-xs text-gray-500 mt-1 break-all">${ids.length ? this.escapeHtml(ids.join('، ')) : 'شناسه‌ای وجود ندارد'}</div>
            </div>`;
        }).join('');
        const confirm = await Swal.fire({
            title: 'حذف کاربر',
            html: `<div class="text-right text-sm space-y-2"><div>کاربر <b>${this.escapeHtml(userName)}</b> حذف می‌شود.</div><div class="text-red-300">اگر دیتای مالکیتی دارد، بهتر است ابتدا انتقال دیتا انجام شود.</div>${html}</div>`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'حذف کاربر',
            cancelButtonText: 'انصراف',
            confirmButtonColor: '#dc2626'
        });
        if (!confirm.isConfirmed) return;

        const res = await this.req('delete_user', { user_id: userId });
        if (res.status === 'success') {
            const reportDetails = res.details || {};
            const notes = Array.isArray(res.notes) ? res.notes : [];
            const reportHtml = Object.keys(reportDetails).map(key => {
                const item = reportDetails[key] || {};
                const ids = Array.isArray(item.identifiers) ? item.identifiers : [];
                return `<div class="border border-slate-700 rounded p-2">
                    <div>${this.escapeHtml(item.label || key)}: <b>${this.formatNumber(ids.length)}</b></div>
                    <div class="text-xs text-gray-500 mt-1 break-all">${ids.length ? this.escapeHtml(ids.join('، ')) : 'شناسه‌ای وجود ندارد'}</div>
                </div>`;
            }).join('');
            Swal.fire({
                icon: 'success',
                title: 'کاربر حذف شد',
                html: `<div class="text-right text-sm space-y-2">${reportHtml}${notes.length ? `<div class="text-xs text-slate-400 pt-2">${notes.map(n => this.escapeHtml(n)).join('<br>')}</div>` : ''}</div>`,
                width: 760
            });
            this.loadUsers();
        } else {
            Swal.fire('خطا', res.message || 'خطا در حذف کاربر', 'error');
        }
    },
    
    openEditUserRoleModal(userId, currentRole, userName) {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند نقش کاربران را تغییر دهد', 'error');
            return;
        }
        const form = document.querySelector('#modalEditUserRole form');
        form.reset();
        document.getElementById('edit-role-user-id').value = userId;
        document.getElementById('edit-role-user-name').textContent = userName;
        document.getElementById('edit-role-select').value = currentRole;
        this.openModal('modalEditUserRole');
    },
    
    async saveUserRole(e) {
        e.preventDefault();
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند نقش کاربران را تغییر دهد', 'error');
            return;
        }
        const form = e.target;
        const data = Object.fromEntries(new FormData(form));
        
        Swal.fire({
            title: 'در حال ذخیره...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('update_user_role', data);
            Swal.close();
            if (res.status === 'success') {
                Swal.fire({toast: true, position: 'bottom-end', icon: 'success', title: 'نقش کاربر تغییر یافت', showConfirmButton: false, timer: 2000});
                this.closeModal('modalEditUserRole');
                this.loadUsers();
            } else {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
            }
        } catch (error) {
            console.error('Error in saveUserRole:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در تغییر نقش: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    openChangePasswordModal(userId = null) {
        const form = document.querySelector('#modalChangePassword form');
        form.reset();
        const targetUserId = userId || window.currentUserId || window.CURRENT_USER_ID || 0;
        document.getElementById('change-password-user-id').value = targetUserId;
        
        // If admin changing someone else's password, hide current password field
        const isAdmin = window.APP_USER?.role === 'admin';
        const isOwnPassword = String(targetUserId) === String(window.CURRENT_USER_ID || 0);
        
        const currentSection = document.getElementById('change-password-current-section');
        const currentInput = document.getElementById('change-password-current');
        
        if (isAdmin && !isOwnPassword) {
            currentSection.style.display = 'none';
            currentInput.value = '';
            currentInput.removeAttribute('required');
        } else {
            currentSection.style.display = 'block';
            currentInput.setAttribute('required', 'required');
        }
        
        this.openModal('modalChangePassword');
    },
    
    async changePassword(e) {
        e.preventDefault();
        const formData = new FormData(e.target);
        const data = Object.fromEntries(formData);
        
        const res = await this.req('change_password', data);
        if(res.status === 'success') {
            this.closeModal('modalChangePassword');
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'رمز عبور تغییر یافت', showConfirmButton:false, timer:2000});
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    openChangePasswordModal(userId = null) {
        const form = document.querySelector('#modalChangePassword form');
        form.reset();
        const targetUserId = userId || window.CURRENT_USER_ID || 0;
        document.getElementById('change-password-user-id').value = targetUserId;
        
        // If admin changing someone else's password, hide current password field
        const isAdmin = window.APP_USER?.role === 'admin';
        const isOwnPassword = String(targetUserId) === String(window.CURRENT_USER_ID || 0);
        
        const currentSection = document.getElementById('change-password-current-section');
        const currentInput = document.getElementById('change-password-current');
        
        if (isAdmin && !isOwnPassword) {
            currentSection.style.display = 'none';
            currentInput.value = '';
            currentInput.removeAttribute('required');
        } else {
            currentSection.style.display = 'block';
            currentInput.setAttribute('required', 'required');
        }
        
        this.openModal('modalChangePassword');
    },
    
    async changePassword(e) {
        e.preventDefault();
        const formData = new FormData(e.target);
        const data = Object.fromEntries(formData);
        
        const res = await this.req('change_password', data);
        if(res.status === 'success') {
            this.closeModal('modalChangePassword');
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'رمز عبور تغییر یافت', showConfirmButton:false, timer:2000});
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    async changePasswordFromSettings(e) {
        e.preventDefault();
        const formData = new FormData(e.target);
        const data = Object.fromEntries(formData);

        const res = await this.req('change_password', data);
        if(res.status === 'success') {
            // Reset form
            e.target.reset();
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'رمز عبور تغییر یافت', showConfirmButton:false, timer:2000});
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },

    async setMasterPassword(e) {
        e.preventDefault();
        const formData = new FormData(e.target);
        const data = Object.fromEntries(formData);

        // Client-side validation
        if (data.new_master_password !== data.confirm_master_password) {
            Swal.fire('خطا', 'رمز عبور اصلی جدید و تأیید آن مطابقت ندارند', 'error');
            return;
        }

        if (data.new_master_password.length < 6) {
            Swal.fire('خطا', 'رمز عبور اصلی باید حداقل 6 کاراکتر باشد', 'error');
            return;
        }

        const res = await this.req('set_master_password', data);
        if(res.status === 'success') {
            // Reset form
            e.target.reset();
            Swal.fire({
                toast: true, 
                position: 'bottom-end', 
                icon: 'success', 
                title: res.message || 'رمز عبور اصلی تنظیم شد', 
                showConfirmButton: false, 
                timer: 3000
            });
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    // SAVE ACTIVITY
    async saveActivity(e) {
        e.preventDefault();
        
        try {
            console.log('saveActivity called');
            const form = e.target;
            const contactId = window.currentContactId || form.contact_id.value;
            const dealSelectVal = document.getElementById('activity-deal-select')?.value || '';
            const dealHiddenVal = document.getElementById('activity-deal-id')?.value || '';
            const dealId = dealSelectVal || dealHiddenVal;
            
            // Check if selected deal is closed
            if (this.state.currentDeals && dealId) {
                const currentDeal = this.state.currentDeals.find(d => (d.didar_deal_id || d.id || d.deal_id) === dealId);
                if (currentDeal && (currentDeal.status === 'Won' || currentDeal.status === 'Lost')) {
                    Swal.fire('خطا', 'این معامله بسته شده است و امکان ثبت فعالیت ندارد.', 'error');
                    return;
                }
            }
            
            if(!contactId) {
                Swal.fire('خطا', 'لطفاً شخص را انتخاب کنید', 'error');
                return;
            }
            
            console.log('Contact ID:', contactId);
            
            // بررسی و تنظیم وضعیت required فیلد خرید قبلی قبل از submit
            try {
                this.handleActivityFormVisibility();
            } catch(err) {
                console.error('Error in handleActivityFormVisibility:', err);
            }
            
                const pipelineStage = form.pipeline_stage?.value || '';
                const isScheduled = form.is_scheduled?.checked || false;

                // pipelineStage is only required when a deal is selected
                if (dealId && !pipelineStage) {
                    Swal.fire('خطا', 'لطفاً مرحله کاریز معامله را انتخاب کنید', 'error');
                return;
            }

            if (!form.checkValidity()) {
                form.reportValidity();
                return;
            }
            
            // Get due date if scheduled
            let dueDate = '';
            // isScheduled already declared above
            if (isScheduled) {
                const persianDateValue = form.due_date?.value || '';
                const hourSelect = document.getElementById('activity-due-hour');
                const minuteSelect = document.getElementById('activity-due-minute');
                const hour = hourSelect?.value || '00';
                const minute = minuteSelect?.value || '00';
                
                if (!persianDateValue || !persianDateValue.trim()) {
                    Swal.fire('خطا', 'لطفاً تاریخ را انتخاب کنید', 'error');
                    return;
                }
                
                if (!hour || hour === '') {
                    Swal.fire('خطا', 'لطفاً ساعت را انتخاب کنید', 'error');
                    return;
                }
                
                if (!minute || minute === '') {
                    Swal.fire('خطا', 'لطفاً دقیقه را انتخاب کنید', 'error');
                    return;
                }
                
                try {
                    // Convert Persian/Farsi digits to English digits
                    const persianToEnglish = (str) => {
                        const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
                        const englishDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
                        let result = str;
                        for (let i = 0; i < persianDigits.length; i++) {
                            result = result.replace(new RegExp(persianDigits[i], 'g'), englishDigits[i]);
                        }
                        return result;
                    };
                    
                    // Convert Persian digits to English
                    const englishDateValue = persianToEnglish(persianDateValue.trim());
                    console.log('Original date:', persianDateValue);
                    console.log('Converted date:', englishDateValue);
                    
                    // Parse Persian date string (format: YYYY/MM/DD)
                    const dateParts = englishDateValue.split('/');
                    if (dateParts.length !== 3) {
                        throw new Error('فرمت تاریخ نامعتبر است');
                    }
                    
                    const year = parseInt(dateParts[0]);
                    const month = parseInt(dateParts[1]);
                    const day = parseInt(dateParts[2]);
                    const hours = parseInt(hour);
                    const minutes = parseInt(minute);
                    
                    console.log('Parsed values:', { year, month, day, hours, minutes });
                    
                    // Validate
                    if (isNaN(year) || isNaN(month) || isNaN(day) || isNaN(hours) || isNaN(minutes)) {
                        throw new Error('مقادیر تاریخ یا زمان نامعتبر است');
                    }
                    
                    // Additional validation
                    if (year < 1300 || year > 1500) {
                        throw new Error('سال نامعتبر است');
                    }
                    if (month < 1 || month > 12) {
                        throw new Error('ماه نامعتبر است');
                    }
                    if (day < 1 || day > 31) {
                        throw new Error('روز نامعتبر است');
                    }
                    
                    // Use the helper function that already works
                    // Format: HH:mm
                    const timeString = `${hour}:${minute}`;
                    dueDate = persianToGregorian(englishDateValue, timeString);
                    
                    if (!dueDate) {
                        throw new Error('خطا در تبدیل تاریخ. لطفاً تاریخ معتبری انتخاب کنید.');
                    }
                    
                    // persianToGregorian now returns format: YYYY-MM-DD HH:mm:ss (local timezone)
                    console.log('Converted date (local timezone):', dueDate);
                    
                    // Validate the final date
                    if (!dueDate || dueDate.length < 19) {
                        throw new Error('فرمت تاریخ نهایی نامعتبر است');
                    }
                    
                    console.log('Sending due date to server:', dueDate);
                    
                } catch (e) {
                    console.error('Error converting date:', e, 'Input:', persianDateValue);
                    Swal.fire('خطا', 'تاریخ وارد شده معتبر نیست. لطفاً تاریخ و زمان را به درستی وارد کنید. (' + (e.message || 'خطای نامشخص') + ')', 'error');
                    return;
                }
            }
            
                const activityTypeSelect = form.activity_type_id;
                const activityTypeTitle = activityTypeSelect?.selectedOptions?.[0]?.textContent?.trim?.() || '';
                const activityDuration = Math.max(0, parseInt(form.activity_duration?.value || '0', 10) || 0);
                if (!activityTypeSelect?.value) {
                    Swal.fire('خطا', 'نوع فعالیت را انتخاب کنید', 'error');
                    return;
                }
                if (activityDuration <= 0) {
                    Swal.fire('خطا', 'مدت زمان فعالیت الزامی است', 'error');
                    if (form.activity_duration?.focus) form.activity_duration.focus();
                    return;
                }
                const callType = form.call_type?.value || '';
                if (callType !== 'voip' && callType !== 'phone') {
                    Swal.fire('خطا', 'نوع مکالمه را انتخاب کنید', 'error');
                    if (form.call_type?.focus) form.call_type.focus();
                    return;
                }

                const data = {
                    contact_id: contactId,
                    deal_id: dealId,
                    activity_type_id: activityTypeSelect?.value || '',
                    activity_type_title: activityTypeTitle,
                    note: form.note?.value || '',
                    result_note: form.result_note?.value || '',
                    activity_duration: activityDuration,
                    call_type: callType,
                    direction: form.direction?.value || 'outgoing',
                    current_stage: form.current_stage?.value || 'keep_current',
                    pipeline_stage: form.pipeline_stage?.value || '',
                    has_previous_purchase_other_number: form.has_previous_purchase_other_number?.value || '',
                    previous_purchase_number: form.previous_purchase_number?.value || '',
                    referral_note: form.referral_note?.value || '',
                    failure_reason: form.failure_reason?.value || '',
                    failure_other_description: form.failure_other_description?.value || '',
                    is_done: form.is_done?.checked ? '1' : '0',
                    is_scheduled: isScheduled ? '1' : '0',
                    due_date: dueDate,
                    reminder_enabled: form.reminder_enabled?.checked ? '1' : '0',
                    reminder_offset_minutes: this.calculateReminderOffset(form)
                };
            
            console.log('Sending data:', data);
            
            Swal.showLoading();
            const res = await this.req('save_activity', data);
            Swal.close();
            
            console.log('Response received:', res);
            
            if(res.status === 'success') {
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'فعالیت ثبت شد', showConfirmButton:false, timer:2000});
                this.closeModal('modalActivity');
                form.reset();
                if(window.currentContactId) {
                    // Reload person profile to get updated virtual stage
                    await this.openPersonProfile(window.currentContactId);
                    // Reload deals to منعکس کردن تغییر مرحله کاریز
                    await this.loadDeals();
                    // اگر مودال معامله باز است، آن را بدون بستن رفرش کنیم
                    const dealModal = document.getElementById('modalDeal');
                    if (dealModal && !dealModal.classList.contains('hidden')) {
                        const currentDealId = this.state.currentDeal?.didar_deal_id || document.getElementById('deal-id')?.value || '';
                        if (currentDealId) {
                            await this.openDealModal(currentDealId);
                            this.showDealFormTab('activities');
                        }
                    }
                    // Also reload leads list if we're on leads view
                    if (document.getElementById('view-leads') && !document.getElementById('view-leads').classList.contains('hidden')) {
                        await this.loadLeads();
                    }
                }
            } else {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
            }
        } catch(error) {
            console.error('Error in saveActivity:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در ثبت فعالیت: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    // HANDLE DEAL STATUS CHANGE
    handleDealStatusChange() {
        const status = document.getElementById('deal-status').value;
        const form = document.getElementById('deal-form');
        const paymentDetails = document.getElementById('deal-payment-details');
        const failureReasonSection = document.getElementById('deal-failure-reason-section');
        const failureReason = document.getElementById('deal-failure-reason');
        const failureOtherReason = document.getElementById('deal-failure-other-reason');
        const failureOtherDesc = document.getElementById('deal-failure-other-description');
        const pipelineStageSelect = document.getElementById('deal-pipeline-stage');
        const pipelineStageIndicator = document.getElementById('pipeline-stage-required-indicator');
        
        const isLost = status === 'lost';
        const shouldRequirePayments = status === 'won';

        if (paymentDetails) paymentDetails.style.display = 'block';

        if (failureReasonSection) failureReasonSection.classList.toggle('hidden', !isLost);
        if (failureReason) {
            failureReason.required = isLost;
            if (!isLost) failureReason.value = '';
        }
        if (failureOtherReason) failureOtherReason.classList.toggle('hidden', !isLost);
        if (failureOtherDesc) {
            failureOtherDesc.required = isLost;
            if (!isLost) {
                failureOtherDesc.value = '';
            } else {
                failureOtherDesc.placeholder = 'لطفاً توضیحات دلیل ناموفق بودن را وارد کنید';
            }
        }

        // Make pipeline stage optional when status is lost
        if (pipelineStageSelect) {
            pipelineStageSelect.required = !isLost;
            if (isLost && !pipelineStageSelect.value) {
                // Optionally set a default value for lost deals
                pipelineStageSelect.value = 'معامله ناموفق';
            }
        }
        // Update the required indicator
        if (pipelineStageIndicator) {
            pipelineStageIndicator.style.display = isLost ? 'none' : 'inline';
        }

        const payableAmount = document.getElementById('deal-payable-amount');
        const paymentConditions = document.getElementById('deal-payment-conditions');
        if (payableAmount) payableAmount.required = true;
        if (paymentConditions) paymentConditions.required = false;

        if (form) {
            const paymentDates = form.querySelectorAll('input[name="payment_date[]"]');
            const paymentAmounts = form.querySelectorAll('input[name="payment_amount[]"]');
            const paymentMethods = form.querySelectorAll('select[name="payment_method[]"]');
            const paymentDescriptions = form.querySelectorAll('textarea[name="payment_description[]"]');
        const paymentCardLast4s = form.querySelectorAll('input[name="payment_card_last4[]"]');
            paymentDates.forEach(input => input.required = shouldRequirePayments);
            paymentAmounts.forEach(input => input.required = shouldRequirePayments);
            paymentMethods.forEach(select => select.required = shouldRequirePayments);
            paymentDescriptions.forEach(textarea => textarea.required = shouldRequirePayments);
        }

        this.updateDealCurrentFields(null, status);
    },

    handleDealPipelineStageChange() {
        const stageSelect = document.getElementById('deal-pipeline-stage');
        const statusSelect = document.getElementById('deal-status');
        const stageVal = stageSelect?.value || '';
        const isFailed = stageVal === 'معامله ناموفق' || stageVal === 'deal_failed';
        const isSuccess = stageVal === 'اتمام' || stageVal === 'deal_success';
        if (statusSelect) {
            if (isFailed) {
                statusSelect.value = 'lost';
            } else if (isSuccess) {
                statusSelect.value = 'won';
            }
        }
        this.handleDealStatusChange();
        this.updateDealCurrentFields(stageVal, statusSelect?.value || '');
    },

    handleActivityStageChange(stageVal) {
        const statusSelect = document.getElementById('deal-status');
        const contactId = document.getElementById('deal-activity-contact-id')?.value || window.currentContactId || '';
        const dealId = document.getElementById('deal-activity-deal-id')?.value || document.getElementById('deal-id')?.value || '';
        const paymentsOk = this.hasEnoughPaymentsForSuccess();
        const failureBox = document.getElementById('failure-reason-extra');
        const stageSelect = document.getElementById('activity-virtual-stage') || document.getElementById('deal-activity-stage');
        console.log('handleActivityStageChange', { stageVal, paymentsOk, contactId, dealId });

        if (stageVal === 'deal_failed' || stageVal === 'معامله ناموفق') {
            if (statusSelect) {
                statusSelect.value = 'lost';
                this.handleDealStatusChange();
            }
            const pipelineStageSelect = document.getElementById('deal-pipeline-stage');
            if (pipelineStageSelect) {
                pipelineStageSelect.value = 'معامله ناموفق';
            }
            this.toggleDealActivityFailure();
            this.toggleActivityFailureOtherReason();
            return;
        }

        if (stageVal === 'deal_success') {
            if (!paymentsOk) {
                Swal.fire('خطا', 'برای ثبت معامله موفق، جزئیات پرداخت باید کامل باشد.', 'error');
                if (stageSelect) stageSelect.value = this.state.activityStagePrev || '';
                console.warn('Blocked deal_success due to missing payments');
                return;
            }
            Swal.fire({
                icon: 'info',
                title: 'ثبت معامله موفق',
                text: 'معامله به عنوان فروش موفق ثبت و بایگانی می‌شود.',
                confirmButtonText: 'متوجه شدم'
            });
            if (statusSelect) {
                statusSelect.value = 'won';
                this.handleDealStatusChange();
            }
            const pipelineStageSelect = document.getElementById('deal-pipeline-stage');
            if (pipelineStageSelect) {
                pipelineStageSelect.value = 'اتمام';
            }
        }

        this.toggleDealActivityFailure();
        this.toggleActivityFailureOtherReason();
        this.updateDealCurrentFields(stageVal, statusSelect?.value || '');
        this.state.activityStagePrev = stageVal || '';
    },
    
	    // TOGGLE FAILURE OTHER REASON (deal form + deal-failed modal)
	    toggleFailureOtherReason() {
	        // Deal form in modalDeal (inline reason select)
	        const dealReasonSelect = document.getElementById('deal-failure-reason');
	        const dealOtherDiv = document.getElementById('deal-failure-other-reason');
	        const dealOtherDesc = document.getElementById('deal-failure-other-description');
	        const dealStatusSelect = document.getElementById('deal-status');

	        if (dealReasonSelect && dealOtherDiv && dealOtherDesc && dealStatusSelect) {
	            const reason = dealReasonSelect.value;
	            const dealStatus = dealStatusSelect.value;

	            // For lost deals, description is always shown + required
	            if (dealStatus === 'lost') {
	                dealOtherDiv.classList.remove('hidden');
	                dealOtherDesc.required = true;
	                dealOtherDesc.placeholder = 'لطفاً توضیحات دلیل ناموفق بودن را وارد کنید';
	            } else {
	                // For other statuses, only show description when "other" is selected
	                if (reason === 'other') {
	                    dealOtherDiv.classList.remove('hidden');
	                    dealOtherDesc.required = true;
	                } else {
	                    dealOtherDiv.classList.add('hidden');
	                    dealOtherDesc.required = false;
	                    dealOtherDesc.value = '';
	                }
	            }
	        }

	        // ModalDealFailed (quick mark as failed)
	        const modalReasonSelect = document.getElementById('failure-reason');
	        const modalOtherDiv = document.getElementById('failure-other-reason');
	        const modalOtherDesc = document.getElementById('failure-other-description');
	        if (modalReasonSelect && modalOtherDiv && modalOtherDesc) {
	            const reason = modalReasonSelect.value;
	            const shouldShow = !!reason;
	            modalOtherDiv.classList.toggle('hidden', !shouldShow);
	            modalOtherDesc.required = shouldShow;
	            if (!shouldShow) {
	                modalOtherDesc.value = '';
	            }
	        }
	    },

    populateActivityDealSelect(deals = [], selectedDealId = '', fallbackStage = '') {
        const select = document.getElementById('activity-deal-select');
        const hiddenDealId = document.getElementById('activity-deal-id');
        const stageDisplay = document.getElementById('activity-current-stage-display');
        const currentStageInput = document.getElementById('activity-current-stage');
        const pipelineSelect = document.getElementById('activity-virtual-stage');
        if (!select) return;

        const normalizedDeals = Array.isArray(deals) ? deals : [];
        // Cache for reuse when modal is opened بدون رفرش
        this.state.currentDeals = normalizedDeals;

        select.innerHTML = '<option value=\"\">-- انتخاب معامله مرتبط --</option>';
        normalizedDeals.forEach(d => {
            const option = document.createElement('option');
            const id = d.didar_deal_id || d.id || d.deal_id || '';
            option.value = id;
            option.dataset.stage = d.pipeline_stage || '';
            
            const isClosed = d.status === 'Won' || d.status === 'Lost';
            if (isClosed) {
                option.disabled = true;
                const statusText = d.status === 'Won' ? 'موفق' : 'ناموفق';
                option.textContent = `${d.title || 'معامله'} (${statusText}) - بسته شده`;
            } else {
                const stageLabel = option.dataset.stage ? ` | ${option.dataset.stage}` : '';
                option.textContent = `${d.title || 'معامله'}${stageLabel}`;
            }
            select.appendChild(option);
        });

        const updateStageDisplay = () => {
            const opt = select.selectedOptions[0];
            const stage = opt?.dataset.stage || fallbackStage || '';
            if (hiddenDealId) hiddenDealId.value = select.value || '';
            if (currentStageInput) currentStageInput.value = stage || 'keep_current';
            if (pipelineSelect) {
                pipelineSelect.value = stage || '';
            }
            if (stageDisplay) {
                stageDisplay.innerHTML = stage
                    ? `<span class=\"text-sm text-gray-200\">${stage}</span>`
                    : '<span class=\"text-sm text-gray-500\">مرحله تعیین نشده</span>';
            }
        };

        select.onchange = () => {
            updateStageDisplay();
            this.handleActivityDealSelection();
        };

        let presetId = selectedDealId || select.dataset.defaultSelected || '';
        if (!presetId && normalizedDeals.length === 1) {
            const d = normalizedDeals[0];
            const isClosed = d.status === 'Won' || d.status === 'Lost';
            if (!isClosed) {
                presetId = d.didar_deal_id || d.id || d.deal_id || '';
            }
        }
        if (presetId) {
            select.value = presetId;
        } else {
            select.value = '';
        }

        updateStageDisplay();
        this.handleActivityDealSelection();
    },
    
    // SAVE DEAL
    async saveDeal(e) {
        e.preventDefault();
        const form = e.target;
        const contactId = this.state.currentLead?.didar_contact_id || window.currentContactId || form.contact_id.value;
        const dealId = form.deal_id.value;
        if (!dealId && !this.canActionClient('deals.create')) {
            Swal.fire('خطا', 'دسترسی به ایجاد معامله ندارید', 'error');
            return;
        }
        if (dealId && !this.canActionClient('deals.edit')) {
            const hasAnyPaymentPermission =
                this.canActionClient('payments.create') ||
                this.canActionClient('payments.edit') ||
                this.canActionClient('payments.delete');
            if (!hasAnyPaymentPermission) {
                Swal.fire('خطا', 'دسترسی به ویرایش معامله ندارید', 'error');
                return;
            }
        }
        let dealStatus = form.deal_status.value;
        let pipelineStage = form.pipeline_stage?.value || '';

        // COMPREHENSIVE LOGGING FOR TROUBLESHOOTING DATA FRAGMENTATION
        console.log('[DEAL_SAVE] Starting deal save process', {
            timestamp: new Date().toISOString(),
            contactId: contactId,
            dealId: dealId,
            windowCurrentContactId: window.currentContactId,
            formContactId: form.contact_id?.value,
            formTitle: form.title?.value,
            dealStatus: dealStatus,
            pipelineStage: pipelineStage,
            isNewDeal: !dealId,
            formDataKeys: Array.from(new FormData(form).keys()),
            userAgent: navigator.userAgent,
            url: window.location.href,
            sessionStorage: Object.keys(sessionStorage),
            localStorage: Object.keys(localStorage)
        });

        // For existing deals, status and stage are ALWAYS required
        // No fallback to existing values - user must explicitly select new values

        const isNewDeal = !dealId;
        const productCheckboxes = form.querySelectorAll('input[name="product_ids[]"]:checked');
        let productIds = Array.from(productCheckboxes).map(cb => cb.value);
        
        if(!contactId) {
            Swal.fire('خطا', 'لطفاً شخص را انتخاب کنید', 'error');
            return;
        }
        
        if(!dealStatus) {
            Swal.fire('خطا', 'لطفاً وضعیت معامله را انتخاب کنید', 'error');
            return;
        }
        // Pipeline stage is only required if status is NOT lost
        if(!pipelineStage && dealStatus !== 'lost') {
            Swal.fire('خطا', 'لطفاً مرحله کاریز معامله را انتخاب کنید', 'error');
            return;
        }

        const stageMeansSuccess = (pipelineStage === 'deal_success' || pipelineStage === 'اتمام');
        const stageMeansFailed = (pipelineStage === 'deal_failed' || pipelineStage === 'معامله ناموفق');
        if (stageMeansSuccess) {
            form.deal_status.value = 'won';
            dealStatus = 'won';
        } else if (stageMeansFailed) {
            form.deal_status.value = 'lost';
            dealStatus = 'lost';
        }
        
        // If status is 'lost' and pipeline_stage is empty, automatically set it to 'معامله ناموفق'
        if (dealStatus === 'lost' && !pipelineStage) {
            pipelineStage = 'معامله ناموفق';
            if (form.pipeline_stage) {
                form.pipeline_stage.value = 'معامله ناموفق';
            }
        }

        // Enforce required deal info fields
        const requiredInfoFields = [
            { field: form.title, label: 'خدمات قابل ارائه' },
            { field: form.service_cost, label: 'هزینه خدمات' },
            { field: form.price_list_code, label: 'کد لیست قیمت' },
            { field: form.payable_amount, label: 'مبلغ قابل پرداخت' }
        ];
        for (const item of requiredInfoFields) {
            const val = item.field?.value ?? '';
            if (String(val).trim() === '') {
                Swal.fire('خطا', `${item.label} الزامی است`, 'error');
                if (item.field?.focus) item.field.focus();
                return;
            }
        }
        if (isNewDeal) {
            const dealSource = form.acquaintance_source?.value || '';
            if (!String(dealSource).trim()) {
                Swal.fire('خطا', 'شیوه آشنایی معامله الزامی است', 'error');
                form.acquaintance_source?.focus?.();
                return;
            }
        }

        const selectedTitle = form.title?.value?.trim() || '';
        const selectedTitleOption = form.title?.selectedOptions?.[0];
        const isGeneratedTitle =
            (selectedTitleOption && selectedTitleOption.dataset && selectedTitleOption.dataset.generated === '1') ||
            /^Ù…Ø¹Ø§Ù…Ù„Ù‡\s+\S+\s+\d{10,12}$/u.test(selectedTitle);
        if (isNewDeal && isGeneratedTitle) {
            Swal.fire('خطا', '\u0644\u0637\u0641\u0627\u064b \u062e\u062f\u0645\u0627\u062a \u0642\u0627\u0628\u0644 \u0627\u0631\u0627\u0626\u0647 \u0631\u0627 \u0627\u0646\u062a\u062e\u0627\u0628 \u06a9\u0646\u06cc\u062f', 'error');
            if (form.title?.focus) form.title.focus();
            return;
        }

        if (productIds.length === 0) {
            const linkedProductIdsRaw = String(selectedTitleOption?.dataset?.productIds || '').trim();
            if (linkedProductIdsRaw !== '') {
                productIds = linkedProductIdsRaw.split(',').map(v => v.trim()).filter(Boolean);
            } else if (selectedTitleOption?.dataset?.productId) {
                productIds = [selectedTitleOption.dataset.productId];
            }
        }

        const paymentDates = form.querySelectorAll('input[name="payment_date[]"]');
        const paymentAmounts = form.querySelectorAll('input[name="payment_amount[]"]');
        const paymentMethods = form.querySelectorAll('select[name="payment_method[]"]');
        const paymentDescriptions = form.querySelectorAll('textarea[name="payment_description[]"]');
        const paymentCardLast4s = form.querySelectorAll('input[name="payment_card_last4[]"]');
        const paymentBanks = form.querySelectorAll('select[name="destination_bank[]"]');
        const paymentBankOthers = form.querySelectorAll('input[name="destination_bank_other[]"]');
        const paymentAccountOwners = form.querySelectorAll('input[name="account_owner_name[]"]');
        const paymentDepositorNames = form.querySelectorAll('input[name="depositor_name[]"]');
        const paymentDestinationCards = form.querySelectorAll('input[name="destination_card_number[]"]');
        const paymentDestinationBankTitles = form.querySelectorAll('input[name="destination_bank_title[]"]');
        const paymentDestinationCardFulls = form.querySelectorAll('input[name="destination_card_full[]"]');
        const paymentDestinationAccounts = form.querySelectorAll('input[name="destination_account_number[]"]');
        const paymentDestinationShebas = form.querySelectorAll('input[name="destination_sheba[]"]');
        const paymentTimes = form.querySelectorAll('input[name="payment_time[]"]');
        const paymentCardReaderDevices = form.querySelectorAll('select[name="card_reader_device[]"]');
        const paymentCurrencies = form.querySelectorAll('select[name="payment_currency[]"]');
        const paymentCurrencyOthers = form.querySelectorAll('input[name="payment_currency_other[]"]');
        const paymentCurrencyAmounts = form.querySelectorAll('input[name="payment_currency_amount[]"]');
        const paymentCheckBanks = form.querySelectorAll('input[name="check_bank_name[]"]');
        const paymentCheckDates = form.querySelectorAll('input[name="check_date[]"]');
        const paymentCheckNumbers = form.querySelectorAll('input[name="check_number[]"]');
        const paymentCheckOwnerNames = form.querySelectorAll('input[name="check_owner_name[]"]');
        const paymentRegisteredAts = form.querySelectorAll('input[name="payment_registered_at[]"]');
        const paymentDepositDates = form.querySelectorAll('input[name="deposit_date[]"]');
        const paymentCheckDueDates = form.querySelectorAll('input[name="check_due_date[]"]');
        const paymentSayadiDates = form.querySelectorAll('input[name="sayadi_date[]"]');
        const paymentCheckDeliveryDates = form.querySelectorAll('input[name="check_delivery_date[]"]');
        const paymentCheckRecipientNames = form.querySelectorAll('input[name="check_recipient_name[]"]');
        const paymentReceiptImages = form.querySelectorAll('input[name="receipt_image_path[]"]');
        const paymentCheckImages = form.querySelectorAll('input[name="check_image_path[]"]');
        const successRequiredAmount = this.getDealSuccessRequiredPaymentAmount(form);
        const mustHavePayments = (dealStatus === 'won' || stageMeansSuccess) && successRequiredAmount > 0;
        const anyPaymentFilled = [
            ...Array.from(paymentDates).map(i => i.value),
            ...Array.from(paymentAmounts).map(i => i.value),
            ...Array.from(paymentMethods).map(i => i.value),
            ...Array.from(paymentDescriptions).map(i => i.value)
        ].some(v => String(v || '').trim() !== '');
        
        // Validate failure reason for lost deals
        if (dealStatus === 'lost') {
            const failureReason = form.failure_reason.value;
            if (!failureReason) {
                Swal.fire('خطا', 'لطفاً دلیل ناموفق بودن معامله را انتخاب کنید', 'error');
                return;
            }
            // برای همه دلایل، توضیحات اجباری است
            const otherDesc = form.failure_other_description.value;
            if (!otherDesc || !otherDesc.trim()) {
                Swal.fire('خطا', 'لطفاً توضیحات دلیل ناموفق بودن را وارد کنید', 'error');
                const descField = document.getElementById('deal-failure-other-description');
                if (descField) descField.focus();
                return;
            }
        }
        
	        const validatePaymentRow = () => true;

        if (mustHavePayments) {
            if (paymentDates.length === 0) {
                Swal.fire('خطا', 'لطفاً حداقل یک پرداخت را تکمیل کنید', 'error');
                return;
            }
            for (let i = 0; i < paymentDates.length; i++) {
                if (!validatePaymentRow(i)) return;
            }
        } else if (anyPaymentFilled) {
            for (let i = 0; i < paymentDates.length; i++) {
                if (!validatePaymentRow(i)) return;
            }
        } else if (dealStatus === 'won' && successRequiredAmount > 0 && !this.hasCompletePaymentsForSuccess()) {
            Swal.fire('خطا', 'برای ثبت معامله موفق، جزئیات پرداخت باید کامل باشد.', 'error');
            return;
        }
        
	        // Get discount occasion (use custom if other)
	        const hasDiscount = String(form.has_discount?.value || '0') === '1';
	        const discountType = form.discount_type?.value || '';
	        let discountOccasion = form.discount_occasion?.value || '';
	        if (discountOccasion === 'other') {
	            discountOccasion = form.discount_occasion_custom?.value || '';
	        }
	        let discountAmount = parseMoneyValue(form.discount_amount?.value || '');
	        const refundAmount = parseMoneyValue(form.refund_amount?.value || '');
	        const refundDescription = (form.refund_description?.value || '').trim();

	        if (hasDiscount && discountType === 'refund') {
	            if (refundAmount <= 0) {
	                Swal.fire('خطا', 'مبلغ مرجوعی الزامی است', 'error');
	                const el = document.getElementById('deal-refund-amount');
	                if (el) el.focus();
	                return;
	            }
	            if (!refundDescription) {
	                Swal.fire('خطا', 'توضیحات مرجوعی الزامی است', 'error');
	                const el = document.getElementById('deal-refund-description');
	                if (el) el.focus();
	                return;
	            }
	            // Refund is not a discount amount/occasion
	            discountOccasion = '';
	            discountAmount = 0;
	        } else if (!hasDiscount) {
	            discountOccasion = '';
	            discountAmount = 0;
	        }
        
	        // Collect multiple payments (only if status is not lost)
	        const payments = [];
	        
	        for (let i = 0; i < paymentDates.length; i++) {
	            const persianDate = paymentDates[i]?.value?.trim() || '';
	            const amountRaw = paymentAmounts[i]?.value?.trim() || '';
	            const method = paymentMethods[i]?.value || '';
	            const description = paymentDescriptions[i]?.value?.trim() || '';
            const cardLast4 = paymentCardLast4s[i]?.value?.trim() || '';
            const bankVal = paymentBanks[i]?.value?.trim() || '';
            const bankOtherVal = paymentBankOthers[i]?.value?.trim() || '';
            const destinationCardVal = paymentDestinationCards[i]?.value?.trim() || '';
            const destinationCardDigits = destinationCardVal.replace(/\D/g, '').slice(-4);
            const destinationBankTitleVal = paymentDestinationBankTitles[i]?.value?.trim() || '';
            const destinationCardFullVal = paymentDestinationCardFulls[i]?.value?.trim() || '';
            const destinationAccountVal = paymentDestinationAccounts[i]?.value?.trim() || '';
            const destinationShebaVal = paymentDestinationShebas[i]?.value?.trim() || '';
            const paymentTimeVal = paymentTimes[i]?.value?.trim() || '';
            const cardReaderDeviceVal = paymentCardReaderDevices[i]?.value?.trim() || '';
            const currencyVal = paymentCurrencies[i]?.value?.trim() || '';
            const currencyOtherVal = paymentCurrencyOthers[i]?.value?.trim() || '';
            const currencyAmountVal = parseMoneyValue(paymentCurrencyAmounts[i]?.value || '');
            const checkBankVal = paymentCheckBanks[i]?.value?.trim() || '';
            const checkDateVal = paymentCheckDates[i]?.value?.trim() || '';
            const checkNumberVal = paymentCheckNumbers[i]?.value?.trim() || '';
            const checkOwnerVal = paymentCheckOwnerNames[i]?.value?.trim() || '';
            const accountOwnerVal = paymentAccountOwners[i]?.value?.trim() || '';
            const depositorNameVal = paymentDepositorNames[i]?.value?.trim() || '';
            const registeredAtVal = paymentRegisteredAts[i]?.value?.trim() || '';
            const depositDatePersian = paymentDepositDates[i]?.value?.trim() || '';
            const checkDueDatePersian = paymentCheckDueDates[i]?.value?.trim() || '';
            const sayadiDateVal = paymentSayadiDates[i]?.value?.trim() || '';
            const checkDeliveryDatePersian = paymentCheckDeliveryDates[i]?.value?.trim() || '';
            const checkRecipientVal = paymentCheckRecipientNames[i]?.value?.trim() || '';
            const receiptImageVal = paymentReceiptImages[i]?.value?.trim() || '';
            const checkImageVal = paymentCheckImages[i]?.value?.trim() || '';
            const amount = parseMoneyValue(amountRaw);
            const toGregOrEmpty = (p) => {
                if (!p) return '';
                try { const g = persianToGregorian(p); return g ? g.split('T')[0] : ''; }
                catch (e) { return ''; }
            };
            // C6: "سایر" destination bank requires destination number + account holder.
            if (amount > 0 && (this.isCardToCardMethod(method) || this.isShebaMethod(method)) && bankVal === '__other__') {
                if (!bankOtherVal || !accountOwnerVal) {
                    Swal.fire('خطا', `برای انتخاب «سایر» در بانک مقصد، شماره بانک مقصد و نام صاحب حساب الزامی است (ردیف ${i + 1})`, 'error');
                    return;
                }
            }
            // C7: cheque requires source bank, owner, number, Sayadi date. The cheque due date
            // (تاریخ سررسید) is captured by the top date field (persianDate) for cheques.
            if (amount > 0 && this.isCheckMethod(method)) {
                if (!checkBankVal || !checkOwnerVal || !checkNumberVal || !sayadiDateVal || !persianDate) {
                    Swal.fire('خطا', `برای چک، نام بانک مبدا، نام صاحب چک، شماره چک، تاریخ صیادی و تاریخ سررسید چک الزامی است (ردیف ${i + 1})`, 'error');
                    return;
                }
            }
	            
	            // Only add payment if date and amount are provided
	            if (persianDate && amount > 0) {
	                let gregorianDate = '';
	                // Convert Persian date to Gregorian
	                if (persianDate) {
	                    try {
	                        const gDateStr = persianToGregorian(persianDate);
	                        if (gDateStr) {
	                            // Extract just the date part (YYYY-MM-DD)
	                            gregorianDate = gDateStr.split('T')[0];
	                        }
	                    } catch(e) {
	                        console.error('Error converting date:', e, 'Date:', persianDate);
	                    }
	                }
	                
	                payments.push({
	                    date: gregorianDate || '',
	                    date_persian: persianDate || '',
	                    amount,
                    method: method || '',
                    description: description || '',
                    card_last4: cardLast4 || '',
                    destination_bank: bankVal || '',
                    destination_bank_other: bankOtherVal || '',
                    account_owner_name: accountOwnerVal || '',
                    depositor_name: depositorNameVal || '',
                    destination_card_number: destinationCardDigits || '',
                    destination_card_number_other: '',
                    destination_bank_title: destinationBankTitleVal || '',
                    destination_card_full: destinationCardFullVal || '',
                    destination_account_number: destinationAccountVal || '',
                    destination_sheba: destinationShebaVal || '',
                    payment_time: paymentTimeVal || '',
                    card_reader_device: cardReaderDeviceVal || '',
                    currency: currencyVal || '',
                    currency_other: currencyOtherVal || '',
                    currency_amount: currencyAmountVal || '',
                    check_bank_name: checkBankVal || '',
                    check_date: checkDateVal || '',
                    check_number: checkNumberVal || '',
                    check_owner_name: checkOwnerVal || '',
                    registered_at: registeredAtVal || '',
                    deposit_date_persian: depositDatePersian || '',
                    deposit_date: toGregOrEmpty(depositDatePersian),
                    check_due_date_persian: this.isCheckMethod(method) ? (persianDate || '') : (checkDueDatePersian || ''),
                    check_due_date: this.isCheckMethod(method) ? (gregorianDate || toGregOrEmpty(persianDate)) : toGregOrEmpty(checkDueDatePersian),
                    sayadi_date: sayadiDateVal || '',
                    check_delivery_date_persian: checkDeliveryDatePersian || '',
                    check_delivery_date: toGregOrEmpty(checkDeliveryDatePersian),
                    check_recipient_name: checkRecipientVal || '',
                    receipt_image_path: receiptImageVal || '',
                    check_image_path: checkImageVal || ''
                });
            }
        }

        // Calculate total payment amount
        const totalPaymentAmount = payments.reduce((sum, p) => sum + (p.amount || 0), 0);
        if ((dealStatus === 'won' || stageMeansSuccess) && successRequiredAmount > 0 && totalPaymentAmount + 0.0001 < successRequiredAmount) {
            Swal.fire('خطا', 'برای موفق شدن معامله، مجموع پرداخت‌ها باید حداقل برابر مبلغ قابل پرداخت باشد', 'error');
            return;
        }
        
        // Prepare failure reason fields
        let failureReasonCode = '';
        let failureReasonText = '';
        let failureReasonDescription = '';
        if (dealStatus === 'lost') {
            const selectedReason = form.failure_reason.value;
            failureReasonCode = selectedReason || '';
            failureReasonDescription = (form.failure_other_description.value || '').trim();
            const reasonText = {
                'no_financial_ability': 'عدم توان مالی',
                'no_purchase_intent': 'فعلا قصد خرید ندارد',
                'competitor_purchase': 'خرید از رقبا',
                'wrong_number': 'شماره اشتباه',
                'irrelevant_lead': 'لید نامرتبط',
                'other': 'سایر'
            };
            failureReasonText = reasonText[selectedReason] || '';
            if (!failureReasonCode) {
                Swal.fire('خطا', 'لطفاً دلیل ناموفق شدن را انتخاب کنید', 'error');
                return;
            }
            if (!failureReasonDescription) {
                Swal.fire('خطا', 'توضیحات دلیل ناموفق شدن الزامی است', 'error');
                return;
            }
        }
        
	        const data = {
	            deal_id: dealId || '',
	            contact_id: contactId,
	            deal_status: dealStatus, // pending, won, lost
	            pipeline_stage: pipelineStage,
	            product_ids: productIds.join(','),
	            title: form.title.value, // عنوان معامله = خدمات قابل ارائه
	            service_cost: parseMoneyValue(form.service_cost.value) || 0,
	            price_list_code: form.price_list_code.value,
	            has_discount: form.has_discount.value,
	            discount_type: discountType,
	            discount_occasion: discountOccasion,
	            discount_amount: discountAmount,
	            refund_amount: refundAmount,
	            refund_description: refundDescription,
	            payable_amount: parseMoneyValue(form.payable_amount.value) || 0,
	            payment_conditions: form.payment_conditions.value,
                acquaintance_source: form.acquaintance_source?.value || '',
                acquaintance_detail: form.acquaintance_detail?.value || '',
                content_topic: form.content_topic?.value || '',
	            payments: JSON.stringify(payments), // Convert to JSON string for FormData
	            payment_amount: totalPaymentAmount, // Total for backward compatibility
                admin_unlock_paid_deal_fields: this.state.dealInfoAdminUnlocked ? '1' : '0',
	            failure_reason: failureReasonCode,
	            failure_reason_text: failureReasonText,
	            failure_other_description: failureReasonDescription
	        };

        // Include requested services from activities tab (hidden field JSON)
        const reqServicesHidden = document.getElementById('deal-activity-requested-services');
        if (reqServicesHidden && reqServicesHidden.value) {
            data.requested_services = reqServicesHidden.value;
        }

        // Include مالی fields (financial_level, asset_estimation, income_estimation, has_previous_purchase)
        const finLevel = document.getElementById('deal-activity-financial-level')?.value || '';
        const assetEst = document.getElementById('deal-activity-asset')?.value || '';
        const incomeEst = document.getElementById('deal-activity-income')?.value || '';
        const hasPurchase = document.getElementById('deal-activity-has-purchase')?.value || '';
        if (finLevel) data.financial_level = finLevel;
        if (assetEst) data.asset_estimation = assetEst;
        if (incomeEst) data.income_estimation = incomeEst;
        if (hasPurchase !== '') data.has_previous_purchase = hasPurchase;

        const titleVal = form.title.value?.trim();
        data.requested_services = titleVal ? JSON.stringify([titleVal]) : '[]';
        if (reqServicesHidden) reqServicesHidden.value = data.requested_services;
        
        console.log('Sending payments to backend:', payments);

        // COMPREHENSIVE LOGGING BEFORE SENDING DEAL SAVE REQUEST
        console.log('[DEAL_SAVE_REQUEST] Sending deal save request to backend', {
            timestamp: new Date().toISOString(),
            data: data,
            contactId: data.contact_id,
            dealId: data.deal_id,
            title: data.title,
            windowCurrentContactId: window.currentContactId,
            paymentsCount: Array.isArray(data.payments) ? data.payments.length : 0,
            formTitle: form.title?.value,
            formContactId: form.contact_id?.value,
            url: window.location.href,
            sessionId: this.getSessionId ? this.getSessionId() : 'unknown'
        });

        Swal.showLoading();
        const res = await this.req('save_deal', data);
        Swal.close();

        // LOGGING DEAL SAVE RESPONSE
        console.log('[DEAL_SAVE_RESPONSE] Deal save response received', {
            timestamp: new Date().toISOString(),
            status: res.status,
            message: res.message,
            deal_id: res.deal_id,
            contact_id: data.contact_id,
            original_deal_id: data.deal_id,
            success: res.status === 'success'
        });

        if(res.status === 'success') {
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title: dealId ? 'معامله به‌روزرسانی شد' : 'معامله ثبت شد', showConfirmButton:false, timer:2000});
            
            // Update deal-id if it was a new deal
            const currentDealId = document.getElementById('deal-id').value;
            if (!currentDealId && res.deal_id) {
                document.getElementById('deal-id').value = res.deal_id;
            }
            
            // Close modal and reload
            this.closeModal('modalDeal');
            if (window.currentContactId) {
                await this.openPersonProfile(window.currentContactId);
            }
            if (document.getElementById('deals-container')) {
                await this.loadDeals();
            }
        } else {
            const mismatchMsg = 'شناسه معامله با لید انتخاب‌شده مطابقت ندارد';
            if ((res.message || '').includes(mismatchMsg)) {
                console.warn('[DEAL_SAVE_BLOCKED] Deal/contact mismatch detected; clearing deal context', {
                    timestamp: new Date().toISOString(),
                    contact_id: data.contact_id,
                    deal_id: data.deal_id,
                    message: res.message
                });
                const dealIdInput = document.getElementById('deal-id');
                if (dealIdInput) dealIdInput.value = '';
                const dealActDeal = document.getElementById('deal-activity-deal-id');
                if (dealActDeal) dealActDeal.value = '';
                const activityDealId = document.getElementById('activity-deal-id');
                if (activityDealId) activityDealId.value = '';
                this.state.currentDeal = null;
            }
            console.error('[DEAL_SAVE_ERROR] Deal save failed', {
                timestamp: new Date().toISOString(),
                error: res.message,
                status: res.status,
                contact_id: data.contact_id,
                deal_id: data.deal_id,
                title: data.title,
                fullResponse: res
            });
            Swal.fire('خطا', res.message, 'error');
        }
    },

    saveDealFromTab(tab) {
        const form = document.getElementById('deal-form');
        if (!form) return;
        this.state.activeDealTab = tab;
        return this.saveDeal({ preventDefault: () => {}, target: form });
    },
    
    // MARK DEAL AS SUCCESS
    async markDealAsSuccess() {
        const dealId = document.getElementById('deal-id').value;
        const contactId = document.getElementById('deal-contact-id').value;
        
        if (!dealId) {
            Swal.fire('خطا', 'لطفاً ابتدا معامله را ذخیره کنید', 'error');
            return;
        }
        
        Swal.fire({
            title: 'تایید معامله موفق',
            text: 'آیا از موفق بودن این معامله اطمینان دارید؟',
            icon: 'question',
            showCancelButton: true,
            confirmButtonText: 'بله، معامله موفق است',
            cancelButtonText: 'انصراف',
            confirmButtonColor: '#10b981'
        }).then(async (result) => {
            if (result.isConfirmed) {
                Swal.showLoading();
                const res = await this.req('set_deal_status', {
                    deal_id: dealId,
                    status: '1', // Won
                    contact_id: contactId
                });
                Swal.close();
                
                if (res.status === 'success') {
                    Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'معامله به عنوان موفق ثبت شد', showConfirmButton:false, timer:2000});
                    this.closeModal('modalDeal');
                    if (window.currentContactId) {
                        this.openPersonProfile(window.currentContactId);
                    }
                    if (document.getElementById('deals-container')) {
                        this.loadDeals();
                    }
                } else {
                    Swal.fire('خطا', res.message, 'error');
                }
            }
        });
    },
    
    // MARK DEAL AS FAILED
    markDealAsFailed() {
        const dealId = document.getElementById('deal-id').value;
        const contactId = document.getElementById('deal-contact-id').value;
        
        if (!dealId) {
            Swal.fire('خطا', 'لطفاً ابتدا معامله را ذخیره کنید', 'error');
            return;
        }
        
        document.getElementById('failed-deal-id').value = dealId;
        document.getElementById('failed-contact-id').value = contactId;
        document.getElementById('failure-reason').value = '';
        document.getElementById('failure-other-description').value = '';
        document.getElementById('failure-other-reason').classList.add('hidden');
        this.openModal('modalDealFailed');
    },
    
	    
    // CONFIRM DEAL FAILED
    async confirmDealFailed(e) {
        e.preventDefault();
        
        const dealId = document.getElementById('failed-deal-id').value;
        const contactId = document.getElementById('failed-contact-id').value;
        const reason = document.getElementById('failure-reason').value;
        const otherDesc = document.getElementById('failure-other-description').value;
        
	        // Map reason to Persian text
	        const reasonText = {
            'no_financial_ability': 'عدم توان مالی',
            'no_purchase_intent': 'فعلا قصد خرید ندارد',
            'competitor_purchase': 'خرید از رقبا',
            'wrong_number': 'شماره اشتباه',
            'irrelevant_lead': 'لید نامرتبط',
            'other': 'سایر'
        };
        
        Swal.showLoading();
        if (!reason) {
            Swal.fire('خطا', 'لطفاً دلیل ناموفق شدن را انتخاب کنید', 'error');
            return;
        }
        // For consistency with deal form, always require description
        if (!otherDesc.trim()) {
            Swal.fire('خطا', 'لطفاً توضیحات را وارد کنید', 'error');
            return;
        }

        const res = await this.req('set_deal_status', {
            deal_id: dealId,
            status: '2', // Lost
            contact_id: contactId,
            failure_reason: reason,
            failure_reason_text: reasonText[reason] || '',
            failure_other_description: otherDesc
        });
        Swal.close();
        
        if (res.status === 'success') {
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'معامله به عنوان ناموفق ثبت شد', showConfirmButton:false, timer:2000});
            this.closeModal('modalDealFailed');
            this.closeModal('modalDeal');
            if (window.currentContactId) {
                this.openPersonProfile(window.currentContactId);
            }
            if (document.getElementById('deals-container')) {
                this.loadDeals();
            }
        } else {
            Swal.fire('خطا', res.message, 'error');
        }
    },
    
    // DELETE DEAL (admin only)
    async deleteDeal(dealId) {
        if (!this.canActionClient('deals.delete')) {
            Swal.fire('خطا', 'دسترسی به حذف معامله ندارید', 'error');
            return;
        }
        
        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }
        
        const result = await Swal.fire({
            title: 'آیا مطمئن هستید؟',
            text: 'این معامله به طور دائم حذف خواهد شد',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#3085d6',
            confirmButtonText: 'بله، حذف کن',
            cancelButtonText: 'لغو',
            reverseButtons: true
        });
        
        if (!result.isConfirmed) {
            return;
        }
        
        Swal.fire({
            title: 'در حال حذف...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('delete_deal', { deal_id: dealId });
            Swal.close();
            
            if (res.status === 'success') {
                Swal.fire('موفق', res.message || 'معامله با موفقیت حذف شد', 'success');
                
                // Refresh deals list
                if (document.getElementById('deals-container')) {
                    this.loadDeals();
                }
                
                // Refresh person profile if open
                if (window.currentContactId) {
                    this.openPersonProfile(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در حذف معامله', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در حذف معامله: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    // REACTIVATE DEAL (admin only)
    async reactivateDeal(dealId) {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند معاملات بسته شده را فعال کند', 'error');
            return;
        }

        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }

        // نمایش modal برای انتخاب مرحله کاریز جدید
        const { value: formData } = await Swal.fire({
            title: 'فعال‌سازی مجدد معامله',
            html: `
                <div class="text-right mb-4">
                    <p class="text-sm text-gray-600 mb-3">مرحله کاریز جدید را انتخاب کنید:</p>
                    <select id="reactivate-stage" class="swal2-input w-full text-right">
                        <option value="">-- انتخاب مرحله کاریز --</option>
                        <option value="تماس پیگیری">تماس پیگیری</option>
                        <option value="ارسال لینک پرداخت">ارسال لینک پرداخت</option>
                        <option value="پرداخت">پرداخت</option>
                        <option value="پیگیری مانده حساب">پیگیری مانده حساب</option>
                        <option value="برگزاری دوره">برگزاری دوره</option>
                        <option value="پشتیبانی">پشتیبانی</option>
                        <option value="ارجاع به CRM">ارجاع به CRM</option>
                    </select>
                </div>
                <div class="text-right">
                    <label class="block text-sm text-gray-600 mb-2">دلیل فعال‌سازی مجدد (اختیاری):</label>
                    <textarea id="reactivate-reason" class="swal2-textarea w-full text-right" placeholder="توضیح دهید چرا این معامله را فعال می‌کنید..." rows="3"></textarea>
                </div>
            `,
            showCancelButton: true,
            confirmButtonText: 'فعال‌سازی',
            cancelButtonText: 'لغو',
            confirmButtonColor: '#10b981',
            cancelButtonColor: '#6b7280',
            preConfirm: () => {
                const stage = document.getElementById('reactivate-stage').value;
                const reason = document.getElementById('reactivate-reason').value;

                if (!stage) {
                    Swal.showValidationMessage('مرحله کاریز جدید را انتخاب کنید');
                    return false;
                }

                return { stage, reason };
            }
        });

        if (!formData) return;

        // نمایش loading
        Swal.fire({
            title: 'در حال فعال‌سازی...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('deal/reactivate', {
                deal_id: dealId,
                new_pipeline_stage: formData.stage,
                reactivation_reason: formData.reason
            });
            Swal.close();

            if (res.status === 'success') {
                Swal.fire({
                    title: 'موفق',
                    text: res.message || 'معامله با موفقیت فعال شد',
                    icon: 'success',
                    confirmButtonText: 'باشه'
                });

                // بروزرسانی لیست معاملات
                if (document.getElementById('deals-container')) {
                    this.loadDeals();
                }

                // بروزرسانی پروفایل شخص اگر باز است
                if (window.currentContactId) {
                    this.openPersonProfile(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در فعال‌سازی معامله', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در فعال‌سازی معامله: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    // DELETE DEAL FROM MODAL (admin only)
    async deleteDealFromModal() {
        const dealId = document.getElementById('deal-id').value;
        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }

        const result = await Swal.fire({
            title: 'آیا مطمئن هستید؟',
            text: 'این معامله به طور دائم حذف خواهد شد و قابل بازیابی نیست',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#6b7280',
            confirmButtonText: 'بله، حذف کن',
            cancelButtonText: 'لغو',
            reverseButtons: true
        });

        if (!result.isConfirmed) {
            return;
        }

        Swal.fire({
            title: 'در حال حذف...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('delete_deal', { deal_id: dealId });
            Swal.close();

            if (res.status === 'success') {
                Swal.fire({
                    title: 'موفق',
                    text: res.message || 'معامله با موفقیت حذف شد',
                    icon: 'success',
                    confirmButtonText: 'باشه'
                });

                // بستن modal و بروزرسانی لیست‌ها
                this.closeModal('modalDeal');
                if (document.getElementById('deals-container')) {
                    this.loadDeals();
                }
                if (window.currentContactId) {
                    this.openPersonProfile(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در حذف معامله', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در حذف معامله: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    // REACTIVATE DEAL FROM MODAL (admin only)
    async reactivateDealFromModal() {
        const dealId = document.getElementById('deal-id').value;
        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }

        // نمایش modal انتخاب مرحله کاریز
        const { value: formData } = await Swal.fire({
            title: 'فعال‌سازی مجدد معامله',
            html: `
                <div class="text-right mb-4">
                    <p class="text-sm text-gray-600 mb-3">مرحله کاریز جدید را انتخاب کنید:</p>
                    <select id="reactivate-stage" class="swal2-input w-full text-right">
                        <option value="">-- انتخاب مرحله کاریز --</option>
                        <option value="تماس پیگیری">تماس پیگیری</option>
                        <option value="ارسال لینک پرداخت">ارسال لینک پرداخت</option>
                        <option value="پرداخت">پرداخت</option>
                        <option value="پیگیری مانده حساب">پیگیری مانده حساب</option>
                        <option value="برگزاری دوره">برگزاری دوره</option>
                        <option value="پشتیبانی">پشتیبانی</option>
                        <option value="ارجاع به CRM">ارجاع به CRM</option>
                    </select>
                </div>
                <div class="text-right">
                    <label class="block text-sm text-gray-600 mb-2">دلیل فعال‌سازی مجدد (اختیاری):</label>
                    <textarea id="reactivate-reason" class="swal2-textarea w-full text-right" placeholder="توضیح دهید چرا این معامله را فعال می‌کنید..." rows="3"></textarea>
                </div>
            `,
            showCancelButton: true,
            confirmButtonText: 'فعال‌سازی',
            cancelButtonText: 'لغو',
            confirmButtonColor: '#10b981',
            cancelButtonColor: '#6b7280',
            preConfirm: () => {
                const stage = document.getElementById('reactivate-stage').value;
                const reason = document.getElementById('reactivate-reason').value;

                if (!stage) {
                    Swal.showValidationMessage('مرحله کاریز جدید را انتخاب کنید');
                    return false;
                }

                return { stage, reason };
            }
        });

        if (!formData) return;

        // نمایش loading
        Swal.fire({
            title: 'در حال فعال‌سازی...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('deal/reactivate', {
                deal_id: dealId,
                new_pipeline_stage: formData.stage,
                reactivation_reason: formData.reason
            });
            Swal.close();

            if (res.status === 'success') {
                Swal.fire({
                    title: 'موفق',
                    text: res.message || 'معامله با موفقیت فعال شد',
                    icon: 'success',
                    confirmButtonText: 'باشه'
                });

                // بروزرسانی وضعیت در modal و لیست‌ها
                this.closeModal('modalDeal');
                if (document.getElementById('deals-container')) {
                    this.loadDeals();
                }
                if (window.currentContactId) {
                    this.openPersonProfile(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در فعال‌سازی معامله', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در فعال‌سازی معامله: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    // REACTIVATE DEAL FROM PROFILE (admin only)
    async reactivateDealFromProfile(dealId) {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند معاملات بسته شده را فعال کند', 'error');
            return;
        }

        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }

        // نمایش modal انتخاب مرحله کاریز
        const { value: formData } = await Swal.fire({
            title: 'فعال‌سازی مجدد معامله',
            html: `
                <div class="text-right mb-4">
                    <p class="text-sm text-gray-600 mb-3">مرحله کاریز جدید را انتخاب کنید:</p>
                    <select id="reactivate-stage" class="swal2-input w-full text-right">
                        <option value="">-- انتخاب مرحله کاریز --</option>
                        <option value="تماس پیگیری">تماس پیگیری</option>
                        <option value="ارسال لینک پرداخت">ارسال لینک پرداخت</option>
                        <option value="پرداخت">پرداخت</option>
                        <option value="پیگیری مانده حساب">پیگیری مانده حساب</option>
                        <option value="برگزاری دوره">برگزاری دوره</option>
                        <option value="پشتیبانی">پشتیبانی</option>
                        <option value="ارجاع به CRM">ارجاع به CRM</option>
                    </select>
                </div>
                <div class="text-right">
                    <label class="block text-sm text-gray-600 mb-2">دلیل فعال‌سازی مجدد (اختیاری):</label>
                    <textarea id="reactivate-reason" class="swal2-textarea w-full text-right" placeholder="توضیح دهید چرا این معامله را فعال می‌کنید..." rows="3"></textarea>
                </div>
            `,
            showCancelButton: true,
            confirmButtonText: 'فعال‌سازی',
            cancelButtonText: 'لغو',
            confirmButtonColor: '#10b981',
            cancelButtonColor: '#6b7280',
            preConfirm: () => {
                const stage = document.getElementById('reactivate-stage').value;
                const reason = document.getElementById('reactivate-reason').value;

                if (!stage) {
                    Swal.showValidationMessage('مرحله کاریز جدید را انتخاب کنید');
                    return false;
                }

                return { stage, reason };
            }
        });

        if (!formData) return;

        // نمایش loading
        Swal.fire({
            title: 'در حال فعال‌سازی...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('deal/reactivate', {
                deal_id: dealId,
                new_pipeline_stage: formData.stage,
                reactivation_reason: formData.reason
            });
            Swal.close();

            if (res.status === 'success') {
                Swal.fire({
                    title: 'موفق',
                    text: res.message || 'معامله با موفقیت فعال شد',
                    icon: 'success',
                    confirmButtonText: 'باشه'
                });

                // بروزرسانی پروفایل شخص
                if (window.currentContactId) {
                    this.openPersonProfile(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در فعال‌سازی معامله', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در فعال‌سازی معامله: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    async forceSuccessExitKarizFromModal() {
        const dealId = document.getElementById('deal-id')?.value || '';
        if (!dealId) {
            Swal.fire('خطا', 'شناسه معامله مشخص نشده است', 'error');
            return;
        }

        const currentDeal = this.state.currentDeal || {};
        const payableAmount = Number(currentDeal.payable_amount || 0);
        const paymentAmount = Number(currentDeal.payment_amount || 0);
        const outstandingAmount = Math.max(payableAmount - paymentAmount, 0);
        if (currentDeal.status !== 'Won') {
            Swal.fire('خطا', 'این عملیات فقط برای معاملات موفق مجاز است', 'error');
            return;
        }
        if (outstandingAmount > 0.0001) {
            Swal.fire('خطا', 'برای معامله‌ای که مجموع پرداخت‌های آن کمتر از مبلغ قابل پرداخت است، این عملیات مجاز نیست', 'error');
            return;
        }

        const result = await Swal.fire({
            title: 'تایید موفق و خروج از کاریز',
            text: 'این عملیات وضعیت معامله را موفق، مرحله کاریز را اتمام و مخاطب را از کاریز خارج می‌کند.',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'اجرا',
            cancelButtonText: 'لغو',
            confirmButtonColor: '#10b981'
        });
        if (!result.isConfirmed) {
            return;
        }

        Swal.fire({
            title: 'در حال اعمال...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await this.req('deal/force_success_exit_kariz', { deal_id: dealId });
            Swal.close();

            if (res.status !== 'success') {
                Swal.fire('خطا', res.message || 'خطا در اعمال عملیات', 'error');
                return;
            }

            Swal.fire('موفق', res.message || 'عملیات با موفقیت انجام شد', 'success');
            await this.openDealModal(dealId);
            if (document.getElementById('deals-container')) {
                this.loadDeals();
            }
            if (window.currentContactId) {
                this.openPersonProfile(window.currentContactId);
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در اعمال عملیات: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    // DELETE ACTIVITY (admin only)
    async deleteActivity(activityId) {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند فعالیت‌ها را حذف کند', 'error');
            return;
        }
        
        if (!activityId) {
            Swal.fire('خطا', 'شناسه فعالیت مشخص نشده است', 'error');
            return;
        }
        
        const result = await Swal.fire({
            title: 'آیا مطمئن هستید؟',
            text: 'این فعالیت به طور دائم حذف خواهد شد',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#d33',
            cancelButtonColor: '#3085d6',
            confirmButtonText: 'بله، حذف کن',
            cancelButtonText: 'لغو',
            reverseButtons: true
        });
        
        if (!result.isConfirmed) {
            return;
        }
        
        Swal.fire({
            title: 'در حال حذف...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('delete_activity', { activity_id: activityId });
            Swal.close();
            
            if (res.status === 'success') {
                Swal.fire('موفق', res.message || 'فعالیت با موفقیت حذف شد', 'success');
                
                // Refresh person profile if open
                if (window.currentContactId) {
                    this.openPersonProfile(window.currentContactId);
                }
                
                // Refresh activities modal if open
                const activitiesModal = document.getElementById('modalDidarActivities');
                if (activitiesModal && !activitiesModal.classList.contains('hidden')) {
                    this.viewDidarActivities(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در حذف فعالیت', 'error');
            }
        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در حذف فعالیت: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    updateDealModalSummary(person = null, deal = null) {
        const fill = (id, value) => {
            const el = document.getElementById(id);
            if (el) el.textContent = value || '-';
        };
        const stage = deal?.pipeline_stage || document.getElementById('deal-pipeline-stage')?.value || '';
        const statusLabel = (person && typeof person.sale_status !== 'undefined')
            ? (String(person.sale_status) === '1' ? 'موفق' : 'ناموفق')
            : '-';

        fill('deal-summary-name', person ? `${person.first_name || ''} ${person.last_name || ''}`.trim() || (deal?.contact_name || 'بدون نام') : (deal?.contact_name || 'بدون نام'));
        fill('deal-summary-owner', person?.owner_name || deal?.owner_name || this.state.currentLead?.owner_name || APP_USER.name || '---');
        fill('deal-summary-prev-owner', person?.previous_owner_name || '-');
        fill('deal-summary-sale-status', statusLabel);
        fill('deal-summary-mobile', person?.mobile_phone || deal?.contact_mobile || '-');
        fill('deal-summary-mobile2', person?.secondary_mobile_phone || '-');
        fill('deal-summary-mobile3', person?.mobile_phone_3 || '-');
        fill('deal-summary-mobile4', person?.mobile_phone_4 || '-');
        fill('deal-summary-city', person?.city || '-');
        fill('deal-summary-job', person?.job_title || '-');
        fill('deal-summary-didar-id', deal?.didar_deal_id || deal?.id || person?.didar_contact_id || '-');
        fill('deal-summary-didar-id-box', deal?.didar_deal_id || deal?.id || person?.didar_contact_id || '-');
        const createdByWrap = document.getElementById('deal-summary-created-by-wrap');
        const createdViaWrap = document.getElementById('deal-summary-created-via-wrap');
        const createdByValue = String(deal?.created_by_name || '').trim();
        const createdViaValue = this.translateCreatedVia(deal?.created_via || '');
        fill('deal-summary-created-by', createdByValue || '-');
        fill('deal-summary-created-via', createdViaValue || '-');
        if (createdByWrap) {
            createdByWrap.classList.toggle('hidden', !(this.isAdmin() && createdByValue));
        }
        if (createdViaWrap) {
            createdViaWrap.classList.toggle('hidden', !(this.isAdmin() && createdViaValue));
        }

        const stageBadge = document.getElementById('deal-summary-stage');
        if (stageBadge) {
            stageBadge.textContent = stage || 'مرحله تعیین نشده';
        }

        this.updateDealCurrentFields(stage, deal?.status);
    },

    updateDealCurrentFields(stageValue = null, statusValue = null) {
        const statusMap = {
            pending: 'جاری',
            won: 'موفق',
            lost: 'ناموفق',
            Pending: 'جاری',
            Won: 'موفق',
            Lost: 'ناموفق'
        };
        const statusSelect = document.getElementById('deal-status');
        const stageSelect = document.getElementById('deal-pipeline-stage');
        const statusText = statusValue || statusSelect?.value || '';
        const stageText = stageValue || stageSelect?.value || '';
        const statusBox = document.getElementById('deal-current-status');
        const stageBox = document.getElementById('deal-current-stage');
        if (statusBox) {
            statusBox.textContent = statusMap[statusText] || '-';
        }
        if (stageBox) {
            stageBox.textContent = stageText || '-';
        }
    },

    showDealFormTab(tab = 'info') {
        const tabs = ['info', 'activities', 'payments'];
        tabs.forEach(t => {
            const section = document.getElementById(`deal-form-tab-${t}`);
            const btn = document.getElementById(`deal-tab-btn-${t}`);
            if (section) section.classList.toggle('hidden', t !== tab);
            if (btn) {
                if (t === tab) {
                    btn.classList.remove('text-gray-300', 'bg-transparent');
                    btn.classList.add('bg-gray-800', 'text-white');
                } else {
                    btn.classList.add('text-gray-300');
                    btn.classList.remove('bg-gray-800', 'text-white');
                }
            }
        });

        // Buttons are inside each tab; no global save toggle needed
    },

    updateDealActivitiesBadge(count = 0) {
        const badge = document.getElementById('deal-activities-badge');
        if (badge) badge.textContent = count > 99 ? '99+' : count;
    },

    hasCompletePaymentsForSuccess() {
        const collected = this.collectCurrentPayments();
        const cached = this.state.lastPaymentsCache || [];
        const stored = Array.isArray(this.state.currentDeal?.payments) ? this.state.currentDeal.payments : [];
        const list = [...collected, ...cached, ...stored];
	        const valid = list.filter(p => {
	            const amountVal = parseMoneyValue(p.amount || 0);
	            const hasDate = !!(p.date_persian || p.date);
	            const hasMethod = !!(p.method && String(p.method).trim() !== '');
	            return hasDate && hasMethod && amountVal > 0;
	        });
        const result = valid.length > 0;
        console.log('hasCompletePaymentsForSuccess', { collected, cached, stored, validCount: valid.length, result });
        return result;
    },

    getDealSuccessRequiredPaymentAmount(form = null) {
        const payableField = form?.payable_amount || document.getElementById('deal-payable-amount');
        if (payableField) {
            return Math.max(0, parseMoneyValue(payableField.value || 0));
        }

        if (this.state.currentDeal && Object.prototype.hasOwnProperty.call(this.state.currentDeal, 'payable_amount') && this.state.currentDeal.payable_amount !== null) {
            return Math.max(0, parseMoneyValue(this.state.currentDeal.payable_amount || 0));
        }

        return Math.max(0, parseMoneyValue(form?.service_cost?.value || this.state.currentDeal?.service_cost || 0));
    },

    hasEnoughPaymentsForSuccess() {
        const requiredAmount = this.getDealSuccessRequiredPaymentAmount();
        if (requiredAmount <= 0) {
            return true;
        }

        const collected = this.collectCurrentPayments();
        const cached = this.state.lastPaymentsCache || [];
        const stored = Array.isArray(this.state.currentDeal?.payments) ? this.state.currentDeal.payments : [];
        const list = [...collected, ...cached, ...stored];
        const totalPaid = list.reduce((sum, p) => sum + parseMoneyValue(p?.amount || 0), 0);
        const result = totalPaid + 0.0001 >= requiredAmount;
        console.log('hasEnoughPaymentsForSuccess', { requiredAmount, totalPaid, result });
        return result;
    },

        populateDealActivityServices(selected = []) {
        // Keep selected services in state
        this.state.dealActivityRequestedServices = Array.isArray(selected) ? [...new Set(selected.map(String))] : [];

        const select = document.getElementById('deal-activity-service-select');
        if (select) {
            select.innerHTML = '<option value="">-- انتخاب خدمت --</option>';

            const lookupOptions = this.getLookupOptions('allowed_services');
            if (lookupOptions.length) {
                lookupOptions.forEach(service => {
                    const o = document.createElement('option');
                    o.value = service.value;
                    o.textContent = service.label || service.value;
                    select.appendChild(o);
                });
            } else {
                // fallback: read from deal-title select (static services)
                const titleSelect = document.getElementById('deal-title');
                if (titleSelect) {
                    Array.from(titleSelect.options || []).forEach(opt => {
                        if (!opt.value) return;
                        const o = document.createElement('option');
                        o.value = opt.value;
                        o.textContent = opt.textContent;
                        select.appendChild(o);
                    });
                }
            }

            // Try to refresh from API in background (optional)
            this.req('get_allowed_services').then(res => {
                if (res.status === 'success' && res.services && res.services.length > 0) {
                    select.innerHTML = '<option value="">-- انتخاب خدمت --</option>';
                    res.services.forEach(service => {
                        const o = document.createElement('option');
                        o.value = service;
                        o.textContent = service;
                        select.appendChild(o);
                    });
                }
            }).catch(() => {});
        }
        this.renderDealActivityServices();
    },

    renderDealActivityServices() {
        const chips = document.getElementById('deal-activity-service-chips');
        const hidden = document.getElementById('deal-activity-requested-services');
        if (!chips || !hidden) return;
        const items = Array.isArray(this.state.dealActivityRequestedServices) ? this.state.dealActivityRequestedServices : [];
        chips.innerHTML = '';
        items.forEach(service => {
            const chip = document.createElement('span');
            chip.className = 'inline-flex items-center gap-1 px-2 py-1 rounded-full bg-gray-800 text-gray-100 text-xs border border-gray-700';
            chip.textContent = service;
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.className = 'text-red-400 hover:text-red-200 ml-1';
            btn.innerHTML = '&times;';
            btn.onclick = () => this.removeDealActivityService(service);
            chip.appendChild(btn);
            chips.appendChild(chip);
        });
        hidden.value = JSON.stringify(items);
    },

    addDealActivityService() {
        if (this.state.dealHasActivity) {
            Swal.fire('خطا', 'پس از ثبت اولین فعالیت امکان ویرایش خدمات نیست', 'error');
            return;
        }
        const select = document.getElementById('deal-activity-service-select');
        if (!select) return;
        const value = select.value;
        if (!value) return;
        this.state.dealActivityRequestedServices = Array.isArray(this.state.dealActivityRequestedServices)
            ? this.state.dealActivityRequestedServices
            : [];
        if (!this.state.dealActivityRequestedServices.includes(value)) {
            this.state.dealActivityRequestedServices.push(value);
            this.renderDealActivityServices();
        }
        select.value = '';
    },

    removeDealActivityService(service) {
        if (this.state.dealHasActivity) {
            Swal.fire('خطا', 'پس از ثبت اولین فعالیت امکان حذف خدمات نیست', 'error');
            return;
        }
        if (!Array.isArray(this.state.dealActivityRequestedServices)) return;
        this.state.dealActivityRequestedServices = this.state.dealActivityRequestedServices.filter(s => s !== service);
        this.renderDealActivityServices();
    },

    toggleAcqLessThanMonth() {
        const less = document.getElementById('deal-activity-acq-less');
        const years = document.getElementById('deal-activity-acq-years');
        const months = document.getElementById('deal-activity-acq-months');
        if (!less || !years || !months) return;
        if (less.checked) {
            years.disabled = true;
            months.disabled = true;
            years.value = '0';
            months.value = '0';
            years.required = false;
            months.required = false;
        } else {
            years.disabled = false;
            months.disabled = false;
            years.required = true;
            months.required = true;
        }
    },

    // Show/hide initial info fields (city/job/financial/etc.) to avoid re-entry in deals
    toggleInitialInfoVisibility(show = true) {
        // initial info fields removed from deal modal; keep no-op for compatibility
        return;
    },

    lockDealActivityExtraFields(values = {}) {
        const setReadonly = (el, condition) => {
            if (!el) return;
            if (condition && String(condition).trim() !== '') {
                if ('readOnly' in el) el.readOnly = true;
                if ('disabled' in el && el.tagName === 'SELECT') el.disabled = true;
                el.classList.add('bg-gray-900', 'cursor-not-allowed');
                el.dataset.locked = 'true';
            } else {
                if ('readOnly' in el) el.readOnly = false;
                if ('disabled' in el && el.tagName === 'SELECT') el.disabled = false;
                el.classList.remove('bg-gray-900', 'cursor-not-allowed');
                el.dataset.locked = '';
            }
        };

        setReadonly(document.getElementById('deal-activity-city'), values.city);
        setReadonly(document.getElementById('deal-activity-job'), values.job_title);
        setReadonly(document.getElementById('deal-activity-job-desc'), values.job_description);
        setReadonly(document.getElementById('deal-activity-extra-info'), values.extra_info);

        const fin = document.getElementById('deal-activity-financial-level');
        const asset = document.getElementById('deal-activity-asset');
        const income = document.getElementById('deal-activity-income');
        setReadonly(fin, values.financial_level);
        setReadonly(asset, values.asset_estimation);
        setReadonly(income, values.income_estimation);

        // Requested services should stay editable
        const svcSelect = document.getElementById('deal-activity-service-select');
        const svcAdd = document.getElementById('deal-activity-service-add');
        if (svcSelect) svcSelect.disabled = false;
        if (svcAdd) svcAdd.disabled = false;

        // خرید قبلی اگر تعیین شده باشد قفل شود
        const purchaseSelect = document.getElementById('deal-activity-has-purchase');
        const purchaseLocked = values.has_previous_purchase === 0 || values.has_previous_purchase === 1 || values.has_previous_purchase === '0' || values.has_previous_purchase === '1';
        if (purchaseSelect) {
            if (purchaseLocked) {
                purchaseSelect.value = String(values.has_previous_purchase);
                purchaseSelect.disabled = true;
                purchaseSelect.dataset.locked = 'true';
                purchaseSelect.classList.add('bg-gray-900', 'cursor-not-allowed');
            } else {
                // For new deals or when no previous purchase value exists, keep field enabled
                purchaseSelect.disabled = false;
                purchaseSelect.dataset.locked = '';
                purchaseSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
            }
        }

        // Acquaintance duration
        const acqLess = document.getElementById('deal-activity-acq-less');
        const acqYears = document.getElementById('deal-activity-acq-years');
        const acqMonths = document.getElementById('deal-activity-acq-months');
        if (acqLess && acqYears && acqMonths) {
            const locked = values.acquaintance_duration && String(values.acquaintance_duration).trim() !== '';
            acqLess.disabled = locked;
            acqYears.disabled = locked || acqLess.checked;
            acqMonths.disabled = locked || acqLess.checked;
            acqLess.dataset.locked = locked ? 'true' : '';
            acqYears.dataset.locked = locked ? 'true' : '';
            acqMonths.dataset.locked = locked ? 'true' : '';
            if (locked) {
                acqYears.classList.add('cursor-not-allowed', 'bg-gray-900');
                acqMonths.classList.add('cursor-not-allowed', 'bg-gray-900');
            } else {
                acqYears.classList.remove('cursor-not-allowed', 'bg-gray-900');
                acqMonths.classList.remove('cursor-not-allowed', 'bg-gray-900');
            }
        }
    },

    populateDealActivityStages() {
        const src = document.getElementById('deal-pipeline-stage');
        const dest = document.getElementById('deal-activity-stage');
        if (!src || !dest) return;
        const current = dest.value;
        dest.innerHTML = '';
        const addOpt = (val, text) => {
            const o = document.createElement('option');
            o.value = val;
            o.textContent = text;
            dest.appendChild(o);
        };
        addOpt('', '-- انتخاب مرحله کاریز فروش --');
        Array.from(src.options || []).forEach(opt => {
            if (!opt.value) return;
            if (opt.value === 'بلاتکلیف' || opt.value === 'معامله جدید') return;
            addOpt(opt.value, opt.textContent);
        });
        if (!Array.from(dest.options).some(o => o.value === 'deal_failed')) {
            addOpt('deal_failed', 'معامله ناموفق');
        }
        if (!Array.from(dest.options).some(o => o.value === 'deal_success')) {
            addOpt('deal_success', 'معامله موفق');
        }

        dest.onchange = () => {
            this.state.activityStagePrev = dest.value;
            this.toggleDealActivityFailure();
            this.handleActivityStageChange(dest.value);
        };
        this.state.activityStagePrev = current || '';
        dest.value = current;
        this.toggleDealActivityFailure();
    },

    toggleDealActivityFailure() {
        const stage = document.getElementById('activity-virtual-stage')?.value || document.getElementById('deal-activity-stage')?.value || '';
        const failureBox = document.getElementById('failure-reason-extra');
        const reason = document.getElementById('activity-failure-reason');
        const otherBox = document.getElementById('activity-failure-other-reason');
        const otherDesc = document.getElementById('activity-failure-other-description');
        const stageMeta = this.getLookupMeta('activity_stages', stage);
        const showFailureFromMeta = stageMeta.show_failure_reason === true || stageMeta.showFailureReason === true;
        const required = stage === 'deal_failed' || stage === 'معامله ناموفق' || showFailureFromMeta;

        if (failureBox) failureBox.classList.toggle('hidden', !required);
        if (reason) {
            reason.required = required;
            if (!required) reason.value = '';
        }
        if (otherBox) otherBox.classList.toggle('hidden', !required);
        if (otherDesc) {
            otherDesc.required = required;
            if (!required) {
                otherDesc.value = '';
            } else {
                otherDesc.placeholder = 'لطفاً توضیحات دلیل عدم موفقیت را وارد کنید';
            }
        }

        this.toggleActivityFailureOtherReason();
    },

    toggleDealActivityFailureOther() {
        const reason = document.getElementById('deal-activity-failure-reason')?.value || '';
        const otherBox = document.getElementById('deal-activity-failure-other');
        const otherDesc = document.getElementById('deal-activity-failure-other-description');
        const show = reason === 'other';
        if (otherBox) otherBox.classList.toggle('hidden', !show);
        if (otherDesc) {
            otherDesc.required = show;
            if (!show) otherDesc.value = '';
        }
    },

    async saveDealActivity(e) {
        e.preventDefault();
        if (this.state.dealHasActivity) {
            Swal.fire('خطا', 'برای این معامله قبلاً فعالیت ثبت شده است', 'error');
            return;
        }
        const contactId = document.getElementById('deal-activity-contact-id')?.value || window.currentContactId || '';
        const dealId = document.getElementById('deal-activity-deal-id')?.value || document.getElementById('deal-id')?.value || '';
        const stage = document.getElementById('activity-virtual-stage')?.value || document.getElementById('deal-activity-stage')?.value || '';
        const note = document.getElementById('deal-activity-result-note')?.value || '';
        // Requested services from state/hidden input
        let services = Array.isArray(this.state.dealActivityRequestedServices) ? this.state.dealActivityRequestedServices : [];
        if (!services.length) {
            const hiddenServices = document.getElementById('deal-activity-requested-services')?.value;
            try {
                const parsed = JSON.parse(hiddenServices || '[]');
                if (Array.isArray(parsed)) services = parsed;
            } catch (err) {
                console.warn('Could not parse deal activity services', err);
            }
        }
        const financialLevel = document.getElementById('deal-activity-financial-level')?.value || '';
        const asset = document.getElementById('deal-activity-asset')?.value || '';
        const income = document.getElementById('deal-activity-income')?.value || '';
        const isDone = document.getElementById('deal-activity-is-done')?.checked ? '1' : '0';
        const failureReason = document.getElementById('deal-activity-failure-reason')?.value || '';
        const failureOther = document.getElementById('deal-activity-failure-other-description')?.value || '';
        const activityType = document.getElementById('deal-activity-type-select')?.value || '';
        const direction = document.getElementById('deal-activity-direction')?.value || 'outgoing';
        const hasPurchaseOther = document.getElementById('deal-activity-has-purchase')?.value || '';
        const prevPurchaseNumber = document.getElementById('deal-activity-previous-number')?.value || '';
        const city = document.getElementById('deal-activity-city')?.value || '';
        const jobTitle = document.getElementById('deal-activity-job')?.value || '';
        const jobDescription = document.getElementById('deal-activity-job-desc')?.value || '';
        const extraInfo = document.getElementById('deal-activity-extra-info')?.value || '';
        const acqLess = document.getElementById('deal-activity-acq-less')?.checked;
        const acqYears = document.getElementById('deal-activity-acq-years')?.value || '0';
        const acqMonths = document.getElementById('deal-activity-acq-months')?.value || '0';
        const acquaintanceDuration = acqLess ? 'کمتر از 1 ماه' : `${acqYears} سال ${acqMonths} ماه`;
        const hasPreviousPurchaseVal = document.getElementById('deal-activity-has-purchase')?.value ?? '';

        if (!contactId) {
            Swal.fire('خطا', 'شناسه لید خالی است', 'error');
            return;
        }
        if (!dealId) {
            console.warn('saveDealActivity blocked: empty dealId', {contactId});
            Swal.fire('خطا', 'ابتدا معامله را ذخیره کنید، سپس فعالیت ثبت کنید', 'error');
            this.showDealFormTab('info');
            return;
        }
        if (!stage) {
            Swal.fire('خطا', 'مرحله کاریز را انتخاب کنید', 'error');
            return;
        }
        if (!note.trim()) {
            Swal.fire('خطا', 'نتیجه فعالیت را وارد کنید', 'error');
            return;
        }
        if (!services.length) {
            Swal.fire('خطا', 'خدمات درخواستی را انتخاب کنید', 'error');
            return;
        }
        const isStageFailed = stage === 'deal_failed' || stage === 'معامله ناموفق';
        const isStageSuccess = stage === 'deal_success';

        if (isStageFailed && !failureReason) {
            Swal.fire('خطا', 'دلیل ناموفق را انتخاب کنید', 'error');
            return;
        }
        if (isStageFailed && failureReason === 'other' && !failureOther.trim()) {
            Swal.fire('خطا', 'توضیح دلیل ناموفق را وارد کنید', 'error');
            return;
        }
        if (isStageSuccess && !this.hasEnoughPaymentsForSuccess()) {
            Swal.fire('خطا', 'برای ثبت معامله موفق، مجموع پرداخت‌ها باید حداقل برابر مبلغ قابل پرداخت باشد.', 'error');
            console.warn('saveDealActivity blocked: no complete payments for success', { stage, dealId });
            return;
        }
        if (!activityType) {
            Swal.fire('خطا', 'نوع فعالیت را انتخاب کنید', 'error');
            return;
        }

        const isLocked = (el) => el?.dataset?.locked === 'true';
        const cityEl = document.getElementById('deal-activity-city');
        const jobEl = document.getElementById('deal-activity-job');
        const prevPurchaseEl = document.getElementById('deal-activity-previous-number');
        const acqYearsEl = document.getElementById('deal-activity-acq-years');
        const acqMonthsEl = document.getElementById('deal-activity-acq-months');

        if (!city.trim() && !isLocked(cityEl)) {
            Swal.fire('خطا', 'شهر را وارد کنید', 'error');
            return;
        }
        if (!jobTitle.trim() && !isLocked(jobEl)) {
            Swal.fire('خطا', 'شغل را وارد کنید', 'error');
            return;
        }

        const acqLocked = isLocked(acqYearsEl) || isLocked(acqMonthsEl);
        if (!acqLocked && !acqLess && acqYears === '0' && acqMonths === '0') {
            Swal.fire('خطا', 'مدت زمان آشنایی را وارد کنید', 'error');
            return;
        }

        let prevPurchaseClean = (prevPurchaseNumber || '').trim();
        if (prevPurchaseClean) {
            prevPurchaseClean = prevPurchaseClean.replace(/\D/g, '');
            if (!/^[1-9]\d{9}$/.test(prevPurchaseClean)) {
                Swal.fire('خطا', 'شماره خرید قبلی باید ۱۰ رقم و بدون صفر شروع باشد', 'error');
                return;
            }
        }
        const cityValue = city.trim();
        const jobValue = jobTitle.trim();
        const jobDescValue = jobDescription.trim();
        const extraInfoValue = extraInfo.trim();

        const payload = {
            contact_id: contactId,
            deal_id: dealId,
            pipeline_stage: isStageFailed ? '' : (isStageSuccess ? 'اتمام' : stage),
            result_note: note,
            requested_services: JSON.stringify(services),
            financial_level: financialLevel,
            asset_estimation: asset,
            income_estimation: income,
            is_done: isDone,
            activity_type_id: activityType,
            direction,
            has_previous_purchase_other_number: hasPurchaseOther,
            previous_purchase_number: prevPurchaseClean,
            city: cityValue,
            job_title: jobValue,
            job_description: jobDescValue,
            acquaintance_duration: acquaintanceDuration,
            extra_info: extraInfoValue,
            failure_reason: failureReason,
            failure_other_description: failureOther,
            has_previous_purchase: hasPreviousPurchaseVal === '' ? null : hasPreviousPurchaseVal
        };

        try {
            Swal.showLoading();
            const res = await this.req('save_activity', payload);
            if (isStageFailed) {
                const reasonText = {
                    'no_financial_ability': 'عدم توان مالی',
                    'no_purchase_intent': 'فعلا قصد خرید ندارد',
                    'competitor_purchase': 'خرید از رقبا',
                    'wrong_number': 'شماره اشتباه',
                    'irrelevant_lead': 'لید نامرتبط',
                    'other': 'سایر'
                };
                const statusRes = await this.req('set_deal_status', {
                    deal_id: dealId,
                    status: '0',
                    contact_id: contactId,
                    pipeline_stage: 'معامله ناموفق',
                    failure_reason: failureReason,
                    failure_reason_text: reasonText[failureReason] || '',
                    failure_other_description: failureOther
                });
                if (statusRes?.status !== 'success') {
                    console.warn('set_deal_status failed for lost', statusRes);
                    Swal.fire('خطا', statusRes?.message || 'وضعیت معامله به ناموفق تغییر نکرد', 'error');
                    return;
                }
            } else if (isStageSuccess) {
                const statusRes = await this.req('set_deal_status', { deal_id: dealId, status: '1', contact_id: contactId, pipeline_stage: 'اتمام' });
                if (statusRes?.status !== 'success') {
                    console.warn('set_deal_status failed for won', statusRes);
                    Swal.fire('خطا', statusRes?.message || 'وضعیت معامله به موفق تغییر نکرد', 'error');
                    return;
                }
            }
            Swal.close();
            if (res.status === 'success') {
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'فعالیت ثبت شد', showConfirmButton:false, timer:2000});
                this.state.dealHasActivity = true;
                this.toggleDealActivityForm(true);
                // refresh profile/deal list
                if (window.currentContactId) {
                    await this.openPersonProfile(window.currentContactId);
                }
                if (document.getElementById('deals-container')) {
                    await this.loadDeals();
                }
                await this.openDealModal(dealId);
                this.showDealFormTab('activities');
            } else {
                Swal.fire('خطا', res.message || 'خطای نامشخص در ثبت فعالیت', 'error');
            }
        } catch (err) {
            console.error('saveDealActivity error', err);
            Swal.close();
            Swal.fire('خطا', 'ثبت فعالیت انجام نشد', 'error');
        }
    },

    // Save only extra info (without creating activity) for missing fields
    async saveDealInfoOnly() {
        const contactId = document.getElementById('deal-activity-contact-id')?.value || window.currentContactId || '';
        const dealId = document.getElementById('deal-activity-deal-id')?.value || document.getElementById('deal-id')?.value || '';
        if (!contactId) {
            Swal.fire('خطا', 'شناسه لید خالی است', 'error');
            return;
        }
        const payload = {
            contact_id: contactId,
            deal_id: dealId
        };

        const addIfEditable = (id, key) => {
            const el = document.getElementById(id);
            if (!el || el.dataset.locked === 'true') return;
            const val = (el.value || '').trim();
            if (val) payload[key] = val;
        };

        addIfEditable('deal-activity-city', 'city');
        addIfEditable('deal-activity-job', 'job_title');
        addIfEditable('deal-activity-job-desc', 'job_description');
        addIfEditable('deal-activity-extra-info', 'extra_info');
        addIfEditable('deal-activity-financial-level', 'financial_level');
        addIfEditable('deal-activity-asset', 'asset_estimation');
        addIfEditable('deal-activity-income', 'income_estimation');

        // Acquaintance duration
        const acqLess = document.getElementById('deal-activity-acq-less');
        const acqYears = document.getElementById('deal-activity-acq-years');
        const acqMonths = document.getElementById('deal-activity-acq-months');
        if (acqYears && acqMonths && !(acqYears.dataset.locked === 'true' || acqMonths.dataset.locked === 'true')) {
            let acqVal = '';
            if (acqLess?.checked) {
                acqVal = 'کمتر از 1 ماه';
            } else {
                acqVal = `${acqYears.value || '0'} سال ${acqMonths.value || '0'} ماه`;
            }
            if (acqVal.trim()) payload.acquaintance_duration = acqVal;
        }

        // Requested services
        if (Array.isArray(this.state.dealActivityRequestedServices) && this.state.dealActivityRequestedServices.length > 0) {
            payload.requested_services = JSON.stringify(this.state.dealActivityRequestedServices);
        }

        // Purchase status if not locked
        const purchaseSelect = document.getElementById('deal-activity-has-purchase');
        if (purchaseSelect && purchaseSelect.dataset.locked !== 'true') {
            const val = purchaseSelect.value;
            if (val !== '') {
                payload.has_previous_purchase = val;
            }
        }

        if (Object.keys(payload).length <= 2) {
            Swal.fire('خطا', 'فیلد قابل ویرایش یا مقداری برای ذخیره وجود ندارد', 'error');
            return;
        }

        try {
            Swal.showLoading();
            const res = await this.req('save_deal_info', payload);
            Swal.close();
            if (res.status === 'success') {
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:res.message || 'ذخیره شد', showConfirmButton:false, timer:2000});
                if (contactId) await this.openPersonProfile(contactId);
                if (dealId) await this.openDealModal(dealId);
            } else {
                Swal.fire('خطا', res.message || 'ذخیره انجام نشد', 'error');
            }
        } catch (err) {
            console.error('saveDealInfoOnly error', err);
            Swal.close();
            Swal.fire('خطا', 'ذخیره اطلاعات انجام نشد', 'error');
        }
    },

    startActivityForDeal(presetStage = '') {
        // محاسبه contactId از چند منبع تا همیشه مقدار داشته باشد
        const contactId =
            document.getElementById('deal-contact-id')?.value ||
            this.state.currentDeal?.local_person_id ||
            this.state.currentDeal?.contact_didar_id ||
            this.state.currentLead?.id ||
            this.state.currentLead?.didar_contact_id ||
            '';
        if (!contactId) {
            Swal.fire('خطا', 'شناسه شخص برای این معامله مشخص نیست', 'error');
            return;
        }
        const dealId = document.getElementById('deal-id')?.value || this.state.currentDeal?.didar_deal_id || this.state.currentDeal?.id || '';
        const activityContactInput = document.getElementById('activity-contact-id');
        if (activityContactInput) {
            activityContactInput.value = contactId;
        }
        window.currentContactId = contactId; // برای openModal که از window.currentContactId استفاده می‌کند

        // اطمینان از اینکه لیست معاملات در اکتیویتی پر است
        let deals = Array.isArray(this.state.currentDeals) ? [...this.state.currentDeals] : [];
        if (dealId && !deals.find(d => String(d.didar_deal_id || d.id || d.deal_id) === String(dealId))) {
            if (this.state.currentDeal) {
                deals.push(this.state.currentDeal);
            }
        }
        // اگر هیچ معامله‌ای در لیست نبود، یک معامله موقت برای انتخاب فعالیت بسازیم
        if (!dealId && deals.length === 0 && (this.state.currentDeal || contactId)) {
            const pipelineStageFallback = document.getElementById('deal-pipeline-stage')?.value || '';
            const tempDeal = {
                didar_deal_id: '',
                id: '',
                deal_id: '',
                contact_didar_id: contactId,
                title: document.getElementById('deal-title')?.value || 'معامله',
                pipeline_stage: pipelineStageFallback
            };
            deals.push(tempDeal);
            this.state.currentDeal = tempDeal;
        }

        const pipelineStage = presetStage || document.getElementById('deal-pipeline-stage')?.value || this.state.currentDeal?.pipeline_stage || '';
        this.populateActivityDealSelect(deals, dealId, pipelineStage);

        const stageSelect = document.getElementById('activity-virtual-stage');
        if (stageSelect && pipelineStage) {
            stageSelect.value = pipelineStage;
        }
        if (pipelineStage) {
            const stageDisplay = document.getElementById('activity-current-stage-display');
            const currentStageInput = document.getElementById('activity-current-stage');
            if (stageDisplay) stageDisplay.innerHTML = `<span class="text-sm text-gray-200">${pipelineStage}</span>`;
            if (currentStageInput) currentStageInput.value = pipelineStage;
        }

        // ابتدا مودال را باز کن تا عناصر فرم حتماً در دسترس باشند، سپس استیج را دوباره ست کن
        this.openModal('modalActivity', { source: 'user', entityId: String(dealId || contactId || '') });
        const activityDealIdInput = document.getElementById('activity-deal-id');
        if (activityDealIdInput && dealId) {
            activityDealIdInput.value = dealId;
        }
        if (stageSelect && pipelineStage) {
            stageSelect.value = pipelineStage;
        }
        const stageDisplay = document.getElementById('activity-current-stage-display');
        const currentStageInput = document.getElementById('activity-current-stage');
        if (stageDisplay) stageDisplay.innerHTML = pipelineStage
            ? `<span class="text-sm text-gray-200">${pipelineStage}</span>`
            : '<span class="text-sm text-gray-500">مرحله تعیین نشده</span>';
        if (currentStageInput) currentStageInput.value = pipelineStage || 'keep_current';

        // برای جلوگیری از پوشانده شدن مودال فعالیت، ابتدا مودال معامله را ببند
        this.closeModal('modalDeal');
    },

    scrollToDealPayments() {
        this.showDealFormTab('payments');
        const section = document.getElementById('deal-payment-details');
        if (section) {
            section.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    },

    openSupportFromDeal() {
        if (!this.state.currentDeal) {
            Swal.fire('خطا', 'ابتدا معامله را انتخاب یا ذخیره کنید', 'error');
            return;
        }
        this.openCourseModal(this.state.currentDeal, 'پشتیبانی');
    },

    openCourseFromDeal() {
        if (!this.state.currentDeal) {
            Swal.fire('خطا', 'ابتدا معامله را انتخاب یا ذخیره کنید', 'error');
            return;
        }
        this.openCourseModal(this.state.currentDeal, 'برگزاری دوره');
    },
    
    // OPEN DEAL MODAL
    async openDealModal(dealId) {
        const dealToken = this.createContextToken('dealModal', { dealId: String(dealId || '') || 'new' });
        const dropIfStaleDeal = () => this.dropIfStaleContext('dealModal', dealToken, { dealId: String(dealId || '') || 'new' });
        this.resetDealModalState({ keepContact: true });
        const isEditDeal = !!dealId;
        if (!isEditDeal && !this.canActionClient('deals.create')) {
            Swal.fire('خطا', 'دسترسی به ایجاد معامله ندارید', 'error');
            return;
        }
        let personForSummary = null;
        
        // Populate lead info display in deal modal
        const dealInfoDisplay = document.getElementById('deal-lead-info-display');
        const dealNameText = document.getElementById('deal-lead-name-text');
        if (this.state.currentLead && dealInfoDisplay && dealNameText) {
            const fullName = `${this.state.currentLead.first_name || ''} ${this.state.currentLead.last_name || ''}`.trim() || 'بدون نام';
            dealNameText.textContent = `${fullName} (${this.state.currentLead.mobile_phone || '-'})`;
            dealInfoDisplay.classList.remove('hidden');
        } else if (dealInfoDisplay) {
            dealInfoDisplay.classList.add('hidden');
        }

        // ensure products list
        if (!this.state.lookupsLoaded) {
            await this.loadLookups();
            if (dropIfStaleDeal()) return;
        }
        if (!this.state.products || this.state.products.length === 0) {
            await this.loadProducts();
            if (dropIfStaleDeal()) return;
        }
        this.initDealServiceLookupAutofillTracking();
        this.resetDealServiceLookupAutofillState();
        this.applyRbacActionVisibility();
        const dealTitleSelectEl = document.getElementById('deal-title');
        if (dealTitleSelectEl) {
            dealTitleSelectEl.onchange = () => {
                this.applyDealServiceLookupPricing();
                this.syncDealProductsFromSelectedService();
            };
        }
        const dealHasDiscountEl = document.getElementById('deal-has-discount');
        const dealDiscountTypeEl = document.getElementById('deal-discount-type');
        const dealDiscountAmountEl = document.getElementById('deal-discount-amount');
        if (dealHasDiscountEl) {
            dealHasDiscountEl.onchange = () => this.toggleDiscountFields();
        }
        if (dealDiscountTypeEl) {
            dealDiscountTypeEl.onchange = () => this.toggleDiscountOccasion();
        }
        if (dealDiscountAmountEl) {
            dealDiscountAmountEl.oninput = () => this.applyDealServiceLookupPricing();
        }
        this.setDealServiceCostLookupLock(true);
        const stageSelectEl = document.getElementById('deal-pipeline-stage');
        if (stageSelectEl) {
            stageSelectEl.onchange = () => {
                const badge = document.getElementById('deal-summary-stage');
                if (badge) badge.textContent = stageSelectEl.value || 'مرحله تعیین نشده';
                this.updateDealCurrentFields();
            };
        }
        // برای معامله جدید، تب اطلاعات معامله نمایش داده شود تا ابتدا معامله ذخیره گردد
        this.showDealFormTab('info');
        
        // ✅ Ensure configs loaded then apply (Tooltips, Logic, Defaults, Roles)
        await this.ensureFieldConfigsLoaded();
        if (dropIfStaleDeal()) return;
        this.applyFieldConfigurations('deal', '#deal-form');

        // اگر اطلاعات اولیه در پروفایل لید موجود است، آن بخش را مخفی کن
        const person = personForSummary || this.state.currentLead;
        const infoHasValues =
            !!(person?.city || person?.job_title || person?.job_description || person?.extra_info ||
                person?.financial_level || person?.asset_estimation || person?.income_estimation ||
                person?.acquaintance_duration || (person?.has_previous_purchase !== undefined && person?.has_previous_purchase !== null));
        this.toggleInitialInfoVisibility(!infoHasValues);
        await this.loadActivityTypesForModal();
        if (dropIfStaleDeal()) return;

	        if (!dealId) {
	            // New deal - Reset form first
	            this.state.dealHasActivity = false;
	            this.setPaymentsLockState(false);
	            document.getElementById('deal-modal-title').textContent = 'ثبت معامله جدید';
	            document.getElementById('deal-form').reset();
            document.getElementById('deal-id').value = '';
            document.getElementById('deal-contact-id').value = '';
            document.getElementById('deal-user-name').value = '';
            document.getElementById('deal-user-mobile').value = '';
            const invoiceCodeField = document.getElementById('deal-invoice-code');
            if (invoiceCodeField) invoiceCodeField.value = '';
            // B2: a new deal inherits the lead's marketing (شیوه آشنایی / موضوع محتوا).
            // updateDealMarketingUi() prefills source/detail/topic from the person record
            // (leadDefaults) when deal is null; fields stay editable + required for new deals.
            this.updateDealMarketingUi(null, true, person);
	            this.state.dealActivityRequestedServices = [];
	            this.populateDealActivityServices();
	            this.renderDealActivityServices();

	            // Ensure action buttons are visible (previous deal might have hidden them)
	            const saveInfoBtn = document.getElementById('deal-info-submit-btn');
		            const savePaymentsBtn = document.querySelector("button[onclick*=\"saveDealFromTab('payments')\"]");
	            const addPaymentBtn = document.getElementById('deal-add-payment-btn');
	            const activityBtns = document.querySelectorAll('button[onclick*="startActivityForDeal"]');
	            if (saveInfoBtn) saveInfoBtn.classList.remove('hidden');
	            if (savePaymentsBtn) savePaymentsBtn.classList.remove('hidden');
	            if (addPaymentBtn) addPaymentBtn.classList.remove('hidden');
	            activityBtns.forEach(btn => btn.classList.remove('hidden'));
	            this.enableDealFormFields();
	            this.dealIsWon = false;

            // Ensure purchase field is enabled after form reset
            const purchaseSelectAfterReset = document.getElementById('deal-activity-has-purchase');
            if (purchaseSelectAfterReset) {
                purchaseSelectAfterReset.disabled = false;
                purchaseSelectAfterReset.dataset.locked = '';
                purchaseSelectAfterReset.classList.remove('bg-gray-900', 'cursor-not-allowed');
            }
	            this.state.currentDeal = null;
	            const pipelineStageSelect = document.getElementById('deal-pipeline-stage');
	            if (pipelineStageSelect) {
	                pipelineStageSelect.value = '';
	                pipelineStageSelect.disabled = false;
	                pipelineStageSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
	            }
            this.renderProductOptions();
            
		            // Enable deal status select for new deals
		            let dealStatusSelect = document.getElementById('deal-status');
		        if (dealStatusSelect) {
		            dealStatusSelect.disabled = false;
		            dealStatusSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
		        }
            
            // Load contact info from currentContactId or currentLead
            const contactId = window.currentContactId;
            if (!contactId) {
                // No contact selected - show error
                Swal.fire('خطا', 'لطفاً ابتدا یک شخص را انتخاب کنید', 'error');
                return;
            }

            if (!this.state.currentLead || this.state.currentLead.didar_contact_id !== contactId) {
                this.state.currentDeals = [];
            }
            
            document.getElementById('deal-contact-id').value = contactId;
            const dealActContact = document.getElementById('deal-activity-contact-id');
            if (dealActContact) dealActContact.value = contactId;
            const dealActDeal = document.getElementById('deal-activity-deal-id');
            if (dealActDeal) dealActDeal.value = '';
            
            // Try to get from currentLead first (if profile is already loaded)
            if (this.state.currentLead && this.state.currentLead.didar_contact_id === contactId) {
                const person = this.state.currentLead;
                personForSummary = person;
                const fullName = `${person.first_name || ''} ${person.last_name || ''}`.trim() || 'بدون نام';
                document.getElementById('deal-user-name').value = fullName;
                document.getElementById('deal-user-mobile').value = person.mobile_phone || '';
            } else {
                // Load person info from API
                try {
                    const res = await this.req('get_person_profile', {contact_id: contactId}, {
                        requestKey: `dealModal:new:profile:${String(contactId || '')}`,
                        silentAbort: true
                    });
                    if (res?.aborted) return;
                    if (dropIfStaleDeal()) return;
                    if (res.status === 'success' && res.person) {
                        const person = res.person;
                        personForSummary = person;
                        const fullName = `${person.first_name || ''} ${person.last_name || ''}`.trim() || 'بدون نام';
                        document.getElementById('deal-user-name').value = fullName;
                        document.getElementById('deal-user-mobile').value = person.mobile_phone || '';
                        // Update currentLead for future use
                        this.state.currentLead = person;
                        this.state.currentDeals = res.deals || [];
                    } else {
                        console.error('Failed to load person profile:', res);
                        Swal.fire('خطا', res.message || 'خطا در بارگذاری اطلاعات شخص', 'error');
                        return;
                    }
                } catch (error) {
                    console.error('Error loading person profile:', error);
                    Swal.fire('خطا', 'خطا در بارگذاری اطلاعات شخص', 'error');
                    return;
                }
            }

            // Generate deal title for new deals
            try {
                const titleRes = await this.req('generate_deal_title', {contact_id: contactId}, {
                    requestKey: `dealModal:new:title:${String(contactId || '')}`,
                    silentAbort: true
                });
                if (titleRes?.aborted) return;
                if (dropIfStaleDeal()) return;
                if (titleRes.status === 'success' && titleRes.title) {
                    const titleSelect = document.getElementById('deal-title');
                    if (titleSelect) {
                        // Add the generated title as an option if it doesn't exist
                        const existingOption = Array.from(titleSelect.options).find(option => option.value === titleRes.title);
                        if (!existingOption) {
                            const newOption = document.createElement('option');
                            newOption.value = titleRes.title;
                            newOption.textContent = titleRes.title;
                            newOption.dataset.generated = '1';
                            titleSelect.appendChild(newOption);
                        } else {
                            existingOption.dataset.generated = '1';
                        }
                        titleSelect.value = titleRes.title;
                        const modalTitleEl = document.getElementById('deal-modal-title');
                        if (modalTitleEl) {
                            modalTitleEl.textContent = `ثبت معامله جدید: ${titleRes.title}`;
                        }
                    }
                }
            } catch (error) {
                console.error('Failed to generate deal title:', error);
                // Continue without setting title - user can select manually
            }

            this.paymentRowCounter = 0;
            // برای معامله جدید یک ردیف پرداخت خالی به‌صورت پیش‌فرض باز می‌شود تا بخش خالی نباشد؛
            // کاربر برای پرداخت‌های بعدی روی «افزودن پرداخت» می‌زند.
            this.loadPayments([], false, true);
            this.toggleDiscountFields();
            // Set default status placeholders for new deals
            dealStatusSelect = document.getElementById('deal-status');
            if (dealStatusSelect) {
                dealStatusSelect.disabled = false; // Enable for new deals
                dealStatusSelect.value = '';
                this.handleDealStatusChange();
            }
            // Reset dealIsWon for new deals
            this.dealIsWon = false;
            
            // Ensure all fields are enabled for new deals
            this.enableDealFormFields();
            this.applyDealServiceLookupPricing();

            // Summary + activity dropdown for new deal
            const stageValue = pipelineStageSelect?.value || '';
            // Prefill extra info fields from person (if available)
            const cityInput = document.getElementById('deal-activity-city');
            const jobInput = document.getElementById('deal-activity-job');
            const jobDescInput = document.getElementById('deal-activity-job-desc');
            const extraInfoInput = document.getElementById('deal-activity-extra-info');
            const acqYears = document.getElementById('deal-activity-acq-years');
            const acqMonths = document.getElementById('deal-activity-acq-months');
            const acqLess = document.getElementById('deal-activity-acq-less');
            const acqVal = personForSummary?.acquaintance_duration || '';
            if (cityInput) cityInput.value = personForSummary?.city || '';
            if (jobInput) jobInput.value = personForSummary?.job_title || '';
            if (jobDescInput) jobDescInput.value = personForSummary?.job_description || '';
            if (extraInfoInput) extraInfoInput.value = personForSummary?.extra_info || '';
            const purchaseSelect = document.getElementById('deal-activity-has-purchase');
            if (purchaseSelect) {
                // Always reset lock/disabled state for a brand-new deal
                purchaseSelect.disabled = false;
                purchaseSelect.dataset.locked = '';
                purchaseSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');

                // For new deals, always start with empty value (no default selection)
                purchaseSelect.value = '';
            }
            // Hide previous-number field by default for new deals
            const prevPurchaseField = document.getElementById('deal-previous-purchase-number-field');
            if (prevPurchaseField) prevPurchaseField.classList.add('hidden');
            this.handlePurchaseOtherNumberChange();
            if (acqYears && acqMonths && acqLess) {
                if (acqVal && acqVal.includes('کمتر')) {
                    acqLess.checked = true;
                    this.toggleAcqLessThanMonth();
                } else {
                    const match = acqVal.match(/(\d+)\s*سال.*?(\d+)\s*ماه/);
                    acqYears.value = match ? match[1] : '0';
                    acqMonths.value = match ? match[2] : '0';
                    acqLess.checked = false;
                    this.toggleAcqLessThanMonth();
                }
            }
            this.lockDealActivityExtraFields({
                city: cityInput?.value || '',
                job_title: jobInput?.value || '',
                job_description: jobDescInput?.value || '',
                extra_info: extraInfoInput?.value || '',
                financial_level: document.getElementById('deal-activity-financial-level')?.value || '',
                asset_estimation: document.getElementById('deal-activity-asset')?.value || '',
                income_estimation: document.getElementById('deal-activity-income')?.value || '',
                requested_services: this.state.dealActivityRequestedServices,
                acquaintance_duration: acqVal,
                has_previous_purchase: undefined // Always keep purchase field unlocked for new deals
            });
            this.updateDealModalSummary(personForSummary || this.state.currentLead, {
                pipeline_stage: stageValue,
                contact_name: document.getElementById('deal-user-name').value || '',
                contact_mobile: document.getElementById('deal-user-mobile').value || '',
                owner_name: (personForSummary || this.state.currentLead)?.owner_name || ''
            });
            const dealActStage = document.getElementById('deal-activity-stage');
            const dealActCurrent = document.getElementById('deal-activity-current-stage');
            if (dealActStage) dealActStage.value = stageValue;
            if (dealActCurrent) dealActCurrent.textContent = stageValue || '-';
            this.populateDealActivityStages();
            this.populateActivityDealSelect(this.state.currentDeals || []);
            this.populateDealActivityServices();
            this.showDealFormTab('activities');
            // فعالیت‌ها در تب فعالیت
            this.renderDealActivitiesInline(contactId);
            this.toggleDealActivityForm(false);
            
            this.openModal('modalDeal', { source: 'user', entityId: '' });
            this.applyRbacActionVisibility();
            return;
        }
        
        // Load deal
        const res = await this.req('get_deal', {deal_id: dealId}, {
            requestKey: `dealModal:deal:${String(dealId || '')}`,
            silentAbort: true
        });
        if (res?.aborted) return;
        if (dropIfStaleDeal()) return;
	        if (res.status === 'success' && res.deal) {
            this.setPaymentsLockState(true);
            const deal = res.deal;
            this.updateDealMarketingUi(deal, false);
            const dealTitleText = deal.title || deal.pipeline_stage_title || deal.pipeline_stage || 'معامله';
            document.getElementById('deal-modal-title').textContent = `ویرایش معامله: ${dealTitleText}`;
            
            // Set deal status in form
            const dealStatusSelect = document.getElementById('deal-status');
            const isWonDeal = deal.status === 'Won';
            const pipelineStageSelect = document.getElementById('deal-pipeline-stage');
            const hasRegisteredPayments = this.dealHasRegisteredPayments(deal);
            this.state.dealInfoAdminUnlocked = false;
            
            if (dealStatusSelect) {
                dealStatusSelect.value = String(deal.status || '').toLowerCase();
                this.handleDealStatusChange();
            }
            if (pipelineStageSelect) {
                pipelineStageSelect.value = deal.pipeline_stage || '';
            }
            
            // Display current values in read-only fields
            const statusText = deal.status === 'Won' ? 'موفق' : (deal.status === 'Lost' ? 'ناموفق' : 'جاری');
            const currentStatusDisplay = document.getElementById('deal-current-status');
            const currentStageDisplay = document.getElementById('deal-current-stage');
            
            if (currentStatusDisplay) currentStatusDisplay.textContent = statusText;
            if (currentStageDisplay) currentStageDisplay.textContent = deal.pipeline_stage || '-';

            this.updateDealCurrentFields(deal.pipeline_stage || '', deal.status);

            // Mark products
            if (Array.isArray(deal.products)) {
                const selectedIds = deal.products.map(p => p.product_id);
                this.renderProductOptions(selectedIds);
            } else {
                this.renderProductOptions();
            }
            
            // Save deal info for course modal buttons
            this.state.currentDeal = deal;
            this.state.currentDeals = [deal];
            this.populateDealActivityStages();
            let requestedServices = [];
            try {
                if (deal.requested_services) {
                    const parsed = JSON.parse(deal.requested_services);
                    if (Array.isArray(parsed)) requestedServices = parsed;
                }
            } catch (e) {
                // ignore parse errors
            }
            // Fallback: if عنوان حاوی | خدمات است، از آن استخراج کن
            if ((!requestedServices || requestedServices.length === 0) && deal.title && deal.title.includes('|')) {
                const parts = deal.title.split('|');
                const svc = (parts[parts.length - 1] || '').trim();
                if (svc) requestedServices = [svc];
            }
            this.populateDealActivityServices(requestedServices);

            const dealContactIdInput = document.getElementById('deal-contact-id');
            const dealActContact = document.getElementById('deal-activity-contact-id');
            const dealActDeal = document.getElementById('deal-activity-deal-id');
            const dealActStage = document.getElementById('deal-activity-stage');
            const dealActCurrent = document.getElementById('deal-activity-current-stage');
            const normalizedContactId = deal.contact_didar_id || deal.contact_id || deal.contactId || deal.local_person_id || '';
            if (dealContactIdInput) dealContactIdInput.value = normalizedContactId;
            if (dealActContact) dealActContact.value = normalizedContactId;
            if (dealActDeal) dealActDeal.value = deal.didar_deal_id || deal.id || '';
            if (dealActStage) dealActStage.value = deal.pipeline_stage || '';
            if (dealActCurrent) dealActCurrent.textContent = deal.pipeline_stage || '-';

            const setVal = (id, val) => {
                const el = document.getElementById(id);
                if (el) el.value = val ?? '';
            };
            const extra = deal.extra_fields || {};
            setVal('deal-activity-financial-level', deal.financial_level || extra.financial_level || '');
            setVal('deal-activity-asset', deal.asset_estimation || extra.asset_estimation || '');
            setVal('deal-activity-income', deal.income_estimation || extra.income_estimation || '');
            const purchaseSelect = document.getElementById('deal-activity-has-purchase');
            // Capture purchase value (from deal -> extra fields -> person) but don't lock yet
            const purchaseVal = (deal.has_previous_purchase !== undefined && deal.has_previous_purchase !== null)
                ? deal.has_previous_purchase
                : (extra.has_previous_purchase !== undefined && extra.has_previous_purchase !== null
                    ? extra.has_previous_purchase
                    : personForSummary?.has_previous_purchase);
            const hasPurchaseVal = purchaseVal === 0 || purchaseVal === 1 || purchaseVal === '0' || purchaseVal === '1';
            if (purchaseSelect) {
                purchaseSelect.value = hasPurchaseVal ? String(purchaseVal) : '';
                purchaseSelect.disabled = false;
                purchaseSelect.dataset.locked = '';
                purchaseSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
            }

            this.showDealFormTab('activities');
            this.renderDealActivitiesInline(normalizedContactId);
            const hasAct = await this.checkDealHasActivity(dealId);
            if (dropIfStaleDeal()) return;
            this.state.dealHasActivity = hasAct;
            this.toggleDealActivityForm(hasAct);
            if (purchaseSelect) {
                const shouldLockPurchase =
                    (purchaseVal === 1 || purchaseVal === '1') ||
                    (hasPurchaseVal && hasAct);
                if (shouldLockPurchase) {
                    purchaseSelect.disabled = true;
                    purchaseSelect.dataset.locked = 'true';
                    purchaseSelect.classList.add('bg-gray-900', 'cursor-not-allowed');
                } else {
                    purchaseSelect.disabled = false;
                    purchaseSelect.dataset.locked = '';
                    purchaseSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
                    purchaseSelect.value = '';
                }
            }
            
            // Check if deal is Won or Lost
            const isWonOrLost = deal.status === 'Won' || deal.status === 'Lost';
            
            // For won or lost deals, disable form fields and actions
            const saveInfoBtn = document.getElementById('deal-info-submit-btn');
            const savePaymentsBtn = document.querySelector('button[onclick*="saveDealFromTab(\'payments\')"]');
            const addPaymentBtn = document.getElementById('deal-add-payment-btn');
            const activityBtns = document.querySelectorAll('button[onclick*="startActivityForDeal"]');
            
            if (isWonOrLost) {
                const isAdminUser = this.isAdmin();
                const canAdminUnlockPaid = isAdminUser && hasRegisteredPayments;
                // Hide action buttons
                if (saveInfoBtn) saveInfoBtn.classList.toggle('hidden', !canAdminUnlockPaid);
                if (savePaymentsBtn) savePaymentsBtn.classList.toggle('hidden', !isAdminUser);
                if (addPaymentBtn) addPaymentBtn.classList.toggle('hidden', !isAdminUser);
                activityBtns.forEach(btn => btn.classList.add('hidden'));
                this.setDealInfoLockState(true, { allowAdminUnlock: canAdminUnlockPaid });
                
                // Store readonly state for existing payments (only for won deals)
                if (isWonDeal) {
                    this.dealIsWon = true;
                } else {
                    this.dealIsWon = false;
                }
            } else {
                // Enable deal status select for pending deals
                if (dealStatusSelect) {
                    dealStatusSelect.disabled = false;
                    dealStatusSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
                }
                if (pipelineStageSelect) {
                    pipelineStageSelect.disabled = false;
                    pipelineStageSelect.classList.remove('bg-gray-900', 'cursor-not-allowed');
                }

                // Show action buttons
                if (saveInfoBtn) saveInfoBtn.classList.remove('hidden');
                if (savePaymentsBtn) savePaymentsBtn.classList.remove('hidden');
                if (addPaymentBtn) addPaymentBtn.classList.remove('hidden');
                activityBtns.forEach(btn => btn.classList.remove('hidden'));
                
                // Re-enable all other fields
                this.enableDealFormFields();
                this.setDealInfoLockState(hasRegisteredPayments, { allowAdminUnlock: hasRegisteredPayments });

                this.dealIsWon = false;
            }
            
	            // Load failure reason if deal is lost
	            if (deal.status === 'Lost') {
	                const failureSection = document.getElementById('deal-failure-reason-section');
	                const failureReasonSelect = document.getElementById('deal-failure-reason');
	                const failureOtherDiv = document.getElementById('deal-failure-other-reason');
	                const failureOtherDesc = document.getElementById('deal-failure-other-description');

	                if (failureSection) failureSection.classList.remove('hidden');
	                if (failureReasonSelect) {
	                    failureReasonSelect.value = deal.failure_reason_code || '';
	                    failureReasonSelect.disabled = true; // Closed deal => read-only
	                }
	                if (failureOtherDiv) failureOtherDiv.classList.remove('hidden');
	                if (failureOtherDesc) {
	                    failureOtherDesc.value = deal.failure_reason_description || '';
	                    failureOtherDesc.readOnly = true;
	                }
	            } else {
	                const failureSection = document.getElementById('deal-failure-reason-section');
	                const failureReasonSelect = document.getElementById('deal-failure-reason');
	                const failureOtherDiv = document.getElementById('deal-failure-other-reason');
	                const failureOtherDesc = document.getElementById('deal-failure-other-description');
	                if (failureSection) failureSection.classList.add('hidden');
	                if (failureReasonSelect) {
	                    failureReasonSelect.value = '';
	                    failureReasonSelect.disabled = false;
	                }
	                if (failureOtherDiv) failureOtherDiv.classList.add('hidden');
	                if (failureOtherDesc) {
	                    failureOtherDesc.value = '';
	                    failureOtherDesc.readOnly = false;
	                }
	            }
            
            document.getElementById('deal-id').value = deal.didar_deal_id || deal.id;
            document.getElementById('deal-contact-id').value = normalizedContactId;
            
            // Load contact info - try from deal first, then from API if missing
            if (deal.contact_name && deal.contact_mobile) {
                document.getElementById('deal-user-name').value = deal.contact_name;
                document.getElementById('deal-user-mobile').value = deal.contact_mobile;
                personForSummary = personForSummary || {
                    first_name: deal.contact_name,
                    mobile_phone: deal.contact_mobile,
                    owner_name: deal.owner_name
                };
            }
            
            if (!personForSummary && (deal.local_person_id || deal.contact_didar_id || deal.contact_id || deal.contactId)) {
                // Load person info from API if not in deal data
                try {
                    const profileContactId = normalizedContactId;
                    const personRes = await this.req('get_person_profile', {contact_id: profileContactId}, {
                        requestKey: `dealModal:dealProfile:${String(profileContactId || '')}`,
                        silentAbort: true
                    });
                    if (personRes?.aborted) return;
                    if (dropIfStaleDeal()) return;
                    if (personRes.status === 'success' && personRes.person) {
                        const person = personRes.person;
                        personForSummary = person;
                        this.state.currentLead = person;
                        this.state.currentDeals = personRes.deals || [deal];
                        const fullName = `${person.first_name || ''} ${person.last_name || ''}`.trim() || 'بدون نام';
                        document.getElementById('deal-user-name').value = fullName;
                        document.getElementById('deal-user-mobile').value = person.mobile_phone || '';
                    } else {
                        document.getElementById('deal-user-name').value = deal.contact_name || '';
                        document.getElementById('deal-user-mobile').value = deal.contact_mobile || '';
                    }
                } catch (error) {
                    console.error('Error loading person profile for deal:', error);
                    document.getElementById('deal-user-name').value = deal.contact_name || '';
                    document.getElementById('deal-user-mobile').value = deal.contact_mobile || '';
                }
            } else if (!personForSummary) {
                document.getElementById('deal-user-name').value = deal.contact_name || '';
                document.getElementById('deal-user-mobile').value = deal.contact_mobile || '';
            }

            // Update summary & activity dropdown with latest data
            const cityInput = document.getElementById('deal-activity-city');
            const jobInput = document.getElementById('deal-activity-job');
            const jobDescInput = document.getElementById('deal-activity-job-desc');
            const extraInfoInput = document.getElementById('deal-activity-extra-info');
            const acqYears = document.getElementById('deal-activity-acq-years');
            const acqMonths = document.getElementById('deal-activity-acq-months');
            const acqLess = document.getElementById('deal-activity-acq-less');
            
            // Prioritize currentLead (latest data) over deal snapshot
            const currentPerson = (this.state.currentLead && (
                                    this.state.currentLead.id == deal.local_person_id || 
                                    this.state.currentLead.didar_contact_id === deal.contact_didar_id || 
                                    this.state.currentLead.didar_contact_id === deal.contact_id
                                )) 
                                ? this.state.currentLead 
                                : null;
            const getVal = (prop) => currentPerson?.[prop] || deal[prop] || personForSummary?.[prop] || '';

            const acqVal = getVal('acquaintance_duration');
            if (cityInput) cityInput.value = getVal('city');
            if (jobInput) jobInput.value = getVal('job_title');
            if (jobDescInput) jobDescInput.value = getVal('job_description');
            if (extraInfoInput) extraInfoInput.value = getVal('extra_info');
            if (purchaseSelect) {
                purchaseSelect.value = hasPurchaseVal ? String(purchaseVal) : '';
            }
            if (acqYears && acqMonths && acqLess) {
                if (acqVal && acqVal.includes('کمتر')) {
                    acqLess.checked = true;
                    this.toggleAcqLessThanMonth();
                } else {
                    const match = acqVal.match(/(\d+)\s*سال.*?(\d+)\s*ماه/);
                    acqYears.value = match ? match[1] : '0';
                    acqMonths.value = match ? match[2] : '0';
                    acqLess.checked = false;
                    this.toggleAcqLessThanMonth();
                }
            }
            this.lockDealActivityExtraFields({
                city: cityInput?.value || '',
                job_title: jobInput?.value || '',
                job_description: jobDescInput?.value || '',
                extra_info: extraInfoInput?.value || '',
                financial_level: deal.financial_level || personForSummary?.financial_level || '',
                asset_estimation: deal.asset_estimation || personForSummary?.asset_estimation || '',
                income_estimation: deal.income_estimation || personForSummary?.income_estimation || '',
                requested_services: requestedServices,
                acquaintance_duration: acqVal,
                has_previous_purchase: (
                    (purchaseVal === 1 || purchaseVal === '1') ||
                    (hasPurchaseVal && hasAct)
                ) ? purchaseVal : undefined
            });
            this.updateDealModalSummary(personForSummary || this.state.currentLead, deal);
            
            // Update deal lead info display when opening existing deal
            const dealInfoDisplay = document.getElementById('deal-lead-info-display');
            const dealNameText = document.getElementById('deal-lead-name-text');
            if (dealInfoDisplay && dealNameText) {
                // Use person from deal or current lead - prioritize personForSummary
                const person = personForSummary || this.state.currentLead;
                const dealContactId = deal.contact_didar_id || deal.contact_id || '';
                const currentContactId = window.currentContactId || '';
                
                // If deal belongs to different contact than current, show warning
                if (dealContactId && currentContactId && dealContactId !== currentContactId) {
                    // This deal belongs to a different contact
                    const contactName = deal.contact_name || 'بدون نام';
                    dealNameText.textContent = `${contactName} (${deal.contact_mobile || '-'}) - ⚠️ این معامله متعلق به لید دیگری است`;
                    dealInfoDisplay.classList.remove('hidden');
                    dealInfoDisplay.classList.add('border-red-500'); // Add red border to indicate warning
                } else if (person && (
                    person.didar_contact_id === dealContactId || 
                    person.id == deal.local_person_id ||
                    (!dealContactId && person.didar_contact_id === currentContactId)
                )) {
                    const fullName = `${person.first_name || ''} ${person.last_name || ''}`.trim() || 'بدون نام';
                    dealNameText.textContent = `${fullName} (${person.mobile_phone || '-'})`;
                    dealInfoDisplay.classList.remove('hidden');
                    dealInfoDisplay.classList.remove('border-red-500');
                } else if (deal.contact_name) {
                    dealNameText.textContent = `${deal.contact_name} (${deal.contact_mobile || '-'})`;
                    dealInfoDisplay.classList.remove('hidden');
                    dealInfoDisplay.classList.remove('border-red-500');
                } else {
                    dealInfoDisplay.classList.add('hidden');
                }
            }
            
            this.populateActivityDealSelect(this.state.currentDeals || [deal], deal.didar_deal_id || deal.id);

            // ریست گزینه‌های خدمات بر اساس محصولات ثبت‌شده و سپس انتخاب مقدار معتبر
            const titleSelect = document.getElementById('deal-title');
            if (titleSelect) {
                this.renderDealTitleOptions(titleSelect.value || '');
            }
            let options = titleSelect ? Array.from(titleSelect.options || []) : [];

            // اگر requested_services موجود است، اولین مقدار را برای انتخاب استفاده کن
            let candidateService = '';
            const dealTitleRaw = String(deal.title || '').trim();
            if (dealTitleRaw && options.some(o => o.value === dealTitleRaw)) {
                candidateService = dealTitleRaw;
            } else if (dealTitleRaw && dealTitleRaw.includes('|')) {
                const parts = deal.title.split('|');
                candidateService = (parts[parts.length - 1] || '').trim();
            }
            if (!candidateService) {
                candidateService = (requestedServices && requestedServices[0]) || '';
            }

            if (titleSelect) {
                let hasOption = options.some(o => o.value === candidateService);
                if (!hasOption && candidateService) {
                    const legacyOption = document.createElement('option');
                    legacyOption.value = candidateService;
                    legacyOption.textContent = candidateService;
                    legacyOption.dataset.generated = '1';
                    titleSelect.appendChild(legacyOption);
                    hasOption = true;
                    options = Array.from(titleSelect.options || []);
                }
                titleSelect.value = hasOption ? candidateService : '';
            }
        const infoHasValues =
            !!(deal.city || deal.job_title || deal.job_description || deal.extra_info ||
                deal.financial_level || deal.asset_estimation || deal.income_estimation ||
                deal.acquaintance_duration || deal.has_previous_purchase !== undefined);
        this.toggleInitialInfoVisibility(!infoHasValues);
            document.getElementById('deal-service-cost').value = formatMoneyString(deal.service_cost || '');
                const invoiceCodeField = document.getElementById('deal-invoice-code');
                if (invoiceCodeField) invoiceCodeField.value = deal.invoice_code || '';
            document.getElementById('deal-price-list-code').value = deal.price_list_code || '';
            document.getElementById('deal-has-discount').value = deal.has_discount || 0;
            document.getElementById('deal-discount-type').value = deal.discount_type || '';
            document.getElementById('deal-discount-amount').value = formatMoneyString(deal.discount_amount || '');
            const refundAmountEl = document.getElementById('deal-refund-amount');
            const refundDescEl = document.getElementById('deal-refund-description');
            if (refundAmountEl) refundAmountEl.value = formatMoneyString(deal.refund_amount || '');
            if (refundDescEl) refundDescEl.value = deal.refund_description || '';
            document.getElementById('deal-payable-amount').value = formatMoneyString(deal.payable_amount || '');
            document.getElementById('deal-payment-conditions').value = deal.payment_conditions || '';
            
            // Handle discount occasion
            if (deal.discount_occasion) {
                const occasionMap = {
                    'black_friday': 'black_friday',
                    'teacher_birthday': 'teacher_birthday',
                    'nowruz': 'nowruz'
                };
                if (occasionMap[deal.discount_occasion]) {
                    document.getElementById('deal-discount-occasion').value = deal.discount_occasion;
                } else {
                    document.getElementById('deal-discount-occasion').value = 'other';
                    document.getElementById('deal-discount-occasion-custom').value = deal.discount_occasion;
                }
            }
            const serviceCostField = document.getElementById('deal-service-cost');
            const payableAmountField = document.getElementById('deal-payable-amount');
            const priceListCodeField = document.getElementById('deal-price-list-code');
            if (titleSelect) {
                titleSelect.dataset.lastSelectedService = candidateService || '';
            }
            if (serviceCostField) {
                this.clearDealFieldLookupAutofill(serviceCostField);
            }
            if (payableAmountField) {
                this.clearDealFieldLookupAutofill(payableAmountField);
            }
            if (priceListCodeField) {
                this.clearDealFieldLookupAutofill(priceListCodeField);
            }
            this.applyDealServiceLookupPricing();
            
            // Load payments
            this.paymentRowCounter = 0;
            let payments = [];
            console.log('Loading payments for deal:', deal);
            
            if (deal.payments && Array.isArray(deal.payments) && deal.payments.length > 0) {
                console.log('Using payments array:', deal.payments);
                payments = deal.payments.map(p => {
                    // Ensure date_persian exists
                    let datePersian = p.date_persian || '';
                    if (!datePersian && p.date) {
                        // Try to convert Gregorian to Persian
                        try {
                            const gDate = new Date(p.date);
                            if (!isNaN(gDate.getTime())) {
                                const pDate = new persianDate(gDate);
                                datePersian = pDate.format('YYYY/MM/DD');
                            } else {
                                // If date is already in Persian format, use it as is
                                datePersian = p.date;
                            }
                        } catch(e) {
                            datePersian = p.date || '';
                        }
                    }
                    
                    // Normalize payment keys to standard format
                    // Support both card_last4 and card_last_4, but use card_last4 as standard
                    const cardLast4 = p.card_last4 || p.card_last_4 || '';
                    const destinationBank = p.destination_bank || p.bank_destination || '';
                    const method = p.method || p.payment_method || '';
                    
                    const paymentObj = {
                        ...p,
                        date_persian: datePersian,
                        date: p.date || '',
                        method: method,
                        card_last4: cardLast4,  // Always use card_last4 (standard)
                        destination_bank: destinationBank,  // Always use destination_bank (standard)
                        destination_bank_other: p.destination_bank_other || '',
                        destination_card_number: String(p.destination_card_number || '').replace(/\D/g, '').slice(-4),
                        destination_card_number_other: p.destination_card_number_other || '',
                        destination_bank_title: p.destination_bank_title || '',
                        destination_card_full: p.destination_card_full || '',
                        destination_account_number: p.destination_account_number || '',
                        destination_sheba: p.destination_sheba || '',
                        payment_time: p.payment_time || '',
                        card_reader_device: p.card_reader_device || '',
                        currency: p.currency || '',
                        currency_other: p.currency_other || '',
                        currency_amount: p.currency_amount || '',
                        check_bank_name: p.check_bank_name || '',
                        check_date: p.check_date || '',
                        check_number: p.check_number || '',
                        check_owner_name: p.check_owner_name || ''
                    };
                    console.log('Processed payment:', paymentObj);
                    return paymentObj;
                });
            } else if (deal.payment_amount || deal.payment_method) {
                console.log('Using legacy payment fields');
                // Legacy single payment - convert date if needed
                let persianDate = '';
                if (deal.payment_date) {
                    try {
                        const gDate = new Date(deal.payment_date);
                        if (!isNaN(gDate.getTime())) {
                            const pDate = new persianDate(gDate);
                            persianDate = pDate.format('YYYY/MM/DD');
                        } else {
                            // If date is already in Persian format, use it as is
                            persianDate = deal.payment_date;
                        }
                    } catch(e) {
                        persianDate = deal.payment_date || '';
                    }
                }
                payments = [{
                    date: deal.payment_date || '',
                    date_persian: persianDate,
                    amount: deal.payment_amount || '',
                    method: deal.payment_method || '',
                    description: deal.payment_description || ''
                }];
                console.log('Legacy payment:', payments[0]);
            }
            
            console.log('Final payments array:', payments);
            // Pass null to loadPayments so it can decide readonly based on payment data.
            // addDefault=true: when an existing deal has no payments, still show one
            // empty starter row so the payments tab isn't blank.
            this.loadPayments(payments, null, true);

            this.setDealServiceCostLookupLock(false);
            this.toggleDiscountFields();
            this.toggleDiscountOccasion();
            this.toggleCustomOccasion();

            // نمایش/پنهان کردن دکمه فعال‌سازی بر اساس وضعیت معامله
            const reactivateBtn = document.getElementById('reactivate-deal-btn');
            if (reactivateBtn) {
                const isClosed = deal.status === 'Won' || deal.status === 'Lost';
                reactivateBtn.style.display = this.isAdmin() && isClosed ? 'inline-block' : 'none';
            }
            const forceSuccessBtn = document.getElementById('force-success-exit-kariz-btn');
            if (forceSuccessBtn) {
                const hasDealId = !!(deal.didar_deal_id || dealId || deal.id);
                const serviceCost = Number(deal.service_cost || deal.payable_amount || 0);
                const paymentAmount = Number(deal.payment_amount || 0);
                const outstandingAmount = Math.max(serviceCost - paymentAmount, 0);
                const canForceExitKariz = hasDealId && deal.status === 'Won' && outstandingAmount <= 0.0001;
                forceSuccessBtn.style.display = canForceExitKariz ? 'inline-block' : 'none';
            }

            this.openModal('modalDeal', { source: 'user', entityId: String(dealId || deal.didar_deal_id || deal.id || '') });
            this.applyRbacActionVisibility();
        } else {
            Swal.fire('خطا', res.message || 'خطا در بارگذاری معامله', 'error');
        }
    },
    
    // PAYMENT MANAGEMENT
    paymentRowCounter: 0,

    setPaymentsLockState(isLocked) {
        this.state.paymentsLocked = !!isLocked;
        const addBtn = document.getElementById('deal-add-payment-btn');
        if (addBtn) {
            addBtn.disabled = false;
            addBtn.title = isLocked ? 'پرداخت‌های ثبت‌شده قابل ویرایش نیستند؛ برای ثبت پرداخت جدید کلیک کنید' : '';
            addBtn.classList.remove('opacity-50', 'pointer-events-none');
        }
    },

    collectCurrentPayments() {
        const dates = document.querySelectorAll('input[name="payment_date[]"]');
        const amounts = document.querySelectorAll('input[name="payment_amount[]"]');
        const methods = document.querySelectorAll('select[name="payment_method[]"]');
        const descriptions = document.querySelectorAll('textarea[name="payment_description[]"]');
        const cardLast4s = document.querySelectorAll('input[name="payment_card_last4[]"]');
        const destinationBanks = document.querySelectorAll('select[name="destination_bank[]"]');
        const destinationBankOthers = document.querySelectorAll('input[name="destination_bank_other[]"]');
        const accountOwnerNames = document.querySelectorAll('input[name="account_owner_name[]"]');
        const depositorNames = document.querySelectorAll('input[name="depositor_name[]"]');
        const destinationCards = document.querySelectorAll('input[name="destination_card_number[]"]');
        const destinationBankTitles = document.querySelectorAll('input[name="destination_bank_title[]"]');
        const destinationCardFulls = document.querySelectorAll('input[name="destination_card_full[]"]');
        const destinationAccounts = document.querySelectorAll('input[name="destination_account_number[]"]');
        const destinationShebas = document.querySelectorAll('input[name="destination_sheba[]"]');
        const paymentTimes = document.querySelectorAll('input[name="payment_time[]"]');
        const cardReaderDevices = document.querySelectorAll('select[name="card_reader_device[]"]');
        const currencies = document.querySelectorAll('select[name="payment_currency[]"]');
        const currencyOthers = document.querySelectorAll('input[name="payment_currency_other[]"]');
        const currencyAmounts = document.querySelectorAll('input[name="payment_currency_amount[]"]');
        const checkBanks = document.querySelectorAll('input[name="check_bank_name[]"]');
        const checkDates = document.querySelectorAll('input[name="check_date[]"]');
        const checkNumbers = document.querySelectorAll('input[name="check_number[]"]');
        const checkOwnerNames = document.querySelectorAll('input[name="check_owner_name[]"]');
        const registeredAts = document.querySelectorAll('input[name="payment_registered_at[]"]');
        const depositDates = document.querySelectorAll('input[name="deposit_date[]"]');
        const checkDueDates = document.querySelectorAll('input[name="check_due_date[]"]');
        const sayadiDates = document.querySelectorAll('input[name="sayadi_date[]"]');
        const checkDeliveryDates = document.querySelectorAll('input[name="check_delivery_date[]"]');
        const checkRecipientNames = document.querySelectorAll('input[name="check_recipient_name[]"]');
        const receiptImagePaths = document.querySelectorAll('input[name="receipt_image_path[]"]');
        const checkImagePaths = document.querySelectorAll('input[name="check_image_path[]"]');
        const len = Math.max(
            dates.length,
            amounts.length,
            methods.length,
            descriptions.length,
            cardLast4s.length,
            destinationBanks.length,
            destinationCards.length,
            paymentTimes.length
        );
        const list = [];
        for (let i = 0; i < len; i++) {
            list.push({
                date_persian: dates[i]?.value || '',
                amount: amounts[i]?.value || '',
                method: methods[i]?.value || '',
                description: descriptions[i]?.value || '',
                card_last4: cardLast4s[i]?.value || '',
                destination_bank: destinationBanks[i]?.value || '',
                destination_bank_other: destinationBankOthers[i]?.value || '',
                account_owner_name: accountOwnerNames[i]?.value || '',
                depositor_name: depositorNames[i]?.value || '',
                destination_card_number: String(destinationCards[i]?.value || '').replace(/\D/g, '').slice(-4),
                destination_card_number_other: '',
                destination_bank_title: destinationBankTitles[i]?.value || '',
                destination_card_full: destinationCardFulls[i]?.value || '',
                destination_account_number: destinationAccounts[i]?.value || '',
                destination_sheba: destinationShebas[i]?.value || '',
                payment_time: paymentTimes[i]?.value || '',
                card_reader_device: cardReaderDevices[i]?.value || '',
                currency: currencies[i]?.value || '',
                currency_other: currencyOthers[i]?.value || '',
                currency_amount: currencyAmounts[i]?.value || '',
                check_bank_name: checkBanks[i]?.value || '',
                check_date: checkDates[i]?.value || '',
                check_number: checkNumbers[i]?.value || '',
                check_owner_name: checkOwnerNames[i]?.value || '',
                registered_at: registeredAts[i]?.value || '',
                deposit_date_persian: depositDates[i]?.value || '',
                check_due_date_persian: this.isCheckMethod(methods[i]?.value || '') ? (dates[i]?.value || '') : (checkDueDates[i]?.value || ''),
                sayadi_date: sayadiDates[i]?.value || '',
                check_delivery_date_persian: checkDeliveryDates[i]?.value || '',
                check_recipient_name: checkRecipientNames[i]?.value || '',
                receipt_image_path: receiptImagePaths[i]?.value || '',
                check_image_path: checkImagePaths[i]?.value || ''
            });
        }
        return list.filter(p =>
            p.date_persian || p.amount || p.method || p.description ||
            p.card_last4 || p.destination_bank || p.destination_bank_other ||
            p.account_owner_name || p.depositor_name ||
            p.destination_card_number || p.destination_card_full || p.destination_account_number || p.destination_sheba ||
            p.payment_time || p.card_reader_device || p.currency || p.currency_other ||
            p.currency_amount || p.check_bank_name || p.check_date || p.check_number || p.check_owner_name
        );
    },

    normalizePaymentMethodCode(methodValue) {
        return String(methodValue || '').trim().toLowerCase().replace(/[\s\-\/]+/g, '_');
    },

    isShebaMethod(methodValue) {
        const code = this.normalizePaymentMethodCode(methodValue);
        return code.includes('sheba') || code.includes('shaba') || code.includes('pol') || code.includes('pool') || code.includes('پل');
    },

    isCardToCardMethod(methodValue) {
        const code = this.normalizePaymentMethodCode(methodValue);
        return code === 'card_to_card' || code === 'card_to_card_lookup' || code === 'card_to_card_transfer';
    },

    isCardReaderMethod(methodValue) {
        const code = this.normalizePaymentMethodCode(methodValue);
        return code === 'card_reader' || code === 'pos' || code === 'pos_machine';
    },

    isCashMethod(methodValue) {
        const code = this.normalizePaymentMethodCode(methodValue);
        return code === 'cash' || code === 'نقد' || code === 'وجه_نقد';
    },

    isCheckMethod(methodValue) {
        const code = this.normalizePaymentMethodCode(methodValue);
        return code === 'check' || code === 'cheque' || code === 'چک';
    },

    normalizeTimeValue(timeValue) {
        const raw = String(timeValue || '').trim();
        const match = raw.match(/^(\d{1,2}):(\d{2})/);
        if (!match) return '';
        const hour = Number(match[1]);
        const minute = Number(match[2]);
        if (!Number.isInteger(hour) || !Number.isInteger(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
            return '';
        }
        return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
    },

    getSystemTimeValue() {
        const now = new Date();
        const hour = now.getHours();
        const minute = now.getMinutes();
        const roundedMinute = Math.round(minute / 5) * 5;
        const adjustedHour = roundedMinute >= 60 ? (hour + 1) % 24 : hour;
        const adjustedMinute = roundedMinute >= 60 ? 0 : roundedMinute;
        return `${String(adjustedHour).padStart(2, '0')}:${String(adjustedMinute).padStart(2, '0')}`;
    },

    ensurePaymentTimeDefault(timeInput) {
        if (!timeInput) return;
        const normalized = this.normalizeTimeValue(timeInput.value);
        if (normalized) {
            timeInput.value = normalized;
            return;
        }
        timeInput.value = this.getSystemTimeValue();
    },
    
    async uploadPaymentFile(inputEl, kind) {
        const file = inputEl?.files?.[0];
        if (!file) return;
        if (file.size > 5 * 1024 * 1024) {
            Swal.fire('خطا', 'حجم فایل باید حداکثر ۵ مگابایت باشد', 'error');
            inputEl.value = '';
            return;
        }
        const wrap = inputEl.closest('div');
        const hidden = wrap?.querySelector('input[type="hidden"]');
        const link = wrap?.querySelector('a');
        const fd = new FormData();
        fd.append('file', file);
        fd.append('kind', kind || 'receipt');
        try {
            Swal.fire({ title: 'در حال بارگذاری...', allowOutsideClick: false, didOpen: () => Swal.showLoading() });
            const r = await fetch('?api=upload_payment_file', { method: 'POST', body: fd });
            const data = await r.json();
            Swal.close();
            if (r.ok && data.status === 'success' && data.path) {
                if (hidden) hidden.value = data.path;
                if (link) { link.href = data.path; link.classList.remove('hidden'); }
            } else {
                Swal.fire('خطا', data.message || 'بارگذاری ناموفق بود', 'error');
                inputEl.value = '';
            }
        } catch (e) {
            Swal.close();
            console.error('uploadPaymentFile error', e);
            Swal.fire('خطا', 'خطا در بارگذاری فایل', 'error');
            inputEl.value = '';
        }
    },

    isOtherPaymentMethod(value, label = '') {
        const code = this.normalizePaymentMethodCode(value);
        if (code === 'other' || code === 'سایر' || code === 'دیگر') return true;
        const text = String(label || '').trim();
        return text === 'سایر' || text === 'دیگر';
    },

    getPaymentDestinationMeta(value) {
        const item = this.findLookupItem('payment_destination_banks', value, true);
        if (!item || !item.meta_json) return {};
        try {
            const meta = JSON.parse(item.meta_json);
            return meta && typeof meta === 'object' && !Array.isArray(meta) ? meta : {};
        } catch (_) {
            return {};
        }
    },

    getPaymentDestinationSnapshot(value) {
        const item = this.findLookupItem('payment_destination_banks', value, true);
        const meta = this.getPaymentDestinationMeta(value);
        const cardFull = String(meta.card_number || '').replace(/\D/g, '');
        return {
            title: item?.title || '',
            bankName: String(meta.bank_name || ''),
            accountType: String(meta.account_type || ''),
            cardFull,
            cardLast4: cardFull ? cardFull.slice(-4) : '',
            accountNumber: String(meta.account_number || '').replace(/\s+/g, ''),
            sheba: String(meta.sheba || '').replace(/\s+/g, '').toUpperCase()
        };
    },

    formatPaymentDestinationOption(opt) {
        const snapshot = this.getPaymentDestinationSnapshot(opt.value);
        const parts = [
            snapshot.bankName || opt.label,
            snapshot.accountType,
            snapshot.cardLast4 ? `کارت ${snapshot.cardLast4}` : ''
        ].filter(Boolean);
        return parts.length ? parts.join(' - ') : opt.label;
    },

    populatePaymentMethods(selectElement, selectedValue = '') {
        const options = this.getLookupOptions('payment_methods');
        selectElement.innerHTML = '<option value="">-- انتخاب نحوه پرداخت --</option>';
        options.forEach(opt => {
            // C1: hide the "سایر/Other" payment method, but keep it if an existing
            // record was already saved with it so the locked row still displays correctly.
            if (this.isOtherPaymentMethod(opt.value, opt.label) && opt.value !== selectedValue) return;
            const optElem = document.createElement('option');
            optElem.value = opt.value;
            const isSheba = this.isShebaMethod(opt.value) || this.isShebaMethod(opt.label);
            optElem.textContent = isSheba ? 'شِبا/پُل' : opt.label;
            if (opt.value === selectedValue) optElem.selected = true;
            selectElement.appendChild(optElem);
        });
        // Guard: if the stored method isn't in the loaded lookups yet (e.g. options not
        // loaded when an existing payment renders), still keep the saved value selected.
        // Otherwise the select would fall back to '' and togglePaymentFields would wipe
        // card_last4/depositor/time before a re-save, silently dropping saved data.
        if (selectedValue && !Array.from(selectElement.options).some(o => o.value === selectedValue)) {
            const keepElem = document.createElement('option');
            keepElem.value = selectedValue;
            const isSheba = this.isShebaMethod(selectedValue);
            keepElem.textContent = isSheba ? 'شِبا/پُل' : selectedValue;
            keepElem.selected = true;
            selectElement.appendChild(keepElem);
        }
    },

    populateDestinationBanks(selectElement, selectedValue = '') {
        const allOptions = this.getLookupOptions('payment_destination_banks')
            .filter(opt => !this.isOtherPaymentMethod(opt.value, opt.label));
        let options = [...allOptions];
        if (selectedValue && selectedValue !== '__other__' && !options.some(o => o.value === selectedValue)) {
            let extra = allOptions.find(o => o.value === selectedValue);
            if (!extra) {
                // The saved bank was hidden/disabled in the lookup manager. Resolve it from
                // the include-inactive archive so older deals keep showing their card.
                const archived = this.findLookupItem('payment_destination_banks', selectedValue, true);
                if (archived) {
                    extra = { value: selectedValue, label: archived.title ?? archived.label ?? selectedValue };
                }
            }
            if (extra) options = options.concat(extra);
        }
        selectElement.innerHTML = '<option value="">-- انتخاب بانک --</option>';
        options.forEach(opt => {
            const optElem = document.createElement('option');
            optElem.value = opt.value;
            optElem.textContent = this.formatPaymentDestinationOption(opt);
            if (opt.value === selectedValue) optElem.selected = true;
            selectElement.appendChild(optElem);
        });
        // C6: "سایر" option triggers manual destination number + account holder.
        const otherOpt = document.createElement('option');
        otherOpt.value = '__other__';
        otherOpt.textContent = 'سایر';
        if (selectedValue === '__other__') otherOpt.selected = true;
        selectElement.appendChild(otherOpt);
    },

    populateCurrencyTypes(selectElement, selectedValue = '') {
        const options = [
            { value: 'IRR', label: 'ریال' },
            { value: 'USD', label: 'دلار' },
            { value: 'EUR', label: 'یورو' },
            { value: 'GBP', label: 'پوند' },
            { value: '__other__', label: 'دیگر' }
        ];
        selectElement.innerHTML = '<option value="">-- انتخاب ارز --</option>';
        options.forEach(opt => {
            const optElem = document.createElement('option');
            optElem.value = opt.value;
            optElem.textContent = opt.label;
            if (opt.value === selectedValue) optElem.selected = true;
            selectElement.appendChild(optElem);
        });
    },

    addPaymentRow(paymentData = null, readonly = null) {
        if (paymentData === null && !this.canActionClient('payments.create')) {
            Swal.fire('خطا', 'دسترسی به ثبت پرداخت ندارید', 'error');
            return;
        }
        const isAdminUser = this.isAdmin();
        if (readonly === null) {
            const hasData = paymentData !== null && (
                paymentData.date || paymentData.date_persian || paymentData.amount || paymentData.method || paymentData.description
            );
            readonly = ((this.state.paymentsLocked && hasData) || (this.dealIsWon && hasData)) && !isAdminUser;
        }
        const container = document.getElementById('payment-rows');
        if (!container) return;

        const rowId = `payment-row-${this.paymentRowCounter++}`;
        const row = document.createElement('div');
        row.id = rowId;
        row.className = 'bg-gray-800/50 p-4 rounded border border-gray-700 relative';

        let dateValue = paymentData?.date_persian || '';
        if (!dateValue && paymentData?.date) {
            try {
                const gDate = new Date(paymentData.date);
                dateValue = !isNaN(gDate.getTime()) ? (new persianDate(gDate)).format('YYYY/MM/DD') : paymentData.date;
            } catch (e) {
                dateValue = paymentData.date || '';
            }
        }

        const amountValue = paymentData?.amount || '';
        const amountDisplay = formatMoneyString(amountValue);
        const methodValue = paymentData?.method || paymentData?.payment_method || '';
        const descriptionValue = paymentData?.description || '';
        const cardLast4Value = paymentData?.card_last4 || paymentData?.card_last_4 || '';
        const bankValue = paymentData?.destination_bank || paymentData?.bank_destination || '';
        const destinationBankOtherValue = paymentData?.destination_bank_other || '';
        const accountOwnerNameValue = paymentData?.account_owner_name || '';
        const depositorNameValue = paymentData?.depositor_name || '';
        const destinationCardValueRaw = (
            paymentData?.destination_card_number === '__other__'
                ? (paymentData?.destination_card_number_other || '')
                : (paymentData?.destination_card_number || paymentData?.destination_card_number_other || '')
        );
        const destinationCardValue = String(destinationCardValueRaw || '').replace(/\D/g, '').slice(-4);
        const destinationSnapshot = this.getPaymentDestinationSnapshot(bankValue || '');
        const destinationBankTitleValue = paymentData?.destination_bank_title || destinationSnapshot.title || '';
        const destinationCardFullValue = paymentData?.destination_card_full || destinationSnapshot.cardFull || '';
        const destinationAccountNumberValue = paymentData?.destination_account_number || destinationSnapshot.accountNumber || '';
        const destinationShebaValue = paymentData?.destination_sheba || destinationSnapshot.sheba || '';
        const paymentTimeValue = this.normalizeTimeValue(paymentData?.payment_time || '');
        const cardReaderDeviceValue = paymentData?.card_reader_device || '';
        const currencyValue = paymentData?.currency || '';
        const currencyOtherValue = paymentData?.currency_other || '';
        const currencyAmountValue = paymentData?.currency_amount || '';
        const checkBankValue = paymentData?.check_bank_name || '';
        const checkDateValue = paymentData?.check_date || '';
        const checkNumberValue = paymentData?.check_number || '';
        const checkOwnerNameValue = paymentData?.check_owner_name || '';
        // C3: registration date (immutable). Gregorian value round-trips in a hidden input;
        // the Jalali form is displayed disabled. New rows show today and are stamped server-side.
        const registeredAtValue = paymentData?.registered_at || '';
        let registeredAtDisplay = '';
        try {
            if (registeredAtValue) {
                const rDate = new Date(String(registeredAtValue).replace(' ', 'T'));
                registeredAtDisplay = !isNaN(rDate.getTime()) ? (new persianDate(rDate)).format('YYYY/MM/DD') : registeredAtValue;
            } else {
                registeredAtDisplay = (new persianDate()).format('YYYY/MM/DD');
            }
        } catch (e) {
            registeredAtDisplay = String(registeredAtValue || '');
        }
        // C4: deposit date (واریز) and cheque due date (سررسید چک)
        const depositDateValue = paymentData?.deposit_date_persian || paymentData?.deposit_date || '';
        const checkDueDateValue = paymentData?.check_due_date_persian || paymentData?.check_due_date || '';
        // C7/C8: Sayadi date, cheque delivery date and recipient name
        const sayadiDateValue = paymentData?.sayadi_date || '';
        const checkDeliveryDateValue = paymentData?.check_delivery_date_persian || paymentData?.check_delivery_date || '';
        const checkRecipientNameValue = paymentData?.check_recipient_name || '';
        // C2: uploaded receipt / cheque image paths (relative to web root).
        const receiptImagePath = paymentData?.receipt_image_path || '';
        const checkImagePath = paymentData?.check_image_path || '';

        const isExistingPayment = paymentData !== null;
        const canEditPaymentByRbac = this.canActionClient('payments.edit');
        const lockExistingForAdmin = isAdminUser && isExistingPayment;
        const effectiveReadonly = !!readonly || (isExistingPayment && !canEditPaymentByRbac) || lockExistingForAdmin;
        const readonlyAttr = effectiveReadonly ? 'readonly' : '';
        const readonlyClass = effectiveReadonly ? 'bg-gray-900 cursor-not-allowed pointer-events-none opacity-70' : '';
        const canDeletePayment = this.canUserDeleteFieldClient('payment_delete');
        const canDeletePaymentByRbac = this.canActionClient('payments.delete');
        const canShowAdminPaymentControls = isAdminUser && isExistingPayment;
        const adminPaymentActions = canShowAdminPaymentControls
            ? `<div class="mb-3 flex flex-wrap gap-2 justify-start">
                    <button type="button" id="${rowId}-edit-btn" onclick="app.togglePaymentRowEdit('${rowId}')" class="text-xs px-2 py-1 rounded border border-yellow-500 text-yellow-400 hover:bg-yellow-500/10">ویرایش پرداخت</button>
                    <button type="button" onclick="app.removePaymentRow('${rowId}')" class="text-xs px-2 py-1 rounded border border-red-500 text-red-400 hover:bg-red-500/10">حذف پرداخت</button>
               </div>`
            : '';
        const removeButton = (canShowAdminPaymentControls || effectiveReadonly || !canDeletePayment || !canDeletePaymentByRbac)
            ? ''
            : `<button type="button" onclick="app.removePaymentRow('${rowId}')" class="absolute top-2 left-2 text-red-500 hover:text-red-400 text-sm"><i class="fa-solid fa-times"></i></button>`;

        row.innerHTML = `
            ${adminPaymentActions}
            ${removeButton}
            <div class="space-y-3 ${effectiveReadonly ? 'pr-0' : 'pr-6'}">
                <div class="grid grid-cols-2 gap-3">
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block">تاریخ ثبت <span class="text-gray-500">(قفل‌شده)</span></label>
                        <input type="text" class="input bg-gray-900 cursor-not-allowed opacity-70" value="${registeredAtDisplay}" disabled>
                        <input type="hidden" name="payment_registered_at[]" value="${registeredAtValue}">
                    </div>
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block">نحوه پرداخت</label>
                        <select name="payment_method[]" class="input bg-gray-800 payment-method-select ${readonlyClass}">
                            <option value="">-- انتخاب نحوه پرداخت --</option>
                        </select>
                    </div>
                </div>
                <div class="grid grid-cols-2 gap-3">
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block payment-date-label">${this.isCheckMethod(methodValue) ? 'تاریخ سررسید چک (شمسی)' : 'تاریخ واریز (شمسی)'}</label>
                        <input type="text" name="payment_date[]" class="input payment-date-jalali ${readonlyClass}" placeholder="1403/01/01" value="${dateValue}" ${readonlyAttr}>
                    </div>
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block">مبلغ پرداخت (تومان)</label>
                        <input type="text" inputmode="numeric" name="payment_amount[]" class="input money-input ${readonlyClass}" placeholder="0" value="${amountDisplay}" ${readonlyAttr} autocomplete="off">
                    </div>
                </div>
                <div class="grid grid-cols-2 gap-3">
                    <div class="payment-card-last4 ${(this.isCardToCardMethod(methodValue) || this.isCardReaderMethod(methodValue) || this.isShebaMethod(methodValue)) ? '' : 'hidden'}">
                        <label class="text-xs text-gray-400 mb-1 block">4 رقم آخر کارت مبدا</label>
                        <input type="text" name="payment_card_last4[]" inputmode="numeric" maxlength="4" pattern="\\d{4}" class="input ${effectiveReadonly ? readonlyClass : ''}" placeholder="1234" value="${cardLast4Value}" ${readonlyAttr}>
                    </div>
                    <div class="payment-depositor ${(this.isCardToCardMethod(methodValue) || this.isCardReaderMethod(methodValue) || this.isShebaMethod(methodValue)) ? '' : 'hidden'}">
                        <label class="text-xs text-gray-400 mb-1 block">نام و نام خانوادگی واریز‌کننده</label>
                        <input type="text" name="depositor_name[]" class="input ${effectiveReadonly ? readonlyClass : ''}" value="${depositorNameValue}" placeholder="نام و نام خانوادگی واریز‌کننده" ${readonlyAttr}>
                    </div>
                </div>
                <div class="payment-bank ${(this.isCardToCardMethod(methodValue) || this.isShebaMethod(methodValue)) ? '' : 'hidden'}">
                    <label class="text-xs text-gray-400 mb-1 block">نام بانک مقصد</label>
                    <select name="destination_bank[]" class="input bg-gray-800 destination-bank-select w-full ${readonlyClass}">
                        <option value="">-- انتخاب بانک --</option>
                    </select>
                </div>
                <div class="payment-bank-other ${(this.isCardToCardMethod(methodValue) || this.isShebaMethod(methodValue)) && bankValue === '__other__' ? '' : 'hidden'}">
                    <div class="grid grid-cols-2 gap-3">
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">شماره بانک/حساب مقصد <span class="text-red-500">*</span></label>
                            <input type="text" name="destination_bank_other[]" class="input ${effectiveReadonly ? readonlyClass : ''}" value="${destinationBankOtherValue}" placeholder="شماره بانک مقصد" ${readonlyAttr}>
                        </div>
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">نام صاحب حساب <span class="text-red-500">*</span></label>
                            <input type="text" name="account_owner_name[]" class="input ${effectiveReadonly ? readonlyClass : ''}" value="${accountOwnerNameValue}" placeholder="نام صاحب حساب" ${readonlyAttr}>
                        </div>
                    </div>
                </div>
                <div class="payment-destination-details ${(this.isCardToCardMethod(methodValue) || this.isShebaMethod(methodValue)) && bankValue && bankValue !== '__other__' ? '' : 'hidden'} rounded border border-gray-700 bg-gray-900/50 p-3">
                    <input type="hidden" name="destination_card_number[]" value="${destinationCardValue}">
                    <input type="hidden" name="destination_bank_title[]" value="${this.escapeHtml(destinationBankTitleValue)}">
                    <input type="hidden" name="destination_card_full[]" value="${this.escapeHtml(destinationCardFullValue)}">
                    <input type="hidden" name="destination_account_number[]" value="${this.escapeHtml(destinationAccountNumberValue)}">
                    <input type="hidden" name="destination_sheba[]" value="${this.escapeHtml(destinationShebaValue)}">
                    <div class="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs">
                        <div>
                            <div class="text-gray-500 mb-1">کارت مقصد</div>
                            <div class="text-gray-200 font-mono ltr payment-destination-card-display">${this.escapeHtml(destinationCardFullValue || '-')}</div>
                        </div>
                        <div>
                            <div class="text-gray-500 mb-1">شماره حساب</div>
                            <div class="text-gray-200 font-mono ltr payment-destination-account-display">${this.escapeHtml(destinationAccountNumberValue || '-')}</div>
                        </div>
                        <div>
                            <div class="text-gray-500 mb-1">شبا</div>
                            <div class="text-gray-200 font-mono ltr payment-destination-sheba-display">${this.escapeHtml(destinationShebaValue || '-')}</div>
                        </div>
                    </div>
                </div>
                <div class="payment-card-reader-extra ${(this.isCardReaderMethod(methodValue)) ? '' : 'hidden'}">
                    <label class="text-xs text-gray-400 mb-1 block">نام دستگاه</label>
                    <select name="card_reader_device[]" class="input bg-gray-800 ${readonlyClass}">
                        <option value="">-- انتخاب دستگاه --</option>
                        <option value="academy" ${cardReaderDeviceValue === 'academy' ? 'selected' : ''}>آکادمی</option>
                        <option value="showroom" ${cardReaderDeviceValue === 'showroom' ? 'selected' : ''}>نمایشگاه</option>
                    </select>
                </div>
                <div class="payment-cash-extra ${(this.isCashMethod(methodValue)) ? '' : 'hidden'} space-y-3">
                    <div class="grid grid-cols-2 gap-3">
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">نوع ارز</label>
                            <select name="payment_currency[]" class="input bg-gray-800 payment-currency-select ${readonlyClass}"></select>
                        </div>
                        <div class="payment-currency-other ${currencyValue === '__other__' ? '' : 'hidden'}">
                            <label class="text-xs text-gray-400 mb-1 block">واحد ارز (دستی)</label>
                            <input type="text" name="payment_currency_other[]" class="input ${effectiveReadonly ? readonlyClass : ''}" value="${currencyOtherValue}" placeholder="مثال: درهم" ${readonlyAttr}>
                        </div>
                    </div>
                    <div class="payment-currency-amount ${currencyValue && currencyValue !== 'IRR' ? '' : 'hidden'}">
                        <label class="text-xs text-gray-400 mb-1 block">مبلغ ارز</label>
                        <input type="text" name="payment_currency_amount[]" class="input money-input ${effectiveReadonly ? readonlyClass : ''}" value="${currencyAmountValue}" placeholder="مبلغ به واحد ارز" ${readonlyAttr}>
                    </div>
                </div>
                <div class="payment-check-extra ${(this.isCheckMethod(methodValue)) ? '' : 'hidden'} space-y-3">
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block">نام بانک مبدا <span class="text-gray-500">(پس از ثبت قفل)</span></label>
                        <input type="text" name="check_bank_name[]" class="input check-lock-field ${effectiveReadonly ? readonlyClass : ''}" value="${checkBankValue}" placeholder="نام بانک مبدا" ${readonlyAttr}>
                    </div>
                    <div class="grid grid-cols-2 gap-3">
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">تاریخ ثبت صیادی <span class="text-gray-500">(پس از ثبت قفل)</span></label>
                            <input type="text" name="sayadi_date[]" class="input payment-sayadi-date-jalali check-lock-field ${effectiveReadonly ? readonlyClass : ''}" value="${sayadiDateValue}" placeholder="1403/01/01" ${readonlyAttr}>
                        </div>
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">شماره چک <span class="text-gray-500">(پس از ثبت قفل)</span></label>
                            <input type="text" name="check_number[]" class="input check-lock-field ${effectiveReadonly ? readonlyClass : ''}" value="${checkNumberValue}" placeholder="شماره چک" ${readonlyAttr}>
                        </div>
                    </div>
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block">نام صاحب چک <span class="text-gray-500">(پس از ثبت قفل)</span></label>
                        <input type="text" name="check_owner_name[]" class="input check-lock-field ${effectiveReadonly ? readonlyClass : ''}" value="${checkOwnerNameValue}" placeholder="نام صاحب چک" ${readonlyAttr}>
                    </div>
                    <div class="grid grid-cols-2 gap-3 border-t border-gray-700 pt-3">
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">تاریخ تحویل چک <span class="text-gray-500">(اختیاری، پس از ثبت قفل)</span></label>
                            <input type="text" name="check_delivery_date[]" class="input payment-check-delivery-date-jalali check-delivery-lock-field ${effectiveReadonly ? readonlyClass : ''}" value="${checkDeliveryDateValue}" placeholder="1403/01/01" ${readonlyAttr}>
                        </div>
                        <div>
                            <label class="text-xs text-gray-400 mb-1 block">نام تحویل‌گیرنده <span class="text-gray-500">(اختیاری، پس از ثبت قفل)</span></label>
                            <input type="text" name="check_recipient_name[]" class="input check-delivery-lock-field ${effectiveReadonly ? readonlyClass : ''}" value="${checkRecipientNameValue}" placeholder="نام تحویل‌گیرنده" ${readonlyAttr}>
                        </div>
                    </div>
                    <div>
                        <label class="text-xs text-gray-400 mb-1 block">تصویر چک (تصویر/PDF)</label>
                        <input type="hidden" name="check_image_path[]" value="${checkImagePath}">
                        <input type="file" accept="image/*,application/pdf" class="input text-xs ${effectiveReadonly ? 'pointer-events-none opacity-60' : ''}" onchange="app.uploadPaymentFile(this, 'check')" ${effectiveReadonly ? 'disabled' : ''}>
                        <a href="${checkImagePath || '#'}" target="_blank" rel="noopener" class="payment-check-link text-xs text-blue-400 hover:underline ${checkImagePath ? '' : 'hidden'}">مشاهده تصویر چک</a>
                    </div>
                </div>
                <div class="payment-time-wrap ${(this.isCardToCardMethod(methodValue) || this.isShebaMethod(methodValue) || this.isCardReaderMethod(methodValue)) ? '' : 'hidden'}">
                    <label class="text-xs text-gray-400 mb-1 block">ساعت پرداخت</label>
                    <input type="text" inputmode="numeric" maxlength="5" name="payment_time[]" class="input payment-time-input payment-time-lock w-full ${effectiveReadonly ? readonlyClass : ''}" value="${paymentTimeValue}" placeholder="مثال: 14:30" ${readonlyAttr}>
                </div>
                <div class="payment-receipt-upload">
                    <label class="text-xs text-gray-400 mb-1 block">فیش واریزی (تصویر/PDF)</label>
                    <input type="hidden" name="receipt_image_path[]" value="${receiptImagePath}">
                    <input type="file" accept="image/*,application/pdf" class="input text-xs ${effectiveReadonly ? 'pointer-events-none opacity-60' : ''}" onchange="app.uploadPaymentFile(this, 'receipt')" ${effectiveReadonly ? 'disabled' : ''}>
                    <a href="${receiptImagePath || '#'}" target="_blank" rel="noopener" class="payment-receipt-link text-xs text-blue-400 hover:underline ${receiptImagePath ? '' : 'hidden'}">مشاهده فیش بارگذاری‌شده</a>
                </div>
                <div>
                    <label class="text-xs text-gray-400 mb-1 block">توضیح پرداخت</label>
                    <textarea name="payment_description[]" class="input h-20 resize-none ${readonlyClass}" placeholder="توضیحات این پرداخت" ${readonlyAttr}>${descriptionValue}</textarea>
                </div>
            </div>
        `;

        container.appendChild(row);
        row.dataset.existingPayment = isExistingPayment ? '1' : '0';
        row.dataset.adminLocked = lockExistingForAdmin ? '1' : '0';
        row.dataset.adminEditing = '0';

        const methodSelect = row.querySelector('.payment-method-select');
        const bankSelect = row.querySelector('.destination-bank-select');
        const cardInput = row.querySelector('input[name="payment_card_last4[]"]');
        const destinationCardInput = row.querySelector('input[name="destination_card_number[]"]');
        const currencySelect = row.querySelector('.payment-currency-select');
        const timeInput = row.querySelector('input[name="payment_time[]"]');
        if (methodSelect) this.populatePaymentMethods(methodSelect, methodValue);
        if (bankSelect) this.populateDestinationBanks(bankSelect, bankValue || '');
        if (currencySelect) this.populateCurrencyTypes(currencySelect, currencyValue || '');

        if (!effectiveReadonly && timeInput && !paymentTimeValue) {
            this.ensurePaymentTimeDefault(timeInput);
        }

        const canEditMethod = this.canUserEditFieldClient('payment_method', isExistingPayment);
        const canEditCard = this.canUserEditFieldClient('payment_card_last4', isExistingPayment);
        const canEditBank = this.canUserEditFieldClient('destination_bank', isExistingPayment);
        const disableSelect = (el) => { if (el) { el.disabled = true; el.classList.add('bg-gray-900', 'cursor-not-allowed', 'opacity-70'); } };
        const disableInput = (el) => { if (el) { el.readOnly = true; el.classList.add('bg-gray-900', 'cursor-not-allowed', 'opacity-70', 'pointer-events-none'); } };

        if (!canEditMethod && methodSelect) disableSelect(methodSelect);
        if (!canEditCard && cardInput) disableInput(cardInput);
        if (!canEditBank && bankSelect) disableSelect(bankSelect);

        // C7/C8: once a payment is registered, cheque identity fields (bank/owner/number/Sayadi)
        // and the optional delivery fields lock for the sales expert. Admins can re-open the
        // whole row via the "ویرایش پرداخت" toggle (togglePaymentRowEdit re-enables all inputs).
        if (isExistingPayment && !isAdminUser) {
            row.querySelectorAll('.check-lock-field, .check-delivery-lock-field, .payment-time-lock').forEach(disableInput);
        }

        const togglePaymentFields = () => {
            const method = methodSelect?.value || '';
            const isCardTransfer = this.isCardToCardMethod(method) || this.isShebaMethod(method);
            const isCardReader = this.isCardReaderMethod(method);
            const isCardMethod = isCardTransfer || isCardReader;
            const isCash = this.isCashMethod(method);
            const isCheck = this.isCheckMethod(method);

            const cardWrap = row.querySelector('.payment-card-last4');
            const depositorWrap = row.querySelector('.payment-depositor');
            const depositorInput = row.querySelector('input[name="depositor_name[]"]');
            const bankWrap = row.querySelector('.payment-bank');
            const bankOtherWrap = row.querySelector('.payment-bank-other');
            const bankOtherInput = row.querySelector('input[name="destination_bank_other[]"]');
            const destinationDetailsWrap = row.querySelector('.payment-destination-details');
            const destinationBankTitleInput = row.querySelector('input[name="destination_bank_title[]"]');
            const destinationCardFullInput = row.querySelector('input[name="destination_card_full[]"]');
            const destinationAccountInput = row.querySelector('input[name="destination_account_number[]"]');
            const destinationShebaInput = row.querySelector('input[name="destination_sheba[]"]');
            const destinationCardDisplay = row.querySelector('.payment-destination-card-display');
            const destinationAccountDisplay = row.querySelector('.payment-destination-account-display');
            const destinationShebaDisplay = row.querySelector('.payment-destination-sheba-display');
            const transferWrap = row.querySelector('.payment-transfer-extra');
            const cardReaderWrap = row.querySelector('.payment-card-reader-extra');
            const cashWrap = row.querySelector('.payment-cash-extra');
            const checkWrap = row.querySelector('.payment-check-extra');
            const receiptUploadWrap = row.querySelector('.payment-receipt-upload');
            const timeWrap = row.querySelector('.payment-time-wrap');
            const timeInputLocal = row.querySelector('input[name="payment_time[]"]');
            const currencyOtherWrap = row.querySelector('.payment-currency-other');
            const currencyOtherInput = row.querySelector('input[name="payment_currency_other[]"]');
            const currencyAmountWrap = row.querySelector('.payment-currency-amount');
            const currencyAmountInput = row.querySelector('input[name="payment_currency_amount[]"]');
            const checkBankInput = row.querySelector('input[name="check_bank_name[]"]');
            const checkDateInput = row.querySelector('input[name="check_date[]"]');
            const checkNumberInput = row.querySelector('input[name="check_number[]"]');
            const checkOwnerInput = row.querySelector('input[name="check_owner_name[]"]');
            const accountOwnerInput = row.querySelector('input[name="account_owner_name[]"]');
            const depositDateWrap = row.querySelector('.payment-deposit-date');
            const depositDateInput = row.querySelector('input[name="deposit_date[]"]');
            const checkDueDateInput = row.querySelector('input[name="check_due_date[]"]');
            const sayadiDateInput = row.querySelector('input[name="sayadi_date[]"]');
            const checkDeliveryDateInput = row.querySelector('input[name="check_delivery_date[]"]');
            const checkRecipientInput = row.querySelector('input[name="check_recipient_name[]"]');

            const existingRow = row.dataset.existingPayment === '1';
            const requireCurrentRowFields = false;

            if (cardWrap) cardWrap.classList.toggle('hidden', !isCardMethod);
            if (cardInput) {
                cardInput.required = requireCurrentRowFields && isCardMethod;
                if (!isCardMethod) cardInput.value = '';
            }
            // نام واریز‌کننده برای روش‌های کارتی (کارت به کارت/کارتخوان/شبا) نمایش داده می‌شود.
            if (depositorWrap) depositorWrap.classList.toggle('hidden', !isCardMethod);
            if (depositorInput && !isCardMethod) depositorInput.value = '';
            // برای چک، فیلد بالای فرم به‌جای «تاریخ واریز» نقش «تاریخ سررسید چک» را دارد.
            const dateLabel = row.querySelector('.payment-date-label');
            if (dateLabel) dateLabel.textContent = isCheck ? 'تاریخ سررسید چک (شمسی)' : 'تاریخ واریز (شمسی)';
            if (bankWrap) bankWrap.classList.toggle('hidden', !isCardTransfer);
            if (bankSelect) {
                bankSelect.required = requireCurrentRowFields && isCardTransfer;
                if (!isCardTransfer) bankSelect.value = '';
            }
            const bankIsOther = bankSelect?.value === '__other__';
            // C6: "سایر" destination → manual destination number + account holder (required).
            if (bankOtherWrap) bankOtherWrap.classList.toggle('hidden', !isCardTransfer || !bankIsOther);
            if (bankOtherInput) {
                bankOtherInput.required = isCardTransfer && bankIsOther;
                if (!isCardTransfer || !bankIsOther) bankOtherInput.value = '';
            }
            if (accountOwnerInput) {
                accountOwnerInput.required = isCardTransfer && bankIsOther;
                if (!isCardTransfer || !bankIsOther) accountOwnerInput.value = '';
            }
            const showDestinationDetails = isCardTransfer && !!bankSelect?.value && !bankIsOther;
            if (destinationDetailsWrap) destinationDetailsWrap.classList.toggle('hidden', !showDestinationDetails);
            if (showDestinationDetails) {
                const snapshot = this.getPaymentDestinationSnapshot(bankSelect.value);
                if (destinationCardInput) destinationCardInput.value = snapshot.cardLast4 || '';
                if (destinationBankTitleInput) destinationBankTitleInput.value = snapshot.title || '';
                if (destinationCardFullInput) destinationCardFullInput.value = snapshot.cardFull || '';
                if (destinationAccountInput) destinationAccountInput.value = snapshot.accountNumber || '';
                if (destinationShebaInput) destinationShebaInput.value = snapshot.sheba || '';
                if (destinationCardDisplay) destinationCardDisplay.textContent = snapshot.cardFull || '-';
                if (destinationAccountDisplay) destinationAccountDisplay.textContent = snapshot.accountNumber || '-';
                if (destinationShebaDisplay) destinationShebaDisplay.textContent = snapshot.sheba || '-';
            } else {
                [destinationCardInput, destinationBankTitleInput, destinationCardFullInput, destinationAccountInput, destinationShebaInput].forEach(input => {
                    if (input) input.value = '';
                });
                if (destinationCardDisplay) destinationCardDisplay.textContent = '-';
                if (destinationAccountDisplay) destinationAccountDisplay.textContent = '-';
                if (destinationShebaDisplay) destinationShebaDisplay.textContent = '-';
            }
            // C4: deposit date applies to deposits/transfers and cash, not cheques.
            if (depositDateWrap) depositDateWrap.classList.toggle('hidden', !(isCardTransfer || isCardReader || isCash));
            if (depositDateInput && !(isCardTransfer || isCardReader || isCash)) depositDateInput.value = '';

            if (transferWrap) transferWrap.classList.toggle('hidden', !isCardTransfer);
            if (destinationCardInput) {
                destinationCardInput.required = false;
                if (!isCardTransfer) destinationCardInput.value = '';
            }
            if (cardReaderWrap) cardReaderWrap.classList.toggle('hidden', !isCardReader);
            if (cashWrap) cashWrap.classList.toggle('hidden', !isCash);
            if (checkWrap) checkWrap.classList.toggle('hidden', !isCheck);
            // For cheque, the cheque image (تصویر چک) is enough — the deposit receipt
            // (فیش واریزی) is irrelevant, so hide its upload for cheque payments.
            if (receiptUploadWrap) receiptUploadWrap.classList.toggle('hidden', isCheck);
            if (timeWrap) timeWrap.classList.toggle('hidden', !(isCardTransfer || isCardReader));
            if (timeInputLocal) {
                const needsTime = (isCardTransfer || isCardReader);
                timeInputLocal.required = requireCurrentRowFields && needsTime;
                if (!needsTime) {
                    timeInputLocal.value = '';
                } else if (!effectiveReadonly) {
                    this.ensurePaymentTimeDefault(timeInputLocal);
                }
            }

            const currencyValueNow = currencySelect?.value || '';
            const currencyOther = currencyValueNow === '__other__';
            const showCurrencyAmount = !!currencyValueNow && currencyValueNow !== 'IRR';
            if (currencyOtherWrap) currencyOtherWrap.classList.toggle('hidden', !isCash || !currencyOther);
            if (currencyOtherInput) {
                currencyOtherInput.required = requireCurrentRowFields && isCash && currencyOther;
                if (!isCash || !currencyOther) currencyOtherInput.value = '';
            }
            if (currencyAmountWrap) currencyAmountWrap.classList.toggle('hidden', !isCash || !showCurrencyAmount);
            if (currencyAmountInput) {
                currencyAmountInput.required = requireCurrentRowFields && isCash && showCurrencyAmount;
                if (!isCash || !showCurrencyAmount) currencyAmountInput.value = '';
            }
            // C7: cheque identity fields + payment date required when method is cheque.
            [checkBankInput, checkNumberInput, checkOwnerInput, sayadiDateInput].forEach((input) => {
                if (!input) return;
                input.required = isCheck;
                if (!isCheck) input.value = '';
            });
            if (checkDateInput) {
                checkDateInput.required = isCheck;
                if (!isCheck) checkDateInput.value = '';
            }
            // C4 (cheque due) + C8 (delivery, optional): clear when not a cheque.
            [checkDueDateInput, checkDeliveryDateInput, checkRecipientInput].forEach((input) => {
                if (input && !isCheck) input.value = '';
            });
        };

        if (methodSelect) methodSelect.addEventListener('change', togglePaymentFields);
        if (bankSelect) bankSelect.addEventListener('change', togglePaymentFields);
        if (currencySelect) currencySelect.addEventListener('change', togglePaymentFields);
        if (timeInput) {
            // ساعت پرداخت دستی وارد می‌شود؛ روی blur فقط فرمت HH:MM نرمال‌سازی می‌شود
            // (روی focus دیگر مقدار سیستمی پر نمی‌کنیم تا با ورود دستی کاربر تداخل نکند).
            timeInput.addEventListener('blur', () => this.ensurePaymentTimeDefault(timeInput));
        }
        togglePaymentFields();

        setTimeout(() => {
            const initPicker = (input) => {
                if (!input) return;
                const start = () => {
                    if (typeof window.$ !== 'undefined' && typeof window.$.fn !== 'undefined' && typeof window.$.fn.persianDatepicker !== 'undefined') {
                        try {
                            if (window.$(input).data('persianDatepicker')) window.$(input).persianDatepicker('destroy');
                            window.$(input).persianDatepicker({
                                observer: true,
                                format: 'YYYY/MM/DD',
                                calendarType: 'persian',
                                initialValue: false,
                                autoClose: true
                            });
                        } catch (e) {
                            console.error('Error initializing date picker:', e);
                        }
                    } else {
                        setTimeout(start, 200);
                    }
                };
                start();
            };
            initPicker(row.querySelector('.payment-date-jalali'));
            initPicker(row.querySelector('.payment-sayadi-date-jalali'));
            initPicker(row.querySelector('.payment-check-delivery-date-jalali'));
        }, 200);
    },

    togglePaymentRowEdit(rowId) {
        if (!this.isAdmin()) return;
        const row = document.getElementById(rowId);
        if (!row || row.dataset.existingPayment !== '1') return;

        const editBtn = document.getElementById(`${rowId}-edit-btn`);
        const isEditing = row.dataset.adminEditing === '1';
        const shouldEdit = !isEditing;
        const controls = row.querySelectorAll('input, select, textarea');
        controls.forEach((el) => {
            const tag = (el.tagName || '').toLowerCase();
            if (tag === 'select') {
                el.disabled = !shouldEdit;
            } else {
                el.readOnly = !shouldEdit;
            }
            el.classList.toggle('pointer-events-none', !shouldEdit);
            el.classList.toggle('cursor-not-allowed', !shouldEdit);
            el.classList.toggle('opacity-70', !shouldEdit);
            el.classList.toggle('bg-gray-900', !shouldEdit);
        });
        row.dataset.adminEditing = shouldEdit ? '1' : '0';
        if (editBtn) {
            editBtn.textContent = shouldEdit ? 'پایان ویرایش' : 'ویرایش پرداخت';
        }
    },
    
    removePaymentRow(rowId) {
        if (!this.canActionClient('payments.delete')) {
            Swal.fire('خطا', 'دسترسی به حذف پرداخت ندارید', 'error');
            return;
        }
        const row = document.getElementById(rowId);
        if (row) {
            row.remove();
        }
    },
    
    enableDealFormFields() {
        // Remove readonly/disabled state from all form fields
        const readonlyClasses = ['bg-gray-900', 'cursor-not-allowed'];
        const fields = [
            'deal-title',
            'deal-has-discount',
            'deal-discount-type',
            'deal-discount-occasion',
            'deal-discount-occasion-custom',
            'deal-discount-amount',
            'deal-payment-conditions'
        ];
        
        fields.forEach(fieldId => {
            const field = document.getElementById(fieldId);
            if (field) {
                if (field.tagName === 'SELECT') {
                    field.disabled = false;
                } else {
                    field.readOnly = false;
                }
                // Remove each class separately
                readonlyClasses.forEach(className => {
                    field.classList.remove(className);
                });
            }
        });

        ['deal-status', 'deal-pipeline-stage'].forEach((fieldId) => {
            const field = document.getElementById(fieldId);
            if (!field) return;
            field.disabled = false;
            readonlyClasses.forEach(className => field.classList.remove(className));
        });

        const productsContainer = document.getElementById('deal-products-list');
        if (productsContainer) {
            productsContainer.querySelectorAll('input[name="product_ids[]"]').forEach((checkbox) => {
                checkbox.disabled = false;
                checkbox.classList.remove('cursor-not-allowed', 'opacity-70');
            });
        }
        const addProductBtn = document.querySelector('button[onclick="app.addProductPrompt()"]');
        if (addProductBtn) {
            addProductBtn.disabled = false;
            addProductBtn.classList.remove('opacity-50', 'pointer-events-none');
        }
        this.setDealServiceCostLookupLock(true);
    },

    dealHasRegisteredPayments(deal = null) {
        const targetDeal = deal || this.state.currentDeal || {};
        if (targetDeal.has_registered_payments === true) return true;
        const payments = Array.isArray(targetDeal.payments) ? targetDeal.payments : [];
        return payments.some((payment) => parseMoneyValue(payment?.amount || 0) > 0);
    },

    setDealInfoLockState(locked, options = {}) {
        const isLocked = !!locked;
        const allowAdminUnlock = options.allowAdminUnlock === true;
        const readonlyClasses = ['bg-gray-900', 'cursor-not-allowed', 'opacity-70'];
        const fieldSpecs = [
            { id: 'deal-title', type: 'select' },
            { id: 'deal-service-cost', type: 'input' },
            { id: 'deal-price-list-code', type: 'input' },
            { id: 'deal-has-discount', type: 'select' },
            { id: 'deal-discount-type', type: 'select' },
            { id: 'deal-discount-occasion', type: 'select' },
            { id: 'deal-discount-occasion-custom', type: 'input' },
            { id: 'deal-discount-amount', type: 'input' },
            { id: 'deal-refund-amount', type: 'input' },
            { id: 'deal-refund-description', type: 'textarea' },
            { id: 'deal-payable-amount', type: 'input' },
            { id: 'deal-pipeline-stage', type: 'select' }
        ];

        fieldSpecs.forEach(({ id, type }) => {
            const field = document.getElementById(id);
            if (!field) return;
            if (['deal-service-cost', 'deal-price-list-code', 'deal-payable-amount'].includes(id)) {
                return;
            }
            if (type === 'select') {
                field.disabled = isLocked;
            } else {
                field.readOnly = isLocked;
            }
            readonlyClasses.forEach((className) => field.classList.toggle(className, isLocked));
        });

        const paymentConditionsField = document.getElementById('deal-payment-conditions');
        if (paymentConditionsField) {
            paymentConditionsField.readOnly = false;
            readonlyClasses.forEach((className) => paymentConditionsField.classList.remove(className));
        }
        const dealStatusField = document.getElementById('deal-status');
        if (dealStatusField) {
            dealStatusField.disabled = false;
            readonlyClasses.forEach((className) => dealStatusField.classList.remove(className));
        }

        const productsContainer = document.getElementById('deal-products-list');
        if (productsContainer) {
            productsContainer.querySelectorAll('input[name="product_ids[]"]').forEach((checkbox) => {
                checkbox.disabled = isLocked;
                checkbox.classList.toggle('cursor-not-allowed', isLocked);
                checkbox.classList.toggle('opacity-70', isLocked);
            });
        }
        const addProductBtn = document.querySelector('button[onclick="app.addProductPrompt()"]');
        if (addProductBtn) {
            addProductBtn.disabled = isLocked;
            addProductBtn.classList.toggle('opacity-50', isLocked);
            addProductBtn.classList.toggle('pointer-events-none', isLocked);
        }
        if (!isLocked && allowAdminUnlock && this.isAdmin() && this.dealHasRegisteredPayments()) {
            this.setDealServiceCostLookupLock(false);
        } else {
            this.setDealServiceCostLookupLock(true);
        }

        this.state.dealInfoLocked = isLocked;
        const paidEditBtn = document.getElementById('deal-paid-edit-toggle-btn');
        if (paidEditBtn) {
            const showBtn = allowAdminUnlock && this.isAdmin();
            paidEditBtn.classList.toggle('hidden', !showBtn);
            paidEditBtn.textContent = isLocked ? 'ویرایش اطلاعات قفل‌شده' : 'پایان ویرایش اطلاعات';
        }

        // The deal service ("خدمات قابل ارائه" = the deal-title) may only be set when the deal is
        // being created. Once it exists / has been submitted, it is read-only for non-admins —
        // an agent/کارشناس must not be able to change which service the deal is for. Applied last so
        // it also covers open/Pending deals that the lock logic above would otherwise leave editable.
        if (!(this.isAdmin() || this.isSystemRole())) {
            const cur = this.state.currentDeal || null;
            const isExistingDeal = !!(cur && (cur.id || cur.didar_deal_id || cur.deal_id));
            const titleEl = document.getElementById('deal-title');
            if (isExistingDeal && titleEl) {
                titleEl.disabled = true;
                titleEl.classList.add('bg-gray-900', 'cursor-not-allowed');
            }
        }
    },

    togglePaidDealInfoEdit() {
        if (!this.isAdmin() || !this.dealHasRegisteredPayments()) {
            return;
        }
        const nextUnlocked = !this.state.dealInfoAdminUnlocked;
        this.state.dealInfoAdminUnlocked = nextUnlocked;
        this.setDealInfoLockState(!nextUnlocked, { allowAdminUnlock: true });
    },
    
    loadPayments(payments, readonly = null, addDefault = false) {
        const container = document.getElementById('payment-rows');
        if (!container) return;
        container.innerHTML = '';
        const hasData = (p) => {
            if (!p) return false;
            const amountVal = parseMoneyValue(p.amount || 0);
            const hasMethod = !!(p.method && String(p.method).trim() !== '');
            const hasDesc = !!(p.description && String(p.description).trim() !== '');
            const hasDate = !!(p.date && String(p.date).trim() !== '') || !!(p.date_persian && String(p.date_persian).trim() !== '');
            return amountVal > 0 || hasMethod || hasDesc || (hasDate && amountVal > 0);
        };
        const normalized = Array.isArray(payments) ? payments.filter(hasData) : [];
        this.state.lastPaymentsCache = JSON.parse(JSON.stringify(normalized));
        
        if (normalized && normalized.length > 0) {
            normalized.forEach(payment => {
                // Pass null to addPaymentRow so it can decide readonly based on payment data, dealIsWon, and paymentsLocked
                this.addPaymentRow(payment, readonly);
            });
        } else if (addDefault) {
            // Add one empty row by default (always editable) when explicitly allowed
            this.addPaymentRow(null, false);
        }
    },
    
    // TOGGLE DISCOUNT FIELDS
    toggleDiscountFields() {
        const hasDiscount = document.getElementById('deal-has-discount').value === '1';
        const discountFields = document.getElementById('discount-fields');
        if (hasDiscount) {
            discountFields.classList.remove('hidden');
        } else {
            discountFields.classList.add('hidden');
            const type = document.getElementById('deal-discount-type');
            const occasion = document.getElementById('deal-discount-occasion');
            const custom = document.getElementById('deal-discount-occasion-custom');
            const amount = document.getElementById('deal-discount-amount');
            const refundAmount = document.getElementById('deal-refund-amount');
            const refundDesc = document.getElementById('deal-refund-description');
            if (type) type.value = '';
            if (occasion) occasion.value = '';
            if (custom) {
                custom.value = '';
                custom.classList.add('hidden');
                custom.required = false;
            }
            if (amount) amount.value = '';
            if (refundAmount) refundAmount.value = '';
            if (refundDesc) refundDesc.value = '';
        }
        this.toggleDiscountOccasion();
        this.applyDealServiceLookupPricing();
    },
    
    // TOGGLE DISCOUNT OCCASION
    toggleDiscountOccasion() {
        const discountType = document.getElementById('deal-discount-type').value;
        const occasionField = document.getElementById('discount-occasion-field');
        const refundFields = document.getElementById('refund-fields');
        const refundAmount = document.getElementById('deal-refund-amount');
        const refundDesc = document.getElementById('deal-refund-description');
        const discountAmount = document.getElementById('deal-discount-amount');
        if (discountType === 'occasion') {
            occasionField.classList.remove('hidden');
        } else {
            occasionField.classList.add('hidden');
        }
        const isRefund = discountType === 'refund';
        if (refundFields) refundFields.classList.toggle('hidden', !isRefund);
        if (refundAmount) refundAmount.required = isRefund;
        if (refundDesc) refundDesc.required = isRefund;
        if (discountAmount) discountAmount.classList.toggle('hidden', isRefund);
        if (!isRefund) {
            if (refundAmount) refundAmount.value = '';
            if (refundDesc) refundDesc.value = '';
        }
        this.toggleCustomOccasion();
        this.applyDealServiceLookupPricing();
    },
    
    // TOGGLE CUSTOM OCCASION
    toggleCustomOccasion() {
        const discountType = document.getElementById('deal-discount-type')?.value || '';
        if (discountType === 'refund') {
            const customField = document.getElementById('deal-discount-occasion-custom');
            if (customField) {
                customField.classList.add('hidden');
                customField.required = false;
                customField.value = '';
            }
            const occasionSelect = document.getElementById('deal-discount-occasion');
            if (occasionSelect) {
                occasionSelect.value = '';
            }
            return;
        }
        const occasion = document.getElementById('deal-discount-occasion').value;
        const customField = document.getElementById('deal-discount-occasion-custom');
        if (occasion === 'other') {
            customField.classList.remove('hidden');
            customField.required = true;
        } else {
            customField.classList.add('hidden');
            customField.required = false;
        }
    },
    
    // USERS
    async loadUsers() {
        if (this.isAdmin()) {
            try {
                const rbacRes = await this.req('rbac_catalog');
                if (rbacRes.status === 'success') {
                    this.state.rbacCatalog = {
                        roles: rbacRes.roles || [],
                        capabilities: rbacRes.capabilities || []
                    };
                    this.applyLookupsToStaticSelects();
                }
            } catch (e) {
                console.warn('RBAC catalog load failed for users page:', e);
            }
        }
        const res = await this.req('get_users_list');
        if(res.status === 'success') {
            window.didarUsers = res.didar_users || [];
            this.state.masterPasswordConfigured = !!res.master_password_configured;
            this.renderUsers(res.users || [], res.crm_owner || {});
        }
    },
    
    async saveCrmOwner() {
        if (!this.isAdmin()) return;
        const select = document.getElementById('crm-owner-select');
        if (!select) return;
        const crmOwnerId = select.value;
        if (!crmOwnerId) {
            Swal.fire('خطا', 'لطفاً کارشناس CRM را انتخاب کنید', 'error');
            return;
        }
        Swal.showLoading();
        const res = await this.req('save_crm_settings', {crm_owner_id: crmOwnerId});
        Swal.close();
        if (res.status === 'success') {
            Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'ذخیره شد', showConfirmButton:false, timer:2000});
            this.loadUsers();
        } else {
            Swal.fire('خطا', res.message || 'خطا در ذخیره تنظیمات', 'error');
        }
    },

    async runMigration() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند migration را اجرا کند', 'error');
            return;
        }

        try {
            const preview = await this.req('preview_migration', {});
            if (preview.status !== 'success') {
                Swal.fire('خطا', preview.message || 'خطا در بررسی migration', 'error');
                return;
            }
            const pendingSummary = preview.pending_summary || {};
            const pendingItems = Array.isArray(preview.pending_items) ? preview.pending_items : [];
            const previewHtml = `
                <div class="text-right text-sm space-y-2">
                    <div>تعداد جدول‌های مفقود: <b>${pendingSummary.missing_tables || 0}</b></div>
                    <div>تعداد ستون‌های مفقود (پایش‌شده): <b>${pendingSummary.missing_columns || 0}</b></div>
                    <div>تعداد ایندکس‌های مفقود (پایش‌شده): <b>${pendingSummary.missing_indexes || 0}</b></div>
                    ${pendingItems.length ? `<div class="mt-2 max-h-48 overflow-auto border border-slate-700 rounded p-2 bg-slate-900/40">${pendingItems.slice(0,50).map(i => `<div class="py-0.5">${this.escapeHtml(i)}</div>`).join('')}</div>` : '<div class="text-green-400">مورد جدیدی در لیست پایش‌شده پیدا نشد. اجرای Migration همچنان امن و idempotent است.</div>'}
                </div>`;
            const confirm = await Swal.fire({
                title: 'پیش‌نمایش Migration',
                html: previewHtml,
                icon: 'info',
                showCancelButton: true,
                confirmButtonText: 'اجرای Migration',
                cancelButtonText: 'انصراف',
                width: 700
            });
            if (!confirm.isConfirmed) return;

            Swal.fire({
                title: 'در حال اجرای Migration...',
                text: 'لطفاً صبر کنید',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });
            const res = await this.req('run_migration', {});
            Swal.close();
            if (res.status === 'success') {
                const report = res.report || {};
                const html = `
                    <div class="text-right text-sm space-y-2">
                        <div>مدت اجرا: <b>${report.duration_ms ?? 0}</b> ms</div>
                        <div>جدول‌های جدید: <b>${(report.created_tables || []).length}</b></div>
                        <div>ستون‌های جدید: <b>${(report.added_columns || []).length}</b></div>
                        <div>ایندکس‌های جدید: <b>${(report.added_indexes || []).length}</b></div>
                        ${(report.created_tables || []).length ? `<div><b>جدول‌ها:</b> ${(report.created_tables || []).map(x => this.escapeHtml(x)).join('، ')}</div>` : ''}
                    </div>`;
                Swal.fire({
                    icon: 'success',
                    title: 'موفق',
                    html: html || (res.message || 'Migration با موفقیت اجرا شد'),
                    width: 700
                });
                // Reload users list
                this.loadUsers();
            } else {
                Swal.fire('خطا', res.message || 'خطا در اجرای migration', 'error');
            }
        } catch (error) {
            console.error('Error in runMigration:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در اجرای migration: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    async runActivityTypeRepair() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند ترمیم نوع فعالیت‌ها را اجرا کند', 'error');
            return;
        }

        try {
            const preview = await this.req('preview_activity_type_repair', {});
            if (preview.status !== 'success') {
                Swal.fire('خطا', preview.message || 'خطا در پیش‌نمایش ترمیم', 'error');
                return;
            }

            const report = preview.report || {};
            const sources = report.match_sources || {};
            const previewItems = Array.isArray(report.preview) ? report.preview : [];
            const previewHtml = `
                <div class="text-right text-sm space-y-2">
                    <div>فایل بکاپ: <b>${this.escapeHtml(report.backup_path || '-')}</b></div>
                    <div>تعداد رکوردهای خراب فعلی: <b>${this.formatNumber(report.current_broken_rows || 0)}</b></div>
                    <div>تعداد رکوردهای قابل ترمیم: <b>${this.formatNumber(report.matched_rows || 0)}</b></div>
                    <div>با stage: <b>${this.formatNumber(sources.stage || 0)}</b> | با lookup: <b>${this.formatNumber(sources.lookup || 0)}</b> | با backup match: <b>${this.formatNumber((sources.didar_activity_id || 0) + (sources.composite || 0) + (sources.raw_json || 0))}</b></div>
                    ${previewItems.length ? `<div class="mt-2 max-h-56 overflow-auto border border-slate-700 rounded p-2 bg-slate-900/40">${previewItems.map(item => `<div class="py-1 border-b border-slate-800 last:border-b-0"><div>#${this.escapeHtml(String(item.id || ''))} → <b>${this.escapeHtml(item.new_title || '')}</b></div><div class="text-xs text-gray-500">منبع: ${this.escapeHtml(item.match_source || '-')} | stage: ${this.escapeHtml(item.stage || '-')}</div></div>`).join('')}</div>` : '<div class="text-yellow-300">نمونه‌ای برای نمایش پیدا نشد.</div>'}
                    <div class="text-xs text-gray-500 pt-2">فقط رکوردهایی که نوع فعالیت‌شان خالی/فعالیت/نامشخص است تغییر می‌کنند.</div>
                </div>`;

            const confirm = await Swal.fire({
                title: 'پیش‌نمایش ترمیم نوع فعالیت‌ها',
                html: previewHtml,
                icon: 'info',
                showCancelButton: true,
                confirmButtonText: 'اجرای ترمیم',
                cancelButtonText: 'انصراف',
                width: 760
            });
            if (!confirm.isConfirmed) return;

            Swal.fire({
                title: 'در حال اجرای ترمیم...',
                text: 'لطفاً صبر کنید',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });

            const res = await this.req('run_activity_type_repair', {});
            Swal.close();

            if (res.status !== 'success') {
                Swal.fire('خطا', res.message || 'خطا در اجرای ترمیم', 'error');
                return;
            }

            const runReport = res.report || {};
            const runSources = runReport.match_sources || {};
            Swal.fire({
                icon: 'success',
                title: 'ترمیم انجام شد',
                html: `
                    <div class="text-right text-sm space-y-2">
                        <div>رکوردهای خراب بررسی‌شده: <b>${this.formatNumber(runReport.current_broken_rows || 0)}</b></div>
                        <div>رکوردهای ترمیم‌شده: <b>${this.formatNumber(runReport.updated_rows || 0)}</b></div>
                        <div>stage: <b>${this.formatNumber(runSources.stage || 0)}</b> | lookup: <b>${this.formatNumber(runSources.lookup || 0)}</b> | backup/raw: <b>${this.formatNumber((runSources.didar_activity_id || 0) + (runSources.composite || 0) + (runSources.raw_json || 0))}</b></div>
                    </div>
                `,
                width: 700
            });
        } catch (error) {
            console.error('Error in runActivityTypeRepair:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در اجرای ترمیم: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    async runWonKarizReconcile() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند ترمیم کاریز معاملات موفق را اجرا کند', 'error');
            return;
        }

        try {
            const preview = await this.req('deal/reconcile_won_kariz_preview', {});
            if (preview.status !== 'success') {
                Swal.fire('خطا', preview.message || 'خطا در پیش‌نمایش ترمیم', 'error');
                return;
            }

            const report = preview.report || {};
            const rows = Array.isArray(report.preview) ? report.preview : [];
            const previewHtml = `
                <div class="text-right text-sm space-y-2">
                    <div>کل موارد نیازمند ترمیم: <b>${this.formatNumber(report.total_candidates || 0)}</b></div>
                    <div>نیازمند اصلاح مرحله کاریز: <b>${this.formatNumber(report.needs_pipeline_fix || 0)}</b></div>
                    <div>نیازمند اصلاح خروج از کاریز: <b>${this.formatNumber(report.needs_virtual_fix || 0)}</b></div>
                    ${rows.length ? `<div class="mt-2 max-h-64 overflow-auto border border-slate-700 rounded p-2 bg-slate-900/40">${rows.map(item => `
                        <div class="py-1 border-b border-slate-800 last:border-b-0">
                            <div><b>${this.escapeHtml(item.didar_deal_id || '')}</b> | ${this.escapeHtml(item.title || '-')}</div>
                            <div class="text-xs text-gray-500">
                                stage: ${this.escapeHtml(item.pipeline_stage || '-')}
                                | fix-stage: ${Number(item.needs_pipeline_fix || 0) ? 'بله' : 'خیر'}
                                | fix-kariz: ${Number(item.needs_virtual_fix || 0) ? 'بله' : 'خیر'}
                            </div>
                        </div>
                    `).join('')}</div>` : '<div class="text-green-300">موردی برای ترمیم وجود ندارد.</div>'}
                </div>`;

            const confirm = await Swal.fire({
                title: 'پیش‌نمایش ترمیم معاملات موفق',
                html: previewHtml,
                icon: 'info',
                showCancelButton: true,
                confirmButtonText: 'اجرای ترمیم',
                cancelButtonText: 'انصراف',
                width: 760
            });
            if (!confirm.isConfirmed) return;

            Swal.fire({
                title: 'در حال ترمیم...',
                text: 'لطفاً صبر کنید',
                allowOutsideClick: false,
                didOpen: () => Swal.showLoading()
            });

            const res = await this.req('deal/reconcile_won_kariz_run', {});
            Swal.close();
            if (res.status !== 'success') {
                Swal.fire('خطا', res.message || 'ترمیم انجام نشد', 'error');
                return;
            }

            const runReport = res.report || {};
            const errors = Array.isArray(runReport.errors) ? runReport.errors : [];
            const resultHtml = `
                <div class="text-right text-sm space-y-2">
                    <div>کل موارد بررسی‌شده: <b>${this.formatNumber(runReport.total_candidates || 0)}</b></div>
                    <div>اصلاح مرحله کاریز: <b>${this.formatNumber(runReport.fixed_pipeline || 0)}</b></div>
                    <div>اصلاح خروج از کاریز: <b>${this.formatNumber(runReport.fixed_virtual || 0)}</b></div>
                    <div>خطاها: <b>${this.formatNumber(runReport.errors_count || 0)}</b></div>
                    ${errors.length ? `<div class="mt-2 max-h-56 overflow-auto border border-red-800 rounded p-2 bg-red-950/20">${errors.map(e => `
                        <div class="py-1 border-b border-red-900/50 last:border-b-0 text-xs">
                            ${this.escapeHtml(e.deal_id || '-')} : ${this.escapeHtml(e.error || '')}
                        </div>
                    `).join('')}</div>` : ''}
                </div>`;

            Swal.fire({
                icon: 'success',
                title: 'ترمیم انجام شد',
                html: resultHtml,
                width: 720
            });
        } catch (error) {
            console.error('Error in runWonKarizReconcile:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در اجرای ترمیم: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    renderUsers(users, crmOwner = {}) {
        const tbody = document.getElementById('users-table');
        if(!tbody) return;
        this.state.lastUsersList = users || [];
        tbody.innerHTML = '';
        const isAdmin = this.isAdmin();
        const masterStatus = document.getElementById('master-password-status');
        if (masterStatus) {
            const configured = !!this.state.masterPasswordConfigured;
            masterStatus.textContent = configured ? 'تنظیم شده' : 'تنظیم نشده';
            masterStatus.className = `text-xs px-2 py-1 rounded ${configured ? 'bg-green-900/40 text-green-300' : 'bg-amber-900/40 text-amber-300'}`;
        }
        users.forEach(u => {
            const userDisplayName = this.escapeHtml(u.display_name || (u.first_name + ' ' + u.last_name));
            tbody.innerHTML += `<tr>
                <td class="p-3">${userDisplayName}<div class="text-xs text-gray-500 mt-1">${u.email || '-'}</div></td>
                <td class="p-3 font-mono text-xs">${this.escapeHtml(u.username || u.display_name || '-')}</td>
                <td class="p-3 text-xs"><span class="px-2 py-0.5 bg-slate-800 rounded">${this.escapeHtml(this.getUserRoleLabel(u.role))}</span></td>
                <td class="p-3 font-mono text-xs text-gray-500">${this.escapeHtml(u.didar_user_id || '-')}<div class="mt-1 ${Number(u.is_active) === 1 ? 'text-green-400' : 'text-red-400'}">${Number(u.is_active) === 1 ? 'فعال' : 'غیرفعال'}</div></td>
                <td class="p-3">
                    <div class="flex gap-2 flex-wrap">
                        ${isAdmin ? `<button onclick="app.openUserModal(${u.id})" class="text-xs text-cyan-400 hover:text-cyan-300 px-2 py-1 rounded bg-cyan-500/10">
                            <i class="fa-solid fa-pen ml-1"></i> ویرایش
                        </button>` : ''}
                        ${isAdmin ? `<button onclick="app.openEditUserRoleModal(${u.id}, '${u.role}', '${this.escapeHtml(u.display_name || (u.first_name + ' ' + u.last_name))}')" class="text-xs text-blue-500 hover:text-blue-400 px-2 py-1 rounded bg-blue-500/10">
                            <i class="fa-solid fa-user-gear ml-1"></i> ویرایش نقش
                        </button>` : ''}
                        ${isAdmin ? `<button onclick="app.transferUserData(${u.id}, '${userDisplayName.replace(/'/g, "\\'")}')" class="text-xs text-purple-400 hover:text-purple-300 px-2 py-1 rounded bg-purple-500/10">
                            <i class="fa-solid fa-right-left ml-1"></i> انتقال دیتا
                        </button>` : ''}
                        ${isAdmin ? `<button onclick="app.deleteUser(${u.id}, '${userDisplayName.replace(/'/g, "\\'")}')" class="text-xs text-red-400 hover:text-red-300 px-2 py-1 rounded bg-red-500/10">
                            <i class="fa-solid fa-trash ml-1"></i> حذف
                        </button>` : ''}
                        <button onclick="app.openChangePasswordModal(${u.id})" class="text-xs text-yellow-500 hover:text-yellow-400 px-2 py-1 rounded bg-yellow-500/10">
                            <i class="fa-solid fa-key ml-1"></i> تغییر رمز
                        </button>
                    </div>
                </td>
            </tr>`;
        });
        
        const crmSelect = document.getElementById('crm-owner-select');
        const crmLabel = document.getElementById('crm-owner-current');
        if (crmSelect) {
            crmSelect.innerHTML = '<option value="">-- انتخاب کاربر از دیدار --</option>';
            (window.didarUsers || []).forEach(u => {
                const opt = document.createElement('option');
                const value = u.Id || u.UserId;
                opt.value = value || '';
                opt.textContent = (u.DisplayName || ((u.FirstName || '') + ' ' + (u.LastName || ''))) + (u.UserName ? ` (${u.UserName})` : '');
                crmSelect.appendChild(opt);
            });
            if (crmOwner?.id) {
                crmSelect.value = crmOwner.id;
            }
        }
        if (crmLabel) {
            crmLabel.textContent = crmOwner?.name ? `کاربر فعلی: ${crmOwner.name}` : 'کاربر فعلی: انتخاب نشده';
        }
        
        this.state.crmOwnerId = crmOwner?.id || null;
        this.state.crmOwnerName = crmOwner?.name || '';
    },

    async loadLookupAdmin(force = false) {
        if (!this.isAdmin()) return;
        const select = document.getElementById('lookup-group-select');
        if (!select) return;
        this.toggleLookupServiceSpecificColumns(false);
        this.toggleLookupDestinationBankColumns(false);

        if (!force && this.state.lookupGroups && this.state.lookupGroups.length) {
            if (this.state.lookupSelectedGroup) {
                select.value = this.state.lookupSelectedGroup;
                await this.onLookupGroupChange();
            }
            return;
        }

        const res = await this.req('get_lookup_groups', { include_inactive: 1 });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در بارگذاری گروه‌ها', 'error');
            return;
        }
        this.state.lookupGroups = res.groups || [];
        
        select.innerHTML = '<option value="">-- انتخاب کنید --</option>';
        
        // Add Lookup Groups
        const groupGroup = document.createElement('optgroup');
        groupGroup.label = 'گروه‌های لوکاپ (دارای لیست)';
        this.state.lookupGroups.forEach(group => {
            const opt = document.createElement('option');
            opt.value = group.code;
            opt.textContent = group.title || group.code;
            groupGroup.appendChild(opt);
        });
        select.appendChild(groupGroup);

        const toolsGroup = document.createElement('optgroup');
        toolsGroup.label = 'ابزارهای مرتبط';
        const productManagerOpt = document.createElement('option');
        productManagerOpt.value = '__products_manager__';
        productManagerOpt.textContent = 'مدیریت محصولات';
        toolsGroup.appendChild(productManagerOpt);
        const rbacManagerOpt = document.createElement('option');
        rbacManagerOpt.value = '__rbac_manager__';
        rbacManagerOpt.textContent = 'مدیریت نقش‌ها و دسترسی‌ها (RBAC)';
        toolsGroup.appendChild(rbacManagerOpt);
        select.appendChild(toolsGroup);

        // Add System Fields
        const systemGroup = document.createElement('optgroup');
        systemGroup.label = 'فیلدهای سیستمی';
        [
            { code: 'deal_title', title: 'عنوان معامله' },
            { code: 'payment_method', title: 'نحوه پرداخت' },
            { code: 'payment_amount', title: 'مبلغ پرداخت' },
            { code: 'payment_card_last4', title: '۴ رقم آخر کارت' },
            { code: 'destination_bank', title: 'بانک مقصد پرداخت' },
            { code: 'refund_amount', title: 'مبلغ مرجوعی' },
            { code: 'service_cost', title: 'مبلغ خدمات' },
            { code: 'payable_amount', title: 'مبلغ قابل پرداخت' },
            { code: 'person_name', title: 'نام لید' },
            { code: 'person_mobile', title: 'شماره موبایل لید' }
        ].forEach(field => {
            const opt = document.createElement('option');
            opt.value = field.code;
            opt.textContent = field.title;
            systemGroup.appendChild(opt);
        });
        select.appendChild(systemGroup);

        if (!this.state.lookupSelectedGroup && this.state.lookupGroups.length) {
            this.state.lookupSelectedGroup = this.state.lookupGroups[0].code;
        }
        if (this.state.lookupSelectedGroup) {
            select.value = this.state.lookupSelectedGroup;
            await this.onLookupGroupChange();
        }
    },

    async ensureFieldConfigsLoaded() {
        if (!this.state.fieldConfigurations || !this.state.fieldConfigurations.length) {
            await this.loadFieldConfigurationsPublic('deal');
        }
    },

    async loadFieldConfigurationsPublic(entityType = 'deal') {
        try {
            const res = await this.req('get_field_configurations_public', { entity_type: entityType });
            if (res.status === 'success') {
                this.state.fieldConfigurations = res.configurations || [];
            }
        } catch (error) {
            console.error('Error loading public field configurations:', error);
        }
    },

    async onLookupGroupChange() {
        const select = document.getElementById('lookup-group-select');
        const groupCode = select?.value || '';
        this.state.lookupSelectedGroup = groupCode;
        
        const addBtn = document.getElementById('lookup-add-item-btn');
        const itemsSection = document.getElementById('lookup-items-section');
        const quickSettings = document.getElementById('field-quick-settings');
        const tbody = document.getElementById('lookup-items-table');
        const isProductsManager = groupCode === '__products_manager__';
        const isRbacManager = groupCode === '__rbac_manager__';

        this.setLookupManagerModeUI(
            isProductsManager ? 'products_manager' : (isRbacManager ? 'rbac_manager' : 'lookup_or_field')
        );
        this.toggleLookupServiceSpecificColumns(groupCode === 'allowed_services');
        this.toggleLookupDestinationBankColumns(groupCode === 'payment_destination_banks');
        
        if (!groupCode) {
            if (tbody) tbody.innerHTML = '';
            if (addBtn) addBtn.disabled = true;
            if (itemsSection) itemsSection.classList.add('hidden');
            if (quickSettings) quickSettings.classList.add('hidden');
            return;
        }

        if (isProductsManager) {
            await this.loadProducts();
            this.renderLookupProductsManagerTable();
            return;
        }
        if (isRbacManager) {
            await this.loadLookupRbacManagerData();
            return;
        }

        // Show items section if it's a real lookup group
        const isLookupGroup = this.state.lookupGroups.some(g => g.code === groupCode);
        
        // Items section visibility
        if (itemsSection) itemsSection.classList.remove('hidden');
        
        if (isLookupGroup) {
            if (addBtn) addBtn.disabled = false;
            await this.loadLookupItems(groupCode);
        } else {
            // For system fields, clear items but keep settings
            if (tbody) {
                tbody.innerHTML = `<tr><td colspan="${this.getLookupTableColumnCount()}" class="p-8 text-center text-gray-500 italic">این یک فیلد سیستمی است و گزینه‌های لیست ندارد. فقط تنظیمات بالا اعمال می‌شود.</td></tr>`;
            }
            if (addBtn) addBtn.disabled = true;
        }

        // Load settings for this groupCode/fieldName
        await this.loadFieldConfigurationForAdmin(groupCode);
    },

    async loadFieldConfigurationForAdmin(fieldName) {
        const advSettings = document.getElementById('field-advanced-settings');
        
        const setVal = (id, val, isCheck = false) => {
            const el = document.getElementById(id);
            if (!el) return;
            if (isCheck) el.checked = !!val;
            else el.value = val || '';
        };
        
        try {
            const res = await this.req('get_field_configuration', { field_name: fieldName });
            const config = (res.status === 'success' && res.configuration) ? res.configuration : null;
            
            // Populate all fields safely
            setVal('fc-field-label', config?.field_label);
            setVal('fc-is-required', config?.is_required === 1, true);
            setVal('fc-is-editable', config?.is_editable !== 0, true); // Default to true
            setVal('fc-is-editable-after-create', config?.is_editable_after_create !== 0, true); // Default to true
            setVal('fc-is-active', config?.is_active !== 0, true);     // Default to true
            setVal('fc-help-text', config?.help_text);
            setVal('fc-default-value', config?.default_value);
            setVal('fc-group-name', config?.group_name);
            setVal('fc-validation-pattern', config?.validation_pattern);
            
            // Parse and populate conditional logic UI
            const conditionalLogic = config?.conditional_logic;
            if (conditionalLogic && typeof conditionalLogic === 'object') {
                setVal('fc-conditional-field', conditionalLogic.field);
                setVal('fc-conditional-operator', conditionalLogic.operator || '=');
                setVal('fc-conditional-value', conditionalLogic.value);
                setVal('fc-conditional-logic', JSON.stringify(conditionalLogic));
            } else if (conditionalLogic && typeof conditionalLogic === 'string') {
                try {
                    const parsed = JSON.parse(conditionalLogic);
                    setVal('fc-conditional-field', parsed.field);
                    setVal('fc-conditional-operator', parsed.operator || '=');
                    setVal('fc-conditional-value', parsed.value);
                } catch (e) {}
                setVal('fc-conditional-logic', conditionalLogic);
            } else {
                setVal('fc-conditional-field', '');
                setVal('fc-conditional-operator', '=');
                setVal('fc-conditional-value', '');
                setVal('fc-conditional-logic', '');
            }

            // Set edit roles checkboxes
            const editRoles = config?.edit_allow_roles || [];
            document.querySelectorAll('.role-checkbox').forEach(cb => {
                cb.checked = editRoles.includes(cb.value);
            });

            // Set delete roles checkboxes
            const deleteRoles = config?.delete_allow_roles || [];
            document.querySelectorAll('.delete-role-checkbox').forEach(cb => {
                cb.checked = deleteRoles.includes(cb.value);
            });

            // Set view roles checkboxes
            const viewRoles = config?.view_allow_roles || [];
            document.querySelectorAll('.view-role-checkbox').forEach(cb => {
                cb.checked = viewRoles.includes(cb.value);
            });

            // Show the advanced settings panel
            if (advSettings) advSettings.classList.remove('hidden');
        } catch (error) {
            console.error('Error loading field config:', error);
        }
    },

    async saveFieldConfigurationMerged() {
        const fieldName = document.getElementById('lookup-group-select').value;
        if (!fieldName) return;

        // Collect edit roles
        const selectedRoles = Array.from(
            document.querySelectorAll('.role-checkbox:checked')
        ).map(cb => cb.value);

        // Collect delete roles
        const selectedDeleteRoles = Array.from(
            document.querySelectorAll('.delete-role-checkbox:checked')
        ).map(cb => cb.value);

        // Collect view roles
        const selectedViewRoles = Array.from(
            document.querySelectorAll('.view-role-checkbox:checked')
        ).map(cb => cb.value);

        const isLookupGroup = (this.state.lookupGroups || []).some(g => g.code === fieldName);

        const data = {
            field_name: fieldName,
            field_label: document.getElementById('fc-field-label')?.value || '',
            field_type: isLookupGroup ? 'select' : 'text',
            lookup_group_code: isLookupGroup ? fieldName : null,
            is_required: document.getElementById('fc-is-required')?.checked ? 1 : 0,
            is_editable: document.getElementById('fc-is-editable')?.checked ? 1 : 0,
            is_editable_after_create: document.getElementById('fc-is-editable-after-create')?.checked ? 1 : 0,
            is_active: document.getElementById('fc-is-active')?.checked ? 1 : 0,
            edit_allow_roles: JSON.stringify(selectedRoles),
            delete_allow_roles: JSON.stringify(selectedDeleteRoles),
            view_allow_roles: JSON.stringify(selectedViewRoles),
            help_text: document.getElementById('fc-help-text')?.value || '',
            default_value: document.getElementById('fc-default-value')?.value || '',
            group_name: document.getElementById('fc-group-name')?.value || '',
            validation_pattern: document.getElementById('fc-validation-pattern')?.value || '',
            conditional_logic: document.getElementById('fc-conditional-logic')?.value || ''
        };

        try {
            const res = await this.req('save_field_configuration', data);
            if (res.status === 'success') {
                // Toast success
                const Toast = Swal.mixin({
                    toast: true,
                    position: 'bottom-end',
                    showConfirmButton: false,
                    timer: 2000,
                    timerProgressBar: true
                });
                Toast.fire({
                    icon: 'success',
                    title: 'تنظیمات ذخیره شد'
                });
                
                // Update current label in UI
                const currentLabel = document.getElementById('fc-current-label');
                if (currentLabel && data.field_label) {
                    currentLabel.textContent = data.field_label;
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در ذخیره‌سازی', 'error');
            }
        } catch (error) {
            Swal.fire('خطا', 'خطا در ارتباط با سرور', 'error');
        }
    },

    // Apply conditional logic from UI fields
    applyConditionalLogicFromUI() {
        const field = document.getElementById('fc-conditional-field')?.value?.trim();
        const operator = document.getElementById('fc-conditional-operator')?.value || '=';
        const value = document.getElementById('fc-conditional-value')?.value?.trim();

        if (!field) {
            // Clear conditional logic
            document.getElementById('fc-conditional-logic').value = '';
            this.saveFieldConfigurationMerged();
            return;
        }

        const logic = JSON.stringify({
            field: field,
            operator: operator,
            value: value
        });

        document.getElementById('fc-conditional-logic').value = logic;
        this.saveFieldConfigurationMerged();

        Swal.fire({
            toast: true,
            position: 'bottom-end',
            icon: 'success',
            title: 'منطق شرطی ذخیره شد',
            showConfirmButton: false,
            timer: 2000
        });
    },

    async applyFieldConfigurations(entityType, containerSelector) {
        try {
            const res = await this.req('get_field_configurations', { entity_type: entityType });
            if (res.status !== 'success') return;
            
            const configs = res.configurations || [];
            const container = document.querySelector(containerSelector);
            if (!container) return;

            configs.forEach(config => {
                const fieldName = config.field_name;
                const input = container.querySelector(`[name="${fieldName}"], [name="${fieldName}[]"]`);
                if (!input) return;

                // 1. Tooltips (Help Text)
                if (config.help_text) {
                    const label = input.closest('div')?.querySelector('label');
                    if (label) {
                        label.title = config.help_text;
                        label.classList.add('cursor-help');
                        if (!label.querySelector('.fa-circle-info')) {
                            label.innerHTML += ` <i class="fa-solid fa-circle-info text-[9px] text-gray-500"></i>`;
                        }
                    }
                }

                // 2. Default Values (for new records)
                const dealIdInput = document.getElementById('deal-id');
                const isNew = !dealIdInput || !dealIdInput.value;
                if (isNew && config.default_value && (!input.value || input.value === '0')) {
                    input.value = config.default_value;
                    if (input.tagName === 'SELECT') input.dispatchEvent(new Event('change'));
                }

                // 3. Conditional Logic (support both legacy and new schema)
                if (config.conditional_logic) {
                    try {
                        const logic = typeof config.conditional_logic === 'string' ? JSON.parse(config.conditional_logic) : config.conditional_logic;
                        const wrapper = input.closest('.grid > div') || input.closest('div');
                        const evaluate = (actualValue, operator, expected) => {
                            switch (operator) {
                                case '=':
                                case '==':
                                    return actualValue == expected;
                                case '!=':
                                case '<>':
                                    return actualValue != expected;
                                case 'in':
                                    return Array.isArray(expected) && expected.includes(actualValue);
                                case 'not_in':
                                    return Array.isArray(expected) && !expected.includes(actualValue);
                                case 'empty':
                                    return !actualValue;
                                case 'not_empty':
                                    return !!actualValue;
                                case 'contains':
                                    return typeof actualValue === 'string' && actualValue.includes(expected);
                                default:
                                    return true;
                            }
                        };

                        // New schema: {field, operator, value}
                        if (logic.field) {
                            const dependInput = container.querySelector(`[name="${logic.field}"], [name="${logic.field}[]"]`);
                            if (dependInput) {
                                const checkVisibility = () => {
                                    const val = dependInput.value;
                                    const shouldShow = evaluate(val, logic.operator || '=', logic.value);
                                    if (wrapper) wrapper.classList.toggle('hidden', !shouldShow);
                                    if (input && config.is_required === 1) input.required = shouldShow;
                                };
                                dependInput.addEventListener('change', checkVisibility);
                                checkVisibility();
                            }
                        }

                        // Legacy schema: {depends_on, show_if}
                        if (logic.depends_on && logic.show_if) {
                            const dependInput = container.querySelector(`[name="${logic.depends_on}"], [name="${logic.depends_on}[]"]`);
                            if (dependInput) {
                                const checkVisibility = () => {
                                    const val = dependInput.value;
                                    const shouldShow = Array.isArray(logic.show_if) ? logic.show_if.includes(val) : val === logic.show_if;
                                    if (wrapper) wrapper.classList.toggle('hidden', !shouldShow);
                                    if (input && config.is_required === 1) input.required = shouldShow;
                                };
                                dependInput.addEventListener('change', checkVisibility);
                                checkVisibility();
                            }
                        }
                    } catch (e) {}
                }

                // 4. View permissions
                const userRole = (typeof window.APP_USER !== 'undefined' ? window.APP_USER.role : 'agent');
                const viewRoles = config.view_allow_roles || [];
                if (viewRoles.length && !viewRoles.includes(userRole)) {
                    const wrapper = input.closest('.grid > div') || input.closest('div');
                    if (wrapper) wrapper.classList.add('hidden');
                    input.readOnly = true;
                    if (input.tagName === 'SELECT') input.disabled = true;
                    input.required = false;
                }

                // 5. Role-based Access (Frontend UI)
                const allowedRoles = config.edit_allow_roles || [];
                const editable = config.is_editable !== 0;
                if (allowedRoles.length && !allowedRoles.includes(userRole) || !editable) {
                    input.readOnly = true;
                    if (input.tagName === 'SELECT') input.disabled = true;
                    input.classList.add('bg-gray-900/40', 'cursor-not-allowed', 'opacity-70');
                }
            });
        } catch (e) { console.error('Error applying field configs:', e); }
    },

    /**
     * Check if the current user can edit a field based on configuration
     * @param {string} fieldName - The field name to check
     * @param {boolean} isExisting - Whether the entity already exists (for editable_after_create check)
     * @returns {boolean} - Whether the user can edit this field
     */
    canUserEditFieldClient(fieldName, isExisting = false) {
        const role = (typeof window.APP_USER !== 'undefined' ? window.APP_USER.role : 'agent') || 'agent';
        if (role === 'admin') return true;
        
        // Define aliases for client-side check
        const aliases = {
            'payment_method': ['payment_methods'],
            'destination_bank': ['payment_destination_banks', 'bank_destination'],
            'payment_card_last4': ['card_last4', 'card_last_4', 'payment_card_last_4'],
            'deal_title': ['title']
        };

        const namesToCheck = [fieldName, ...(aliases[fieldName] || [])];
        
        // Find config among aliases (prioritize alias/group code first)
        let config = null;
        for (const name of namesToCheck) {
            config = (this.state.fieldConfigurations || []).find(c => c.field_name === name);
            if (config) break;
        }
        
        // No config = allow by default
        if (!config) return true;
        
        // Check if active
        if (config.is_active === 0) return false;
        
        // Check if editable at all
        if (config.is_editable === 0) return false;
        
        // Check if editable after create (for existing records)
        if (isExisting && config.is_editable_after_create === 0) {
            return false;
        }

        // Check view role permissions
        const viewRoles = config.view_allow_roles || [];
        if (viewRoles.length && !viewRoles.includes(role)) {
            return false;
        }
        
        // Check role permissions
        const roles = config.edit_allow_roles || [];
        if (!roles.length) return true;
        return roles.includes(role);
    },

    canUserViewFieldClient(fieldName) {
        const role = (typeof window.APP_USER !== 'undefined' ? window.APP_USER.role : 'agent') || 'agent';
        if (role === 'admin') return true;
        const config = (this.state.fieldConfigurations || []).find(c => c.field_name === fieldName);
        if (!config || config.is_active === 0) {
            return !['deal_acquaintance_source', 'deal_acquaintance_detail', 'deal_content_topic'].includes(fieldName);
        }
        const viewRoles = config.view_allow_roles || [];
        if (!viewRoles.length) return true;
        return viewRoles.includes(role);
    },

    updateDealMarketingUi(deal = null, isNewDeal = false, leadDefaults = null) {
        const fields = document.getElementById('deal-marketing-fields');
        const summary = document.getElementById('deal-marketing-summary');
        const source = document.getElementById('deal-source');
        const detail = document.getElementById('deal-source-detail');
        const topic = document.getElementById('deal-content-topic');
        const canViewSaved =
            this.canUserViewFieldClient('deal_acquaintance_source') ||
            this.canUserViewFieldClient('deal_acquaintance_detail') ||
            this.canUserViewFieldClient('deal_content_topic');

        if (fields) fields.classList.toggle('hidden', !isNewDeal && !canViewSaved);
        fields?.querySelectorAll('select, input, textarea').forEach((input) => {
            input.disabled = !isNewDeal && !canViewSaved;
        });
        if (source) {
            source.required = !!isNewDeal;
            source.disabled = !isNewDeal && !canViewSaved;
        }
        if (topic) {
            topic.required = !!isNewDeal;
            topic.disabled = !isNewDeal && !canViewSaved;
        }
        if (detail) {
            detail.required = false;
            detail.disabled = !isNewDeal && !canViewSaved;
        }

        if (deal) {
            if (source) source.value = deal.acquaintance_source || '';
            this.handleDealSourceChange();
            if (detail) detail.value = deal.acquaintance_detail || '';
            if (topic) topic.value = deal.content_topic || '';
        } else {
            // New deal: inherit the lead's saved شیوه آشنایی / موضوع محتوا as defaults
            // (leadDefaults is the person record). User can still change them.
            if (source) source.value = leadDefaults?.acquaintance_source || '';
            this.handleDealSourceChange();
            if (detail) detail.value = leadDefaults?.acquaintance_detail || '';
            if (topic) topic.value = leadDefaults?.content_topic || '';
        }

        if (summary) summary.classList.toggle('hidden', isNewDeal || !canViewSaved);
        const setText = (id, value) => {
            const el = document.getElementById(id);
            if (el) el.textContent = value || '-';
        };
        if (deal && canViewSaved) {
            setText('deal-summary-source', this.getLookupLabel('lead_sources', deal.acquaintance_source || ''));
            setText('deal-summary-source-detail', this.getLookupLabel('lead_source_details', deal.acquaintance_detail || ''));
            setText('deal-summary-content-topic', this.getLookupLabel('content_topics', deal.content_topic || ''));
        } else {
            setText('deal-summary-source', '-');
            setText('deal-summary-source-detail', '-');
            setText('deal-summary-content-topic', '-');
        }
    },

    /**
     * Check if the current user can delete based on field configuration
     * @param {string} fieldName - The field name (usually a delete permission key like 'payment_delete')
     * @returns {boolean} - Whether the user can delete
     */
    canUserDeleteFieldClient(fieldName) {
        const role = (typeof window.APP_USER !== 'undefined' ? window.APP_USER.role : 'agent') || 'agent';
        if (role === 'admin') return true;
        
        // Define aliases for client-side check
        const aliases = {
            'payment_delete': ['payment_methods', 'payment_method'],
        };

        const namesToCheck = [fieldName, ...(aliases[fieldName] || [])];
        
        let config = null;
        for (const name of namesToCheck) {
            config = (this.state.fieldConfigurations || []).find(c => c.field_name === name);
            if (config) break;
        }
        
        // No config = allow by default
        if (!config) return true;
        
        // Check if active
        if (config.is_active === 0) return false;
        
        // Check delete role permissions
        const deleteRoles = config.delete_allow_roles || [];
        if (!deleteRoles.length) return true;
        return deleteRoles.includes(role);
    },

    async loadLookupItems(groupCode) {
        if (!groupCode || groupCode === '__products_manager__') return;
        const res = await this.req('get_lookup_items', { group: groupCode });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'خطا در بارگذاری گزینه‌ها', 'error');
            return;
        }
        this.state.lookupItems = res.items || [];
        this.renderLookupItemsTable(groupCode, this.state.lookupItems);
    },

    renderLookupItemsTable(groupCode, items) {
        const tbody = document.getElementById('lookup-items-table');
        if (!tbody) return;
        const parentOptions = groupCode === 'lead_source_details' ? this.getLookupOptions('lead_sources') : [];
        const isAllowedServicesGroup = groupCode === 'allowed_services';
        const isDestinationBankGroup = groupCode === 'payment_destination_banks';
        if (!Array.isArray(items) || !items.length) {
            tbody.innerHTML = `<tr><td colspan="${this.getLookupTableColumnCount()}" class="p-6 text-center text-gray-500">موردی یافت نشد.</td></tr>`;
            this.toggleLookupServiceSpecificColumns(isAllowedServicesGroup);
            this.toggleLookupDestinationBankColumns(isDestinationBankGroup);
            return;
        }
        tbody.innerHTML = (items || []).map(item => {
            const id = item.id || '';
            const title = this.escapeHtml(item.title || '');
            const value = this.escapeHtml(item.value || '');
            const code = this.escapeHtml(item.code || '');
            const metaJson = this.escapeHtml(item.meta_json || '');
            let servicePrice = '';
            let servicePriceListCode = '';
            let linkedProductIds = [];
            let destinationBankName = '';
            let destinationAccountType = '';
            let destinationCard = '';
            let destinationAccount = '';
            let destinationSheba = '';
            let parsedMeta = {};
            if (item.meta_json) {
                try {
                    const meta = JSON.parse(item.meta_json);
                    if (meta && typeof meta === 'object' && !Array.isArray(meta)) parsedMeta = meta;
                } catch (_) {}
            }
            if (isAllowedServicesGroup && item.meta_json) {
                if (parsedMeta.price !== undefined && parsedMeta.price !== null && parsedMeta.price !== '') {
                    servicePrice = formatMoneyString(parsedMeta.price);
                }
                if (parsedMeta.price_list_code !== undefined && parsedMeta.price_list_code !== null) {
                    servicePriceListCode = String(parsedMeta.price_list_code);
                }
            }
            if (isDestinationBankGroup) {
                destinationBankName = String(parsedMeta.bank_name ?? '');
                destinationAccountType = String(parsedMeta.account_type ?? '');
                destinationCard = String(parsedMeta.card_number ?? '');
                destinationAccount = String(parsedMeta.account_number ?? '');
                destinationSheba = String(parsedMeta.sheba ?? '');
            }
            if (isAllowedServicesGroup && item.id) {
                linkedProductIds = this.getProductsLinkedToServiceLookupItem(item.id).map(p => String(p.id));
            }
            const sortOrder = item.sort_order ?? 0;
            const isActive = String(item.is_active ?? 1) === '1' ? 'checked' : '';
            const isVisible = String(item.is_visible ?? 1) === '1' ? 'checked' : '';
            const parentValue = item.parent_value || '';
            const parentSelect = parentOptions.length ? `
                <select class="input bg-gray-800 text-xs" name="parent_value">
                    <option value="">-</option>
                    ${parentOptions.map(opt => `<option value="${this.escapeHtml(opt.value)}" ${opt.value === parentValue ? 'selected' : ''}>${this.escapeHtml(opt.label || opt.value)}</option>`).join('')}
                </select>
            ` : '<span class="text-xs text-gray-500">-</span>';

            return `
                <tr data-id="${id}" data-group="${this.escapeHtml(groupCode)}">
                    <td class="p-3"><input class="input bg-gray-800 text-xs" name="title" value="${title}"></td>
                    <td class="p-3"><input class="input bg-gray-800 text-xs" name="value" value="${value}"></td>
                    <td class="p-3"><input class="input bg-gray-800 text-xs" name="code" value="${code}"></td>
                    <td class="p-3"><input class="input bg-gray-800 text-[10px] font-mono" name="meta_json" value="${metaJson}"></td>
                    <td class="p-3 lookup-col-service-price ${isAllowedServicesGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs money-input ${isAllowedServicesGroup ? '' : 'opacity-60'}" name="service_price" value="${this.escapeHtml(servicePrice)}" ${isAllowedServicesGroup ? 'placeholder="قیمت خدمت"' : 'placeholder=\"-\" disabled'}></td>
                    <td class="p-3 lookup-col-service-price-list-code ${isAllowedServicesGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ${isAllowedServicesGroup ? '' : 'opacity-60'}" name="service_price_list_code" value="${this.escapeHtml(servicePriceListCode)}" ${isAllowedServicesGroup ? 'placeholder="کد لیست قیمت"' : 'placeholder=\"-\" disabled'}></td>
                    <td class="p-3 lookup-col-service-products ${isAllowedServicesGroup ? '' : 'hidden'}">${isAllowedServicesGroup ? this.buildProductsMultiSelectHtml(linkedProductIds) : '<span class="text-xs text-gray-500">-</span>'}</td>
                    <td class="p-3 lookup-col-destination-bank-name ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs" name="destination_bank_name" value="${this.escapeHtml(destinationBankName)}" ${isDestinationBankGroup ? 'placeholder="مثال: تجارت"' : 'disabled'}></td>
                    <td class="p-3 lookup-col-destination-account-type ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs" name="destination_account_type" value="${this.escapeHtml(destinationAccountType)}" ${isDestinationBankGroup ? 'placeholder="جاری / پس انداز"' : 'disabled'}></td>
                    <td class="p-3 lookup-col-destination-card ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ltr" name="destination_card" value="${this.escapeHtml(destinationCard)}" ${isDestinationBankGroup ? 'placeholder="شماره کارت"' : 'disabled'}></td>
                    <td class="p-3 lookup-col-destination-account ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ltr" name="destination_account" value="${this.escapeHtml(destinationAccount)}" ${isDestinationBankGroup ? 'placeholder="شماره حساب"' : 'disabled'}></td>
                    <td class="p-3 lookup-col-destination-sheba ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ltr" name="destination_sheba" value="${this.escapeHtml(destinationSheba)}" ${isDestinationBankGroup ? 'placeholder="IR..."' : 'disabled'}></td>
                    <td class="p-3">${parentSelect}</td>
                    <td class="p-3"><input class="input bg-gray-800 text-xs" name="sort_order" value="${sortOrder}" type="number"></td>
                    <td class="p-3 text-center"><input type="checkbox" name="is_active" class="accent-yellow-500" ${isActive}></td>
                    <td class="p-3 text-center"><input type="checkbox" name="is_visible" class="accent-cyan-500" ${isVisible}></td>
                    <td class="p-3">
                        <div class="flex gap-2">
                            <button type="button" class="px-3 py-1 rounded bg-blue-600 hover:bg-blue-500 text-xs" onclick="app.saveLookupRow(this)">Save</button>
                            <button type="button" class="px-3 py-1 rounded bg-red-600 hover:bg-red-500 text-xs" onclick="app.deleteLookupRow(this)">Delete</button>
                        </div>
                    </td>
                </tr>
            `;
        }).join('');
        this.toggleLookupServiceSpecificColumns(isAllowedServicesGroup);
        this.toggleLookupDestinationBankColumns(isDestinationBankGroup);
    },

    addLookupRow() {
        const groupCode = this.state.lookupSelectedGroup;
        if (!groupCode || groupCode === '__products_manager__') {
            Swal.fire('خطا', 'ابتدا یک گروه را انتخاب کنید', 'error');
            return;
        }
        const tbody = document.getElementById('lookup-items-table');
        if (!tbody) return;
        const parentOptions = groupCode === 'lead_source_details' ? this.getLookupOptions('lead_sources') : [];
        const parentSelect = parentOptions.length ? `
            <select class="input bg-gray-800 text-xs" name="parent_value">
                <option value="">-</option>
                ${parentOptions.map(opt => `<option value="${this.escapeHtml(opt.value)}">${this.escapeHtml(opt.label || opt.value)}</option>`).join('')}
            </select>
        ` : '<span class="text-xs text-gray-500">-</span>';
        const row = document.createElement('tr');
        row.dataset.id = '';
        row.dataset.group = groupCode;
        const isAllowedServicesGroup = groupCode === 'allowed_services';
        const isDestinationBankGroup = groupCode === 'payment_destination_banks';
        row.innerHTML = `
            <td class="p-3"><input class="input bg-gray-800 text-xs" name="title" value=""></td>
            <td class="p-3"><input class="input bg-gray-800 text-xs" name="value" value=""></td>
            <td class="p-3"><input class="input bg-gray-800 text-xs" name="code" value=""></td>
            <td class="p-3"><input class="input bg-gray-800 text-[10px] font-mono" name="meta_json" value=""></td>
            <td class="p-3 lookup-col-service-price ${isAllowedServicesGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs money-input ${isAllowedServicesGroup ? '' : 'opacity-60'}" name="service_price" value="" ${isAllowedServicesGroup ? 'placeholder="قیمت خدمت"' : 'placeholder=\"-\" disabled'}></td>
            <td class="p-3 lookup-col-service-price-list-code ${isAllowedServicesGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ${isAllowedServicesGroup ? '' : 'opacity-60'}" name="service_price_list_code" value="" ${isAllowedServicesGroup ? 'placeholder="کد لیست قیمت"' : 'placeholder=\"-\" disabled'}></td>
            <td class="p-3 lookup-col-service-products ${isAllowedServicesGroup ? '' : 'hidden'}">${isAllowedServicesGroup ? this.buildProductsMultiSelectHtml([]) : '<span class="text-xs text-gray-500">-</span>'}</td>
            <td class="p-3 lookup-col-destination-bank-name ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs" name="destination_bank_name" value="" ${isDestinationBankGroup ? 'placeholder="مثال: تجارت"' : 'disabled'}></td>
            <td class="p-3 lookup-col-destination-account-type ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs" name="destination_account_type" value="" ${isDestinationBankGroup ? 'placeholder="جاری / پس انداز"' : 'disabled'}></td>
            <td class="p-3 lookup-col-destination-card ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ltr" name="destination_card" value="" ${isDestinationBankGroup ? 'placeholder="شماره کارت"' : 'disabled'}></td>
            <td class="p-3 lookup-col-destination-account ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ltr" name="destination_account" value="" ${isDestinationBankGroup ? 'placeholder="شماره حساب"' : 'disabled'}></td>
            <td class="p-3 lookup-col-destination-sheba ${isDestinationBankGroup ? '' : 'hidden'}"><input class="input bg-gray-800 text-xs ltr" name="destination_sheba" value="" ${isDestinationBankGroup ? 'placeholder="IR..."' : 'disabled'}></td>
            <td class="p-3">${parentSelect}</td>
            <td class="p-3"><input class="input bg-gray-800 text-xs" name="sort_order" value="0" type="number"></td>
            <td class="p-3 text-center"><input type="checkbox" name="is_active" class="accent-yellow-500" checked></td>
            <td class="p-3 text-center"><input type="checkbox" name="is_visible" class="accent-cyan-500" checked></td>
            <td class="p-3">
                <div class="flex gap-2">
                    <button type="button" class="px-3 py-1 rounded bg-blue-600 hover:bg-blue-500 text-xs" onclick="app.saveLookupRow(this)">Save</button>
                    <button type="button" class="px-3 py-1 rounded bg-red-600 hover:bg-red-500 text-xs" onclick="app.deleteLookupRow(this)">Delete</button>
                </div>
            </td>
        `;
        tbody.appendChild(row);
        this.toggleLookupServiceSpecificColumns(isAllowedServicesGroup);
        this.toggleLookupDestinationBankColumns(isDestinationBankGroup);
    },

    async saveLookupRow(button) {
        const row = button.closest('tr');
        if (!row) return;
        const groupCode = row.dataset.group;
        const id = row.dataset.id || '';
        const wasNewRow = !id;
        const title = row.querySelector('[name="title"]')?.value?.trim() || '';
        const value = row.querySelector('[name="value"]')?.value?.trim() || '';
        const code = row.querySelector('[name="code"]')?.value?.trim() || '';
        const metaJson = row.querySelector('[name="meta_json"]')?.value?.trim() || '';
        const servicePriceRaw = row.querySelector('[name="service_price"]')?.value?.trim() || '';
        const servicePriceListCodeRaw = row.querySelector('[name="service_price_list_code"]')?.value?.trim() || '';
        const linkedProductIds = Array.from(row.querySelector('[name="service_linked_product_ids"]')?.selectedOptions || []).map(opt => opt.value);
        const destinationBankNameRaw = row.querySelector('[name="destination_bank_name"]')?.value?.trim() || '';
        const destinationAccountTypeRaw = row.querySelector('[name="destination_account_type"]')?.value?.trim() || '';
        const destinationCardRaw = row.querySelector('[name="destination_card"]')?.value?.trim() || '';
        const destinationAccountRaw = row.querySelector('[name="destination_account"]')?.value?.trim() || '';
        const destinationShebaRaw = row.querySelector('[name="destination_sheba"]')?.value?.trim() || '';
        const sortOrder = row.querySelector('[name="sort_order"]')?.value || '0';
        const isActive = row.querySelector('[name="is_active"]')?.checked ? '1' : '0';
        const isVisible = row.querySelector('[name="is_visible"]')?.checked ? '1' : '0';
        const parentValue = row.querySelector('[name="parent_value"]')?.value || '';

        if (!title) {
            Swal.fire('خطا', 'عنوان باید وارد شود', 'error');
            return;
        }
        let parsedMetaJson = null;
        if (metaJson) {
            try {
                parsedMetaJson = JSON.parse(metaJson);
            } catch (e) {
                Swal.fire('خطا', 'فرمت JSON معتبر نیست', 'error');
                return;
            }
        }
        let finalMetaJson = metaJson;
        if (groupCode === 'allowed_services') {
            let metaObject = {};
            if (parsedMetaJson !== null) {
                if (!parsedMetaJson || typeof parsedMetaJson !== 'object' || Array.isArray(parsedMetaJson)) {
                    Swal.fire('خطا', 'برای خدمات، Meta JSON بای�������� یک شیء JSON باشد', 'error');
                    return;
                }
                metaObject = { ...parsedMetaJson };
            }

            const priceNumber = parseMoneyValue(servicePriceRaw);
            if (servicePriceRaw !== '') {
                if (!Number.isFinite(priceNumber) || priceNumber <= 0) {
                    Swal.fire('خطا', 'قیمت خدمت باید عددی بزرگ‌تر از صفر باشد', 'error');
                    return;
                }
                metaObject.price = priceNumber;
            } else {
                delete metaObject.price;
            }
            if (servicePriceListCodeRaw !== '') {
                metaObject.price_list_code = servicePriceListCodeRaw;
            } else {
                delete metaObject.price_list_code;
            }
            finalMetaJson = Object.keys(metaObject).length ? JSON.stringify(metaObject) : '';
        }
        if (groupCode === 'payment_destination_banks') {
            let metaObject = {};
            if (parsedMetaJson !== null) {
                if (!parsedMetaJson || typeof parsedMetaJson !== 'object' || Array.isArray(parsedMetaJson)) {
                    Swal.fire('خطا', 'برای بانک مقصد، Meta JSON باید یک شیء JSON باشد', 'error');
                    return;
                }
                metaObject = { ...parsedMetaJson };
            }
            const setOrDelete = (key, val) => {
                if (val !== '') metaObject[key] = val;
                else delete metaObject[key];
            };
            setOrDelete('bank_name', destinationBankNameRaw);
            setOrDelete('account_type', destinationAccountTypeRaw);
            setOrDelete('card_number', destinationCardRaw.replace(/\D/g, ''));
            setOrDelete('account_number', destinationAccountRaw.replace(/\s+/g, ''));
            setOrDelete('sheba', destinationShebaRaw.replace(/\s+/g, '').toUpperCase());
            finalMetaJson = Object.keys(metaObject).length ? JSON.stringify(metaObject) : '';
        }

        const res = await this.req('save_lookup_item', {
            id,
            group_code: groupCode,
            title,
            value,
            code,
            meta_json: finalMetaJson,
            sort_order: sortOrder,
            is_active: isActive,
            is_visible: isVisible,
            parent_value: parentValue
        });
        if (res.status === 'success') {
            const savedId = String(res.id || row.dataset.id || '');
            let effectiveLinkedProductIds = [...linkedProductIds];
            if (res.id) {
                row.dataset.id = res.id;
            }
            if (groupCode === 'allowed_services' && savedId && wasNewRow && effectiveLinkedProductIds.length === 0) {
                const autoProductName = (value || title || '').trim();
                if (autoProductName) {
                    const createProductRes = await this.req('save_product', {
                        name: autoProductName,
                        service_lookup_item_id: savedId
                    });
                    if (createProductRes.status !== 'success') {
                        Swal.fire('خطا', createProductRes.message || 'ساخت خودکار محصول برای خدمت ناموفق بود', 'error');
                        return;
                    }
                    const autoProductId = String(createProductRes.id || '').trim();
                    if (autoProductId) {
                        effectiveLinkedProductIds = [autoProductId];
                    }
                }
            }
            if (groupCode === 'allowed_services' && savedId) {
                const linkRes = await this.req('save_service_product_links', {
                    service_lookup_item_id: savedId,
                    product_ids: effectiveLinkedProductIds
                });
                if (linkRes.status !== 'success') {
                    Swal.fire('خطا', linkRes.message || 'ذخیره لینک محصولات ناموفق بود', 'error');
                    return;
                }
            }
            Swal.fire('انجام شد', 'ذخیره شد', 'success');
            await this.loadLookups();
            await this.loadProducts();
            await this.loadLookupItems(this.state.lookupSelectedGroup);
        } else {
            Swal.fire('خطا', res.message || 'خطا در ذخیره', 'error');
        }
    },

    async deleteLookupRow(button) {
        const row = button.closest('tr');
        if (!row) return;
        const id = row.dataset.id || '';
        if (!id) {
            row.remove();
            return;
        }
        const result = await Swal.fire({
            title: 'حذف گزینه؟',
            text: 'این گزینه حذف خواهد شد',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'حذف',
            cancelButtonText: 'انصراف'
        });
        if (!result.isConfirmed) return;
        if ((row.dataset.group || '') === 'allowed_services') {
            const clearLinksRes = await this.req('save_service_product_links', {
                service_lookup_item_id: id,
                product_ids: []
            });
            if (clearLinksRes.status !== 'success') {
                Swal.fire('خطا', clearLinksRes.message || 'خطا در پاکسازی لینک محصولات', 'error');
                return;
            }
        }
        const res = await this.req('delete_lookup_item', { id });
        if (res.status === 'success') {
            row.remove();
            await this.loadLookups();
            await this.loadProducts();
            await this.loadLookupItems(this.state.lookupSelectedGroup);
        } else {
            Swal.fire('خطا', res.message || 'خطا در حذف', 'error');
        }
    },
    // UI Helpers
	    setView(id, options = {}) {
        const opts = options || {};
        const target = this.normalizeView(id);
        const currentActive = document.querySelector('.sidebar-item.active');
        const fromView = currentActive ? currentActive.id.replace('menu-', '') : '';
        this.logTelemetry('route_transition', { from: fromView || '', to: target || '', source: String(opts.source || 'setView') }, 'debug');

        if (!this.canAccessView(target, { showWarning: !opts.silentDenied })) {
            return false;
        }

	        document.querySelectorAll('.view-section').forEach(e => e.classList.add('hidden'));
	        const viewEl = document.getElementById('view-'+target);
	        if(viewEl) viewEl.classList.remove('hidden');
        
        document.querySelectorAll('.sidebar-item').forEach(e => e.classList.remove('active', 'text-yellow-500'));
        const menuEl = document.getElementById('menu-'+target);
        if(menuEl) menuEl.classList.add('active', 'text-yellow-500');
        
	        const titles = {
	                    'dashboard': 'داشبورد',
                        'performance-dashboard': 'داشبورد عملکرد',
	                    'leads': 'لیدها',
	                    'operation-manager': 'مدیریت عملیات',
	                    'blataklif-leads': 'لیدهای بلاتکلیف',
	                    'deals': 'معاملات',
	                    'legacy-deals': 'معاملات گذشته دیدار',
	                    'referrals': 'ارجاعات',
	                    'persons': 'اشخاص',
	                    'settings': 'تنظیمات',
	                    'exports': 'خروجی‌ها',
	                    'audit-logs': 'لاگ‌ها',
	                    'phone-logs': 'لاگ تماس‌ها',
	                    'users': 'کاربران',
	                    'lookups': 'Lookup Manager',
	                    'reminders': 'یادآوری‌ها'
	                };
	        const titleEl = document.getElementById('page-title');
	        if(titleEl) titleEl.textContent = titles[target] || target;
	        
	        if(target === 'dashboard') this.loadDashboard();
            if(target === 'performance-dashboard') this.loadPerformanceDashboard({ useState: true });
            if(target === 'reminders') this.loadReminders();
	        if(target === 'leads') {
	            this.loadProducts();
	            this.loadLeads();
	            this.initLeadFiltersDatePickers();
	        }
            if (target === 'operation-manager') {
                this.loadOperationManager();
            }
	        if (target === 'blataklif-leads') {
	            this.loadBlataklifLeads(1);
	        }
	        if(target === 'deals') {
	            this.loadProducts(); // ensure products loaded for deal modal
	            this.loadDeals();
	            this.initLeadFiltersDatePickers();
	        }
        if (target === 'legacy-deals') {
            this.loadLegacyDeals();
        }
	        if(target === 'referrals' && this.isAdmin()) this.loadReferrals();
	        if(target === 'persons') this.loadPersons();
	        if(target === 'settings') this.loadSettings();
	        if(target === 'users') this.loadUsers();
            if(target === 'lookups' && this.isAdmin()) this.loadLookupAdmin(true);
	        if(target === 'exports' && (this.canCurrentUserCapabilityClient('menu.exports') || this.canCurrentUserCapabilityClient('export.catalog'))) this.loadExportCatalog();
	        if(target === 'cheques' && this.canCurrentUserCapabilityClient('cheques.view')) this.loadCheques();
	        if(target === 'audit-logs' && this.isAdmin()) this.loadAuditLogs(true);
	        if(target === 'phone-logs' && this.isAdmin()) this.loadPhoneLogs();

            if (this.featureFlags.historyV1 && !this._isApplyingHistoryState) {
                setTimeout(() => this.replaceCurrentHistoryUiState('set_view_render'), 0);
            }
            return true;
	    },

    // ===== C9: financial cheque workflow =====
    async loadCheques() {
        if (!this.canCurrentUserCapabilityClient('cheques.view')) return;
        const body = document.getElementById('cheques-tbody');
        if (body) body.innerHTML = '<tr><td colspan="9" class="text-center text-gray-400 py-6">در حال بارگذاری...</td></tr>';
        const filters = {
            check_status: document.getElementById('cheques-filter-status')?.value || '',
            collection_status: document.getElementById('cheques-filter-collection')?.value || '',
            search: document.getElementById('cheques-filter-search')?.value || '',
        };
        try {
            const res = await this.req('get_cheques', filters);
            if (res.status !== 'success') {
                if (body) body.innerHTML = `<tr><td colspan="9" class="text-center text-red-400 py-6">${this.escapeHtml(res.message || 'خطا')}</td></tr>`;
                return;
            }
            this.state.cheques = res.cheques || [];
            this.renderCheques();
        } catch (e) {
            if (body) body.innerHTML = '<tr><td colspan="9" class="text-center text-red-400 py-6">خطا در دریافت چک‌ها</td></tr>';
        }
    },

    chequeStatusBadge(status) {
        const map = { approved: ['تایید', 'bg-green-500/20 text-green-400'], rejected: ['رد', 'bg-red-500/20 text-red-400'], pending: ['در انتظار', 'bg-gray-500/20 text-gray-300'] };
        const [label, cls] = map[status] || map.pending;
        return `<span class="text-xs px-2 py-0.5 rounded ${cls}">${label}</span>`;
    },
    chequeCollectionBadge(status) {
        const map = { collected: ['وصول', 'bg-green-500/20 text-green-400'], returned: ['برگشت', 'bg-red-500/20 text-red-400'], blataklif: ['بلاتکلیف', 'bg-yellow-500/20 text-yellow-400'], pending: ['در انتظار', 'bg-gray-500/20 text-gray-300'] };
        const [label, cls] = map[status] || map.pending;
        return `<span class="text-xs px-2 py-0.5 rounded ${cls}">${label}</span>`;
    },

    renderCheques() {
        const body = document.getElementById('cheques-tbody');
        if (!body) return;
        const rows = this.state.cheques || [];
        if (!rows.length) {
            body.innerHTML = '<tr><td colspan="9" class="text-center text-gray-400 py-6">چکی یافت نشد</td></tr>';
            return;
        }
        const canReview = this.canCurrentUserCapabilityClient('cheques.review');
        const canCollect = this.canCurrentUserCapabilityClient('cheques.collect');
        body.innerHTML = rows.map(r => {
            const id = `${r.deal_id}|${r.check_number}`;
            const actions = [];
            if (canReview) actions.push(`<button onclick="app.setChequeStatus('${this.escapeHtml(String(r.deal_id))}','${this.escapeHtml(String(r.check_number))}')" class="text-xs px-2 py-1 rounded border border-yellow-500 text-yellow-400 hover:bg-yellow-500/10">وضعیت چک</button>`);
            if (canCollect) actions.push(`<button onclick="app.setChequeCollection('${this.escapeHtml(String(r.deal_id))}','${this.escapeHtml(String(r.check_number))}')" class="text-xs px-2 py-1 rounded border border-blue-500 text-blue-400 hover:bg-blue-500/10">وضعیت وصول</button>`);
            return `<tr class="border-b border-gray-800 hover:bg-gray-800/40" data-cheque="${this.escapeHtml(id)}">
                <td class="py-2 px-2 text-sm">${this.escapeHtml(r.contact_name || '-')}<div class="text-[11px] text-gray-500">${this.escapeHtml(r.customer_code || '')}</div></td>
                <td class="py-2 px-2 text-sm">${this.escapeHtml(r.deal_title || '-')}</td>
                <td class="py-2 px-2 text-sm">${this.escapeHtml(r.check_number || '-')}</td>
                <td class="py-2 px-2 text-sm">${this.escapeHtml(r.check_bank_name || '-')}</td>
                <td class="py-2 px-2 text-sm">${this.formatMoney ? this.formatMoney(r.amount) : (r.amount || 0)}</td>
                <td class="py-2 px-2 text-sm">${this.escapeHtml(r.check_due_date || r.check_date || '-')}</td>
                <td class="py-2 px-2">${this.chequeStatusBadge(r.check_status)}${r.check_status_note ? `<div class="text-[11px] text-gray-500 mt-1">${this.escapeHtml(r.check_status_note)}</div>` : ''}</td>
                <td class="py-2 px-2">${this.chequeCollectionBadge(r.collection_status)}${r.collection_date ? `<div class="text-[11px] text-gray-500 mt-1">${this.escapeHtml(r.collection_date)}</div>` : ''}</td>
                <td class="py-2 px-2"><div class="flex gap-2">${actions.join('') || '<span class="text-xs text-gray-600">-</span>'}</div></td>
            </tr>`;
        }).join('');
    },

    async setChequeStatus(dealId, checkNumber) {
        if (!this.canCurrentUserCapabilityClient('cheques.review')) return;
        const { value: form } = await Swal.fire({
            title: 'وضعیت چک',
            html: `<select id="swal-cheque-status" class="swal2-input"><option value="approved">تایید</option><option value="rejected">رد</option><option value="pending">در انتظار</option></select>
                   <textarea id="swal-cheque-note" class="swal2-textarea" placeholder="توضیحات (برای رد الزامی)"></textarea>`,
            focusConfirm: false, showCancelButton: true, confirmButtonText: 'ثبت', cancelButtonText: 'انصراف',
            preConfirm: () => {
                const status = document.getElementById('swal-cheque-status').value;
                const note = document.getElementById('swal-cheque-note').value.trim();
                if (status === 'rejected' && !note) { Swal.showValidationMessage('برای رد چک، درج توضیحات الزامی است'); return false; }
                return { status, note };
            }
        });
        if (!form) return;
        const res = await this.req('set_cheque_status', { deal_id: dealId, check_number: checkNumber, status: form.status, note: form.note });
        if (res.status === 'success') { this.loadCheques(); } else { Swal.fire('خطا', res.message || 'خطا در ثبت وضعیت', 'error'); }
    },

    async setChequeCollection(dealId, checkNumber) {
        if (!this.canCurrentUserCapabilityClient('cheques.collect')) return;
        const { value: form } = await Swal.fire({
            title: 'وضعیت وصول چک',
            html: `<select id="swal-coll-status" class="swal2-input"><option value="collected">وصول</option><option value="returned">برگشت</option><option value="blataklif">بلاتکلیف</option><option value="pending">در انتظار</option></select>
                   <input id="swal-coll-date" class="swal2-input" placeholder="تاریخ (1403/01/01)">`,
            focusConfirm: false, showCancelButton: true, confirmButtonText: 'ثبت', cancelButtonText: 'انصراف',
            preConfirm: () => {
                const status = document.getElementById('swal-coll-status').value;
                const persian = document.getElementById('swal-coll-date').value.trim();
                let gregorian = '';
                if (persian) { try { const g = persianToGregorian(persian); gregorian = g ? g.split('T')[0] : ''; } catch (e) {} }
                if ((status === 'collected' || status === 'returned') && !gregorian) { Swal.showValidationMessage('برای وصول/برگشت، درج تاریخ معتبر الزامی است'); return false; }
                return { status, collection_date: gregorian };
            }
        });
        if (!form) return;
        const res = await this.req('set_cheque_collection', { deal_id: dealId, check_number: checkNumber, status: form.status, collection_date: form.collection_date });
        if (res.status === 'success') { this.loadCheques(); } else { Swal.fire('خطا', res.message || 'خطا در ثبت وضعیت وصول', 'error'); }
    },

	    deleteLeadFromLeadModal() {
	        const form = document.getElementById('lead-form');
	        const contactId = form?.dataset?.contactId || '';
	        return this.deleteLead(contactId);
	    },

	    auditLogs: { offset: 0, limit: 100, total: 0, rows: [] },

	    async loadAuditLogs(reset = false) {
	        if (!this.isAdmin()) return;
	        if (reset) this.auditLogs.offset = 0;

	        const action = document.getElementById('audit-filter-action')?.value || '';
	        const entityType = document.getElementById('audit-filter-entity')?.value || '';
            const personSearch = document.getElementById('audit-filter-person')?.value || '';
	        const dateFromJ = (document.getElementById('audit-filter-from')?.value || '').trim();
	        const dateToJ = (document.getElementById('audit-filter-to')?.value || '').trim();
	        const dateFromG = dateFromJ ? (persianToGregorian(this.toEnglishDigits(dateFromJ), '00:00') || '') : '';
	        const dateToG = dateToJ ? (persianToGregorian(this.toEnglishDigits(dateToJ), '23:59') || '') : '';
	        const dateFrom = dateFromG ? String(dateFromG).split(' ')[0] : '';
	        const dateTo = dateToG ? String(dateToG).split(' ')[0] : '';

	        try {
	            this.initJalaliDatePickers([
	                document.getElementById('audit-filter-from'),
	                document.getElementById('audit-filter-to')
	            ]);
	            const res = await this.req('get_audit_logs', {
	                action,
	                entity_type: entityType,
                    person_search: personSearch,
	                date_from: dateFrom,
	                date_to: dateTo,
	                limit: this.auditLogs.limit,
	                offset: this.auditLogs.offset
	            });
	            if (res.status !== 'success') {
	                Swal.fire('خطا', res.message || 'خطا در دریافت لاگ‌ها', 'error');
	                return;
	            }
	            this.auditLogs.total = res.logs?.total || 0;
	            this.auditLogs.rows = res.logs?.rows || [];
	            this.renderAuditLogs();
	        } catch (e) {
	            Swal.fire('خطا', 'خطا در دریافت لاگ‌ها', 'error');
	        }
	    },

	    async loadPhoneLogs() {
	        if (!this.isAdmin()) return;
	        
	        // Load phone logs view
	        const viewEl = document.getElementById('view-phone-logs');
	        if (!viewEl) {
	            const content = document.getElementById('content');
	            const section = document.createElement('div');
	            section.id = 'view-phone-logs';
	            section.className = 'view-section hidden flex-1 flex flex-col overflow-hidden';
	            
	            // Load HTML using fetch
	            try {
	                const response = await fetch('?view=phone-logs');
	                const html = await response.text();
	                section.innerHTML = html;
	                content.appendChild(section);
	            } catch (e) {
	                console.error('Failed to load phone logs view:', e);
	                Swal.fire('خطا', 'خطا در بارگذاری صفحه', 'error');
	            }
	        }
	    },

	    auditLogsPrev() {
	        if (this.auditLogs.offset <= 0) return;
	        this.auditLogs.offset = Math.max(0, this.auditLogs.offset - this.auditLogs.limit);
	        this.loadAuditLogs(false);
	    },

	    auditLogsNext() {
	        if (this.auditLogs.offset + this.auditLogs.limit >= this.auditLogs.total) return;
	        this.auditLogs.offset += this.auditLogs.limit;
	        this.loadAuditLogs(false);
	    },

        async recoverDeletedRecord(auditLogId) {
            const id = Number(auditLogId || 0);
            if (!id) return;

            const confirmRes = await Swal.fire({
                title: 'بازیابی رکورد حذف‌شده؟',
                text: 'این عملیات رکورد انتخابی را به دیتابیس برمی‌گرداند.',
                icon: 'warning',
                showCancelButton: true,
                confirmButtonText: 'بله، بازیابی شود',
                cancelButtonText: 'انصراف'
            });
            if (!confirmRes.isConfirmed) return;

            try {
                const res = await this.req('recover_deleted_record', { audit_log_id: id });
                if (res.status !== 'success') {
                    Swal.fire('خطا', res.message || 'خطا در بازیابی', 'error');
                    return;
                }
                Swal.fire('انجام شد', res.message || 'رکورد بازیابی شد', 'success');
                this.loadAuditLogs(false);
            } catch (error) {
                Swal.fire('خطا', 'خطا در بازیابی رکورد', 'error');
            }
        },

	    renderAuditLogs() {
	        const tbody = document.getElementById('audit-logs-table');
	        const info = document.getElementById('audit-logs-info');
	        if (!tbody) return;

	        const rows = Array.isArray(this.auditLogs.rows) ? this.auditLogs.rows : [];
	        if (!rows.length) {
	            tbody.innerHTML = '<tr><td class="p-3 text-gray-500" colspan="8">لاگی یافت نشد</td></tr>';
	        } else {
	            tbody.innerHTML = rows.map(r => {
	                let snapshot = null;
	                try { snapshot = r.snapshot_json ? JSON.parse(r.snapshot_json) : null; } catch (e) {}

	                const actor = r.actor_name_resolved || r.actor_name || (r.actor_didar_id || '') || (r.actor_user_id ? `user#${r.actor_user_id}` : '');
	                const actorIp = r.actor_ip || '';
	                const action = r.action === 'delete' ? 'حذف' : (r.action === 'owner_change' ? 'تغییر مسئول' : (r.action || ''));
                    const entityMap = {
                        person: 'لید',
                        deal: 'معامله',
                        activity: 'فعالیت',
                        payment: 'پرداخت'
                    };
	                const entity = entityMap[r.entity_type] || (r.entity_type || '');
                    
                    let relatedPerson = r.related_person_name || '';
                    if (r.related_person_mobile) relatedPerson += ` (${r.related_person_mobile})`;
                    if (!relatedPerson && r.related_person_didar_id) relatedPerson = r.related_person_didar_id;

	                let detail = '';
	                if (r.action === 'delete' && r.entity_type === 'person') {
	                    const p = snapshot?.person;
	                    if (p) detail = `${(p.first_name || '')} ${(p.last_name || '')}`.trim() || (p.mobile_phone || '');
	                } else if (r.action === 'delete' && r.entity_type === 'deal') {
	                    const d = snapshot?.deal;
	                    if (d) detail = (d.title || '') || '';
	                } else if (r.action === 'delete' && r.entity_type === 'activity') {
	                    const a = snapshot?.activity;
	                    if (a) detail = (a.activity_type_title || a.title || a.didar_activity_id || '');
	                } else if (r.action === 'delete' && r.entity_type === 'payment') {
	                    const p = snapshot?.payment || snapshot?.transaction;
	                    if (p) detail = `مبلغ: ${p.amount || ''} | روش: ${p.method || ''} | #${p.id || ''}`;
	                } else if (r.action === 'owner_change') {
	                    const personName = snapshot?.person_name || '';
	                    const oldName = snapshot?.old_owner_name || (snapshot?.old_owner_id || '');
	                    const newName = snapshot?.new_owner_name || (snapshot?.new_owner_id || '');
	                    detail = `${personName ? (personName + ' | ') : ''}از ${oldName || '-'} به ${newName || '-'}`;
	                } else if (snapshot) {
                        // Generic JSON handler
                        if (snapshot.details) {
                             const d = snapshot.details;
                             if (d.text) detail += `Text: ${d.text} `;
                             if (d.element) detail += `[${d.element}] `;
                             if (d.path) detail += `Path: ${d.path} `;
                             if (d.uri) detail += `URI: ${d.uri} `;
                             if (d.input) detail += `Input: ${JSON.stringify(d.input).substring(0, 50)}... `;
                        } else if (snapshot.uri) {
                             detail += `URI: ${snapshot.uri} `;
                             if (snapshot.input) detail += `Input: ${JSON.stringify(snapshot.input).substring(0, 50)}... `;
                        } else {
                             // Clean up keys
                             const keys = Object.keys(snapshot).filter(k => k!=='details');
                             if (keys.length > 0) {
                                 detail = keys.map(k => `${k}: ${typeof snapshot[k] === 'object' ? '...' : snapshot[k]}`).join(', ');
                             } else {
                                 detail = JSON.stringify(snapshot).substring(0, 100);
                             }
                        }
                    }

                    const canRecover = r.action === 'delete' && ['person', 'deal', 'activity', 'payment'].includes((r.entity_type || '').toLowerCase());
                    const actionButton = canRecover
                        ? `<button class="px-2 py-1 rounded bg-emerald-700 hover:bg-emerald-600 text-xs" onclick="app.recoverDeletedRecord(${Number(r.id || 0)})">بازیابی</button>`
                        : '<span class="text-gray-500">-</span>';

	                return `
	                    <tr>
	                        <td class="p-3 whitespace-nowrap">${this.escapeHtml(r.created_at || '')}</td>
	                        <td class="p-3 whitespace-nowrap">${this.escapeHtml(actor || '')}</td>
                            <td class="p-3 whitespace-nowrap">${this.escapeHtml(relatedPerson)}</td>
	                        <td class="p-3 whitespace-nowrap">${this.escapeHtml(actorIp || '')}</td>
	                        <td class="p-3 whitespace-nowrap">${this.escapeHtml(action)}</td>
	                        <td class="p-3 whitespace-nowrap">${this.escapeHtml(entity)}</td>
	                        <td class="p-3">${this.escapeHtml(detail || '')}</td>
                            <td class="p-3 whitespace-nowrap">${actionButton}</td>
	                    </tr>
	                `;
	            }).join('');
	        }

	        const from = this.auditLogs.total ? (this.auditLogs.offset + 1) : 0;
	        const to = Math.min(this.auditLogs.offset + this.auditLogs.limit, this.auditLogs.total);
	        if (info) info.textContent = `نمایش ${from} تا ${to} از ${this.auditLogs.total}`;
	    },

	    async loadExportCatalog(force = false) {
	        if (!this.canCurrentUserCapabilityClient('menu.exports') && !this.canCurrentUserCapabilityClient('export.catalog')) return;

	        if (this.state.exportCatalog && !force) {
	            this.renderExportUi();
	            return;
	        }

	        const datasetSelect = document.getElementById('export-dataset');
	        if (datasetSelect) datasetSelect.innerHTML = '<option value="">در حال بارگذاری...</option>';

	        try {
	            const res = await this.req('export_catalog', {});
	            if (res.status !== 'success') {
	                Swal.fire('خطا', res.message || 'خطا در دریافت لیست خروجی‌ها', 'error');
	                return;
	            }
	            this.state.exportCatalog = res;
	            this.renderExportUi();
	        } catch (e) {
	            Swal.fire('خطا', 'خطا در دریافت لیست خروجی‌ها', 'error');
	        }
	    },

	    renderExportUi() {
	        const datasetSelect = document.getElementById('export-dataset');
	        const catalog = this.state.exportCatalog;
	        if (!datasetSelect || !catalog || !Array.isArray(catalog.datasets)) return;

	        datasetSelect.innerHTML = '<option value="">-- انتخاب کنید --</option>' + catalog.datasets.map(d => {
	            return `<option value="${this.escapeHtml(d.id)}">${this.escapeHtml(d.title)}</option>`;
	        }).join('');

	        if (!this.state.exportCurrentDatasetId && catalog.datasets.length) {
	            this.state.exportCurrentDatasetId = catalog.datasets[0].id;
	        }
	        if (this.state.exportCurrentDatasetId) {
	            datasetSelect.value = this.state.exportCurrentDatasetId;
	        }

	        this.onExportDatasetChange();
	    },

	    onExportDatasetChange() {
	        const datasetSelect = document.getElementById('export-dataset');
	        const formatSelect = document.getElementById('export-format');
	        const catalog = this.state.exportCatalog;
	        if (!datasetSelect || !catalog || !Array.isArray(catalog.datasets)) return;

	        const datasetId = datasetSelect.value;
	        this.state.exportCurrentDatasetId = datasetId || null;
	        const dataset = catalog.datasets.find(d => d.id === datasetId);

	        if (formatSelect) {
	            const allowed = dataset?.formats || ['csv', 'xlsx'];
	            formatSelect.innerHTML = allowed.map(f => `<option value="${f}">${f.toUpperCase()}</option>`).join('');
	            if (!allowed.includes(formatSelect.value)) {
	                formatSelect.value = allowed[0] || 'csv';
	            }
	        }

	        this.renderExportFilters(dataset);
	        this.renderExportColumns(dataset);
	    },

	    renderExportFilters(dataset) {
	        const container = document.getElementById('export-filters');
	        if (!container) return;
	        if (!dataset) {
	            container.innerHTML = '';
	            return;
	        }

	        const filters = Array.isArray(dataset.filters) ? dataset.filters : [];
	        container.innerHTML = filters.map(f => {
	            const key = this.escapeHtml(f.key || '');
	            const label = this.escapeHtml(f.label || key);
	            const type = f.type || 'text';
	            const placeholder = this.escapeHtml(f.placeholder || '');

	            if (type === 'checkbox') {
	                return `
	                    <label class="flex items-center gap-2 text-sm text-gray-300 bg-gray-900/40 border border-gray-700 rounded-lg px-3 py-2">
	                        <input type="checkbox" class="accent-yellow-500" data-export-filter="${key}" value="1" onchange="app.onExportFilterChange()">
	                        <span>${label}</span>
	                    </label>
	                `;
	            }

	            if (type === 'select') {
	                const opts = Array.isArray(f.options) ? f.options : [];
	                const optionsHtml = opts.map(o => `<option value="${this.escapeHtml(o.value ?? '')}">${this.escapeHtml(o.label ?? '')}</option>`).join('');
	                return `
	                    <div>
	                        <label class="block text-xs text-gray-400 mb-1">${label}</label>
	                        <select class="input bg-gray-800 w-full" data-export-filter="${key}">${optionsHtml}</select>
	                    </div>
	                `;
	            }

	            if (type === 'date_jalali') {
	                return `
	                    <div>
	                        <label class="block text-xs text-gray-400 mb-1">${label}</label>
	                        <input type="text" class="input bg-gray-800 w-full export-date-jalali" data-export-filter="${key}" data-jalali-picker="1" placeholder="${placeholder}">
	                    </div>
	                `;
	            }

	            return `
	                <div>
	                    <label class="block text-xs text-gray-400 mb-1">${label}</label>
	                    <input type="text" class="input bg-gray-800 w-full" data-export-filter="${key}" placeholder="${placeholder}">
	                </div>
	            `;
	        }).join('');

	        this.initJalaliDatePickers(container.querySelectorAll('input[data-jalali-picker="1"]'));
	    },

	    onExportFilterChange() {
	        // Used to re-render columns when include_raw_json toggles
	        const datasetSelect = document.getElementById('export-dataset');
	        const catalog = this.state.exportCatalog;
	        if (!datasetSelect || !catalog || !Array.isArray(catalog.datasets)) return;
	        const dataset = catalog.datasets.find(d => d.id === datasetSelect.value);
	        this.renderExportColumns(dataset);
	    },

	    renderExportColumns(dataset) {
	        const container = document.getElementById('export-columns');
	        if (!container) return;
	        if (!dataset) {
	            container.innerHTML = '';
	            return;
	        }

	        const includeRaw = !!document.querySelector('[data-export-filter="include_raw_json"]')?.checked;
	        const cols = Array.isArray(dataset.columns) ? dataset.columns : [];

	        container.innerHTML = cols.map(c => {
	            const key = c.key || '';
	            const label = c.label || key;
	            const disabled = (key === 'raw_json' && !includeRaw);
	            const checked = !disabled && (key !== 'raw_json'); // default: raw_json unchecked unless enabled by user
	            return `
	                <label class="flex items-center gap-2 text-sm text-gray-300 bg-gray-900/40 border border-gray-700 rounded-lg px-3 py-2 export-col-item" data-export-col-item="1">
	                    <input type="checkbox" class="accent-yellow-500" data-export-column="${this.escapeHtml(key)}" ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
	                    <span class="${disabled ? 'text-gray-600' : ''}">${this.escapeHtml(label)}</span>
	                </label>
	            `;
	        }).join('');

	        this.filterExportColumns();
	    },

	    filterExportColumns() {
	        const q = (document.getElementById('export-columns-search')?.value || '').trim().toLowerCase();
	        document.querySelectorAll('#export-columns .export-col-item').forEach(el => {
	            const text = (el.textContent || '').toLowerCase();
	            if (!q || text.includes(q)) {
	                el.classList.remove('hidden');
	            } else {
	                el.classList.add('hidden');
	            }
	        });
	    },

	    selectAllExportColumns(on) {
	        document.querySelectorAll('#export-columns input[type="checkbox"][data-export-column]').forEach(cb => {
	            if (cb.disabled) return;
	            cb.checked = !!on;
	        });
	    },

	    collectExportFilters() {
	        const out = {};
	        document.querySelectorAll('[data-export-filter]').forEach(el => {
	            const key = el.getAttribute('data-export-filter');
	            if (!key) return;
	            if (el.type === 'checkbox') {
	                out[key] = el.checked ? '1' : '0';
	            } else {
	                out[key] = (el.value || '').trim();
	            }
	        });

	        // Convert Jalali date filters to Gregorian where backend needs it.
	        // - *_from_jalali -> *_from (00:00)
	        // - *_to_jalali -> *_to (23:59)
	        Object.keys(out).forEach((k) => {
	            if (!k.endsWith('_jalali')) return;
	            const val = (out[k] || '').trim();
	            if (!val) return;

	            const base = k.replace(/_jalali$/, '');
	            if (base.endsWith('_from')) {
	                const g = persianToGregorian(val, '00:00');
	                if (g) out[base] = g;
	            } else if (base.endsWith('_to')) {
	                const g = persianToGregorian(val, '23:59');
	                if (g) out[base] = g;
	            } else {
	                // fallback: store as-is (not converted)
	            }
	        });

	        return out;
	    },

	    initJalaliDatePickers(nodeList) {
	        const inputs = Array.from(nodeList || []).filter(Boolean);
	        if (!inputs.length) return;

	        const initInput = (input) => {
	            if (!input) return;
	            if (typeof $ === 'undefined' || typeof $.fn.persianDatepicker === 'undefined') {
	                setTimeout(() => initInput(input), 200);
	                return;
	            }
	            try {
	                if ($(input).data('persianDatepicker')) {
	                    $(input).persianDatepicker('destroy');
	                }
	            } catch (_) {}
	            $(input).persianDatepicker({
	                format: 'YYYY/MM/DD',
	                initialValue: false,
	                autoClose: true,
	                observer: true
	            });
	        };

	        inputs.forEach(initInput);
	    },

	    collectExportColumns() {
	        const cols = [];
	        document.querySelectorAll('#export-columns input[type="checkbox"][data-export-column]').forEach(cb => {
	            if (cb.checked && !cb.disabled) {
	                cols.push(cb.getAttribute('data-export-column'));
	            }
	        });
	        return cols.filter(Boolean);
	    },

	    submitExport() {
	        if (!this.canCurrentUserCapabilityClient('menu.exports') && !this.canCurrentUserCapabilityClient('export.catalog')) {
	            Swal.fire('خطا', 'دسترسی خروجی برای شما مجاز نیست', 'error');
	            return;
	        }

	        const dataset = document.getElementById('export-dataset')?.value || '';
	        const format = document.getElementById('export-format')?.value || 'csv';
	        if (!dataset) {
	            Swal.fire('خطا', 'نوع خروجی را انتخاب کنید', 'error');
	            return;
	        }

	        const columns = this.collectExportColumns();
	        const filters = this.collectExportFilters();

	        document.getElementById('export-form-dataset').value = dataset;
	        document.getElementById('export-form-format').value = format;
	        document.getElementById('export-form-columns').value = JSON.stringify(columns);
	        document.getElementById('export-form-filters').value = JSON.stringify(filters);

	        document.getElementById('export-form').submit();
	    },
    
    // رفتن به بخش لیدها با فیلتر مشخص
    async goToLeadsWithFilter(stage = 'all') {
        const moved = await this.navigateToView('leads', { source: 'go_to_leads_filter' });
        if (!moved) return;
        // تنظیم فیلترها
        const stageFilter = document.getElementById('leads-stage-filter');
        const registerFrom = document.getElementById('leads-register-from');
        const registerTo = document.getElementById('leads-register-to');
        const activityFrom = document.getElementById('leads-activity-from');
        const activityTo = document.getElementById('leads-activity-to');
        const productFilter = document.getElementById('leads-product-filter');
        
        if(stageFilter) stageFilter.value = stage;
        [registerFrom, registerTo, activityFrom, activityTo].forEach(inp => { if (inp) inp.value = ''; });
        if (productFilter) productFilter.value = '';
        
        // بارگذاری لیدها با فیلتر
        this.loadLeads();
    },

    async goToDealsWithFilter(pipelineStage = 'all') {
        const moved = await this.navigateToView('deals', { source: 'go_to_deals_filter' });
        if (!moved) return;
        const stageFilter = document.getElementById('deals-pipeline-filter');
        if (stageFilter) stageFilter.value = pipelineStage;
        this.loadDeals();
    },

    safeSetText(id, value = '-') {
        const el = document.getElementById(id);
        if (el) el.textContent = value;
    },

    resetProfileModalState(options = {}) {
        const opts = options || {};
        const forOpen = opts.forOpen === true;

        this.safeSetText('prof-name', '---');
        this.safeSetText('prof-mobile', '-');
        this.safeSetText('prof-email', '-');
        this.safeSetText('prof-info-name', '-');
        this.safeSetText('prof-info-customer-code', '-');
        this.safeSetText('prof-info-mobile', '-');
        this.safeSetText('prof-info-mobile2', '-');
        this.safeSetText('prof-info-mobile3', '-');
        this.safeSetText('prof-info-mobile4', '-');
        this.safeSetText('prof-info-email', '-');
        this.safeSetText('prof-info-owner', '-');
        this.safeSetText('prof-info-prev-owner', '-');
        this.safeSetText('prof-info-register', '-');
        this.safeSetText('prof-info-created-by', '-');
        this.safeSetText('prof-info-created-via', '-');
        document.getElementById('prof-info-created-by-container')?.classList.add('hidden');
        document.getElementById('prof-info-created-via-container')?.classList.add('hidden');
        this.safeSetText('prof-info-sale-status', '-');
        this.safeSetText('prof-info-national-id', '-');
        this.safeSetText('prof-info-city', '-');
        this.safeSetText('prof-info-job', '-');
        this.safeSetText('prof-info-job-desc', '-');
        this.safeSetText('prof-info-source', '-');
        this.safeSetText('prof-info-source-detail', '-');
        this.safeSetText('prof-info-topic', '-');
        this.safeSetText('prof-info-products', '-');
        this.safeSetText('prof-info-acq-duration', '-');
        this.safeSetText('prof-info-extra-info', '-');
        this.safeSetText('prof-info-financial-level', '-');
        this.safeSetText('prof-info-asset', '-');
        this.safeSetText('prof-info-income', '-');
        this.safeSetText('prof-info-purchase-status', '-');
        this.safeSetText('prof-info-requested-services', '-');
        this.safeSetText('prof-info-background', '-');
        const stageEl = document.getElementById('prof-info-stage');
        if (stageEl) stageEl.innerHTML = '';

        const deals = document.getElementById('prof-deals');
        if (deals) deals.innerHTML = forOpen ? '<p class="text-sm text-gray-500">در حال بارگذاری...</p>' : '';
        const activities = document.getElementById('prof-activities');
        if (activities) activities.innerHTML = forOpen ? '<p class="text-sm text-gray-500">در حال بارگذاری...</p>' : '';
        const sats = document.getElementById('prof-satisfactions');
        if (sats) sats.innerHTML = forOpen ? '<p class="text-sm text-gray-500">در حال بارگذاری...</p>' : '';

        this.state.currentLeadActivities = [];
    },

    resetDealModalState(options = {}) {
        const opts = options || {};
        const keepContact = opts.keepContact === true;
        const prevContact = document.getElementById('deal-contact-id')?.value || window.currentContactId || '';

        const form = document.getElementById('deal-form');
        if (form) form.reset();
        const titleEl = document.getElementById('deal-modal-title');
        if (titleEl) titleEl.textContent = 'ثبت معامله جدید';

        const dealIdInput = document.getElementById('deal-id');
        if (dealIdInput) dealIdInput.value = '';
        const dealContactInput = document.getElementById('deal-contact-id');
        if (dealContactInput) dealContactInput.value = keepContact ? prevContact : '';
        const userName = document.getElementById('deal-user-name');
        if (userName) userName.value = '';
        const userMobile = document.getElementById('deal-user-mobile');
        if (userMobile) userMobile.value = '';
        const createdByWrap = document.getElementById('deal-summary-created-by-wrap');
        if (createdByWrap) createdByWrap.classList.add('hidden');
        this.safeSetText('deal-summary-created-by', '-');
        const createdViaWrap = document.getElementById('deal-summary-created-via-wrap');
        if (createdViaWrap) createdViaWrap.classList.add('hidden');
        this.safeSetText('deal-summary-created-via', '-');
        this.updateDealMarketingUi(null, false);

        this.safeSetText('deal-current-status', '-');
        this.safeSetText('deal-current-stage', '-');
        const dealActDeal = document.getElementById('deal-activity-deal-id');
        if (dealActDeal) dealActDeal.value = '';
        const dealActContact = document.getElementById('deal-activity-contact-id');
        if (dealActContact) dealActContact.value = keepContact ? prevContact : '';
        const dealActStage = document.getElementById('deal-activity-stage');
        if (dealActStage) dealActStage.value = '';
        const dealActCurrent = document.getElementById('deal-activity-current-stage');
        if (dealActCurrent) dealActCurrent.textContent = '-';
        const inline = document.getElementById('deal-activities-inline');
        if (inline) inline.innerHTML = '<div class="text-center text-gray-500 py-4">فعالیتی یافت نشد</div>';

        const failureSection = document.getElementById('deal-failure-reason-section');
        if (failureSection) failureSection.classList.add('hidden');
        const failureReason = document.getElementById('deal-failure-reason');
        if (failureReason) {
            failureReason.value = '';
            failureReason.disabled = false;
        }
        const failureOther = document.getElementById('deal-failure-other-reason');
        if (failureOther) failureOther.classList.add('hidden');
        const failureOtherDesc = document.getElementById('deal-failure-other-description');
        if (failureOtherDesc) {
            failureOtherDesc.value = '';
            failureOtherDesc.readOnly = false;
        }

        this.state.dealHasActivity = false;
        this.state.currentDeal = null;
        this.state.currentDeals = [];
        this.state.dealActivityRequestedServices = [];
        this.paymentRowCounter = 0;
        this.setPaymentsLockState(false);
        this.state.dealInfoLocked = false;
        this.state.dealInfoAdminUnlocked = false;
        this.enableDealFormFields();
        this.dealIsWon = false;
        const paidEditBtn = document.getElementById('deal-paid-edit-toggle-btn');
        if (paidEditBtn) {
            paidEditBtn.classList.add('hidden');
            paidEditBtn.textContent = 'ویرایش اطلاعات قفل‌شده';
        }
    },

    resetActivityModalState() {
        const form = document.querySelector('#modalActivity form');
        if (form) form.reset();
        const activityContactId = document.getElementById('activity-contact-id');
        if (activityContactId) activityContactId.value = '';
        const activityDealId = document.getElementById('activity-deal-id');
        if (activityDealId) activityDealId.value = '';
        const display = document.getElementById('activity-current-stage-display');
        if (display) display.innerHTML = '<span class="text-sm text-gray-500">مرحله تعیین نشده</span>';
        const currentStage = document.getElementById('activity-current-stage');
        if (currentStage) currentStage.value = 'keep_current';
        const leadInfo = document.getElementById('activity-lead-info-display');
        if (leadInfo) leadInfo.classList.add('hidden');
        const leadsSelect = document.getElementById('activity-deal-select');
        if (leadsSelect) leadsSelect.innerHTML = '<option value="">-- انتخاب معامله مرتبط (اختیاری) --</option>';
        this.state.activityStagePrev = '';
    },
     
    openModal(id, options = {}) { 
        const opts = options || {};
        const source = String(opts.source || 'program');
        if (typeof this.closeAllCustomMultiSelectDropdowns === 'function') {
            this.closeAllCustomMultiSelectDropdowns();
        }
        if (document.activeElement && typeof document.activeElement.blur === 'function') {
            document.activeElement.blur();
        }
        const el = document.getElementById(id);
        if(el) el.classList.remove('hidden');
        // Prevent background focus when modal open
        this.setPageInert(true);
        
        // Initialize Persian date picker when activity modal opens
        if(id === 'modalActivity') {
            this.resetActivityModalState();
            // Populate lead info display
            const infoDisplay = document.getElementById('activity-lead-info-display');
            const nameText = document.getElementById('activity-lead-name-text');
            if (this.state.currentLead && infoDisplay && nameText) {
                const fullName = `${this.state.currentLead.first_name || ''} ${this.state.currentLead.last_name || ''}`.trim() || 'بدون نام';
                nameText.textContent = `${fullName} (${this.state.currentLead.mobile_phone || '-'})`;
                infoDisplay.classList.remove('hidden');
            } else if (infoDisplay) {
                infoDisplay.classList.add('hidden');
            }

            // تنظیم contactId از window.currentContactId
            if (window.currentContactId) {
                const activityContactId = document.getElementById('activity-contact-id');
                if (activityContactId) {
                    activityContactId.value = window.currentContactId;
                }
            }

            // Populate deals dropdown with cached deals for this شخص
            if (this.state.currentDeals && this.state.currentDeals.length) {
                this.populateActivityDealSelect(this.state.currentDeals);
            } else {
                this.populateActivityDealSelect([]);
            }
            this.toggleDealActivityFailure();
            
            // Reset schedule fields
            const isScheduledCheckbox = document.getElementById('activity-is-scheduled');
            const scheduleFields = document.getElementById('activity-schedule-fields');
            if (isScheduledCheckbox) isScheduledCheckbox.checked = false;
            if (scheduleFields) scheduleFields.classList.add('hidden');
            
            // Load activity types for modal
            this.loadActivityTypesForModal();

            // Initialize deal selection handling
            this.handleActivityDealSelection();

            const activityStageSelect = document.getElementById('activity-virtual-stage');
            if (activityStageSelect) {
                activityStageSelect.onchange = (e) => {
                    this.state.activityStagePrev = e.target.value;
                    this.toggleDealActivityFailure();
                    this.handleActivityStageChange(e.target.value);
                };
            }
            
            setTimeout(() => {
                // Initialize activity date picker when modal opens (update every time)
                this.initActivityDatePicker(); // This will update the datepicker with today's date
                this.initPersianDatePicker();
                this.syncActivityDatePickerToTehran();
                
                // افزودن event listener به select های مرحله
                // فقط برای تنظیم فرم، بدون وابستگی به virtual_stage
            }, 100);
        }
        
        // Set contact ID for deal modal
        if(id === 'modalDeal' && window.currentContactId) {
            const dealContactId = document.getElementById('deal-contact-id');
            if (dealContactId) {
                dealContactId.value = window.currentContactId;
            }
        }

        if (this.featureFlags.historyV1 && !opts.skipHistory && !this._isApplyingHistoryState) {
            const view = this.normalizeView(this.getCurrentView());
            const top = this.isValidHistoryState(history.state) ? history.state : null;
            const entityId = String(opts.entityId || '');
            const sameModal = !!(top &&
                top.view === view &&
                top.modal &&
                top.modal.id === id &&
                String(top.modal.entityId || '') === entityId);
            if (!sameModal) {
                const modalPayload = { id: String(id) };
                if (entityId) modalPayload.entityId = entityId;
                const state = this.buildHistoryState({
                    view,
                    modal: modalPayload,
                    ui: this.collectHistoryUiState(view)
                });
                this.syncHistoryState(state, { replace: false, source: `modal_open_${source}` });
            }
        }
    },
    
    initPersianDatePicker() {
        // Initialize Persian date picker for activity modal
        const dateInput = document.getElementById('due-date-jalali');
        if (!dateInput) return;
        
        // Wait for jQuery and persianDatepicker to be available
        if (typeof $ === 'undefined' || typeof $.fn.persianDatepicker === 'undefined') {
            // Retry after a short delay
            setTimeout(() => this.initPersianDatePicker(), 200);
            return;
        }
        
        // Destroy existing picker if any
        try {
            if ($(dateInput).data('persianDatepicker')) {
                $(dateInput).persianDatepicker('destroy');
            }
        } catch(e) {
            console.log('No existing picker to destroy');
        }
        
        // Initialize Persian date picker
        try {
            $(dateInput).persianDatepicker({
                observer: true,
                format: 'YYYY/MM/DD',
                altField: '#due-date-hidden',
                altFormat: 'YYYY-MM-DD',
                calendarType: 'persian',
                timePicker: {
                    enabled: false
                },
                initialValue: false,
                autoClose: true,
                calendar: {
                    persian: {
                        locale: 'fa'
                    }
                }
            });
        } catch(e) {
            console.error('Error initializing Persian date picker:', e);
            // Fallback: make it a simple text input
            dateInput.removeAttribute('readonly');
        }
    },
    
    async closeModalFromUser(id) {
        const key = this.getDirtyKeyByModal(id);
        const ok = key ? await this.confirmDiscardChanges(`close_${String(id || '')}`, [key]) : true;
        if (!ok) return false;
        this.clearDirtyFormsByModal(id);
        this.closeModal(id, { source: 'user' });
        return true;
    },

    closeModal(id, options = {}) { 
        const opts = options || {};
        const source = String(opts.source || 'program');
        if (this.featureFlags.historyV1 && source === 'user' && !opts.skipHistory && !this._isApplyingHistoryState) {
            const top = this.isValidHistoryState(history.state) ? history.state : null;
            if (top && top.modal && top.modal.id === id) {
                history.back();
                return;
            }
        }

        const el = document.getElementById(id);
        if(el) el.classList.add('hidden');

        if (id === 'modalProfile') this.resetProfileModalState({ forOpen: false });
        if (id === 'modalDeal') this.resetDealModalState({ keepContact: true });
        if (id === 'modalActivity') this.resetActivityModalState();
        this.clearDirtyFormsByModal(id);

        if (this.featureFlags.historyV1 && !opts.skipHistory && source !== 'history' && !this._isApplyingHistoryState) {
            const view = this.normalizeView(this.getCurrentView());
            const state = this.buildHistoryState({
                view,
                modal: null,
                ui: this.collectHistoryUiState(view)
            });
            this.syncHistoryState(state, { replace: true, source: `modal_close_${source}` });
        }

        // Restore background focusability if no other modals are open
        const anyOpen = document.querySelectorAll('.fixed.inset-0:not(.hidden)').length > 0;
        if (!anyOpen) {
            this.setPageInert(false);
        }
    },
    
    setPageInert(isInert) {
        // فقط روی بخش‌های محتوایی صفحه (view-section ها و سایدبار) اعمال می‌کنیم، نه روی مودال‌ها
        const elements = document.querySelectorAll('.view-section, aside');
        elements.forEach(el => {
            if (isInert) {
                el.setAttribute('aria-hidden', 'true');
                el.classList.add('bg-inert-active');
            } else {
                el.removeAttribute('aria-hidden');
                el.classList.remove('bg-inert-active');
            }
        });
        console.log('setPageInert', isInert, 'applied on', elements.length, 'elements');
    },
    
    // NOTIFICATION FUNCTIONS
    async loadNotifications() {
        try {
            const res = await this.req('get_notifications', { unread_only: '1', limit: 20 });
            if (res.status === 'success') {
                this.renderNotifications(res.notifications || []);
                this.updateNotificationBadge(res.unread_count || 0);
            }
        } catch (error) {
            console.error('Error loading notifications:', error);
        }
    },
    
    renderNotifications(notifications) {
        const container = document.getElementById('notifications-list');
        if (!container) return;
        
        if (notifications.length === 0) {
            container.innerHTML = '<div class="text-center text-gray-500 text-sm py-8">اعلانی وجود ندارد</div>';
            return;
        }
        
        container.innerHTML = notifications.map(notif => {
            const isRead = notif.is_read == 1;
            const timeAgo = this.getTimeAgo(notif.created_at);
            const icon = this.getNotificationIcon(notif.type);
            
            return `
                <div class="p-3 border-b border-gray-700 hover:bg-gray-700/50 cursor-pointer ${!isRead ? 'bg-blue-500/10' : ''}" 
                     onclick="app.handleNotificationClick('${notif.id}', '${notif.entity_type || ''}', '${notif.entity_id || ''}')">
                    <div class="flex items-start gap-3">
                        <div class="text-2xl">${icon}</div>
                        <div class="flex-1">
                            <div class="flex items-center justify-between mb-1">
                                <h4 class="text-sm font-bold text-white ${!isRead ? '' : 'text-gray-400'}">${this.escapeHtml(notif.title)}</h4>
                                ${!isRead ? '<span class="w-2 h-2 bg-blue-500 rounded-full"></span>' : ''}
                            </div>
                            <p class="text-xs text-gray-400 mb-1">${this.escapeHtml(notif.message || '')}</p>
                            <div class="flex items-center justify-between">
                                <span class="text-[10px] text-gray-500">${timeAgo}</span>
                                <button onclick="event.stopPropagation(); app.deleteNotification('${notif.id}')" 
                                        class="text-red-400 hover:text-red-300 text-xs">
                                    <i class="fa-solid fa-trash"></i>
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
            `;
        }).join('');
    },
    
    getNotificationIcon(type) {
        const icons = {
            'lead_assigned': '👤',
            'activity_new': '📝',
            'activity_reminder': '⏰',
            'deal_created': '💼',
            'deal_status_changed': '🔄',
            'satisfaction_new': '⭐'
        };
        return icons[type] || '🔔';
    },
    
    getTimeAgo(dateString) {
        if (!dateString) return '';
        try {
            const date = new Date(dateString);
            const now = new Date();
            const diff = now - date;
            const minutes = Math.floor(diff / 60000);
            const hours = Math.floor(minutes / 60);
            const days = Math.floor(hours / 24);
            
            if (minutes < 1) return 'همین الان';
            if (minutes < 60) return `${minutes} دقیقه پیش`;
            if (hours < 24) return `${hours} ساعت پیش`;
            if (days < 7) return `${days} روز پیش`;
            
            return toPersianDateTime(dateString);
        } catch (e) {
            return dateString;
        }
    },
    
    updateNotificationBadge(count) {
        const badge = document.getElementById('notification-badge');
        if (badge) {
            if (count > 0) {
                badge.textContent = count > 99 ? '99+' : count;
                badge.classList.remove('hidden');
            } else {
                badge.classList.add('hidden');
            }
        }
    },
    
    toggleNotifications() {
        const dropdown = document.getElementById('notifications-dropdown');
        if (!dropdown) return;
        
        if (dropdown.classList.contains('hidden')) {
            dropdown.classList.remove('hidden');
            this.loadNotifications();
        } else {
            dropdown.classList.add('hidden');
        }
    },
    
    async handleNotificationClick(notificationId, entityType, entityId) {
        // Mark as read
        await this.markNotificationRead(notificationId);
        
        // Navigate to entity if exists
        if (entityType && entityId) {
            if (entityType === 'lead' || entityType === 'person') {
                await this.openPersonProfile(entityId);
            } else if (entityType === 'activity') {
                // Could open activity details if needed
            } else if (entityType === 'deal') {
                // Could open deal details if needed
            }
        }
        
        // Reload notifications
        await this.loadNotifications();
    },
    
    async markNotificationRead(notificationId) {
        try {
            await this.req('mark_notification_read', { notification_id: notificationId });
        } catch (error) {
            console.error('Error marking notification as read:', error);
        }
    },
    
    async markAllNotificationsRead() {
        try {
            const res = await this.req('mark_all_notifications_read', {});
            if (res.status === 'success') {
                await this.loadNotifications();
            }
        } catch (error) {
            console.error('Error marking all notifications as read:', error);
        }
    },
    
    async deleteNotification(notificationId) {
        try {
            const res = await this.req('delete_notification', { notification_id: notificationId });
            if (res.status === 'success') {
                await this.loadNotifications();
            }
        } catch (error) {
            console.error('Error deleting notification:', error);
        }
    },
    
    // POLLING SYSTEM
    notificationPollTimer: null,
    notificationPollingStarted: false,
    notificationPollingInFlight: false,
    notificationPollBaseDelayMs: 30000,
    notificationPollMaxDelayMs: 180000,
    notificationPollErrorCount: 0,
    notificationVisibilityHandlerBound: false,

    getNotificationPollDelay() {
        const multiplier = Math.min(Math.max(this.notificationPollErrorCount || 0, 0), 3);
        return Math.min(this.notificationPollBaseDelayMs * Math.pow(2, multiplier), this.notificationPollMaxDelayMs);
    },

    scheduleNotificationPoll(delayMs) {
        if (!this.notificationPollingStarted) {
            return;
        }
        if (this.notificationPollTimer) {
            clearTimeout(this.notificationPollTimer);
            this.notificationPollTimer = null;
        }
        const delay = Math.max(1000, Number(delayMs) || this.notificationPollBaseDelayMs);
        this.notificationPollTimer = setTimeout(() => {
            this.runNotificationPollTick();
        }, delay);
    },

    async runNotificationPollTick(force = false) {
        if (this.notificationPollingInFlight) {
            this.scheduleNotificationPoll(this.getNotificationPollDelay());
            return;
        }

        if (!force && document.visibilityState === 'hidden') {
            this.scheduleNotificationPoll(this.notificationPollBaseDelayMs);
            return;
        }

        this.notificationPollingInFlight = true;
        try {
            const res = await this.req('check_reminders', {});
            if (res.status === 'success') {
                this.notificationPollErrorCount = 0;

                if (res.notifications && res.notifications.length > 0) {
                    await this.loadNotifications();
                }

                if (res.new_notifications && res.new_notifications.length > 0) {
                    res.new_notifications.forEach(notif => {
                        this.showBrowserNotification(notif);
                    });
                }

                if (res.popup_notifications && res.popup_notifications.length > 0) {
                    res.popup_notifications.forEach(notif => {
                        this.showBrowserNotification(notif);
                    });
                }

                if (res.popup_notifications && res.popup_notifications.length > 0) {
                    console.log('Showing popup notifications:', res.popup_notifications);
                    res.popup_notifications.forEach(notif => {
                        if (this.notificationPopupEnabled) {
                            this.showNotificationPopup(notif);
                        }
                        if (this.notificationBrowserEnabled) {
                            this.showBrowserNotification(notif);
                        }
                    });
                }
            } else {
                this.notificationPollErrorCount = Math.min((this.notificationPollErrorCount || 0) + 1, 10);
            }
        } catch (error) {
            this.notificationPollErrorCount = Math.min((this.notificationPollErrorCount || 0) + 1, 10);
            console.error('Error checking reminders:', error);
        } finally {
            this.notificationPollingInFlight = false;
            if (this.notificationPollingStarted) {
                this.scheduleNotificationPoll(this.getNotificationPollDelay());
            }
        }
    },

    startNotificationPolling() {
        if (this.notificationPollingStarted) {
            return;
        }
        this.notificationPollingStarted = true;

        if (!this.notificationVisibilityHandlerBound) {
            this.notificationVisibilityHandlerBound = true;
            document.addEventListener('visibilitychange', () => {
                if (document.visibilityState === 'visible') {
                    this.scheduleNotificationPoll(1000);
                }
            });
        }

        this.scheduleNotificationPoll(this.notificationPollBaseDelayMs);
    },

    stopNotificationPolling() {
        this.notificationPollingStarted = false;
        if (this.notificationPollTimer) {
            clearTimeout(this.notificationPollTimer);
            this.notificationPollTimer = null;
        }
    },
    
    // BROWSER NOTIFICATIONS
    requestNotificationPermission() {
        if ('Notification' in window && Notification.permission === 'default') {
            Notification.requestPermission().then(permission => {
                if (permission === 'granted') {
                    console.log('Notification permission granted');
                }
            });
        }
    },

    // NOTIFICATION SOUND FUNCTIONS
    notificationSoundEnabled: true,

    playNotificationSound() {
        if (!this.notificationSoundEnabled) return;

        try {
            // Create audio context if not exists
            if (!this.audioContext) {
                this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            }

            // Create a simple beep sound
            const oscillator = this.audioContext.createOscillator();
            const gainNode = this.audioContext.createGain();

            oscillator.connect(gainNode);
            gainNode.connect(this.audioContext.destination);

            oscillator.frequency.setValueAtTime(800, this.audioContext.currentTime); // Frequency in Hz
            oscillator.frequency.setValueAtTime(600, this.audioContext.currentTime + 0.1);

            gainNode.gain.setValueAtTime(0.1, this.audioContext.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + 0.5);

            oscillator.start(this.audioContext.currentTime);
            oscillator.stop(this.audioContext.currentTime + 0.5);
        } catch (error) {
            console.log('Sound not supported, trying HTML5 audio');
            // Fallback to HTML5 audio
            try {
                const audio = new Audio();
                audio.src = 'data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmUhBzqL1fLNfTEFIHDF8N6UWgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfTMFIHDF8N6UXgpUX7Hq66xYFw5Gn+DyvmciBzqL1fLNfLM';
                audio.play().catch(e => console.log('Audio play failed:', e));
            } catch (e) {
                console.log('Audio not supported');
            }
        }
    },

    toggleNotificationSound() {
        this.notificationSoundEnabled = !this.notificationSoundEnabled;
        this.updateSoundButton();
        localStorage.setItem('notificationSoundEnabled', this.notificationSoundEnabled);

        // Show feedback
        const message = this.notificationSoundEnabled ? 'صدا نوتیفیکیشن فعال شد' : 'صدا نوتیفیکیشن غیرفعال شد';
        Swal.fire({
            toast: true,
            position: 'bottom-end',
            icon: 'info',
            title: message,
            timer: 2000,
            showConfirmButton: false
        });
    },

    // NOTIFICATION SETTINGS
    showNotificationSettings() {
        Swal.fire({
            title: 'تنظیمات نوتیفیکیشن',
            html: `
                <div class="text-right space-y-4">
                    <div class="flex items-center justify-between">
                        <label class="flex items-center">
                            <input type="checkbox" id="sound-enabled" class="ml-2">
                            <span>فعال کردن صدا</span>
                        </label>
                    </div>
                    <div class="flex items-center justify-between">
                        <label class="flex items-center">
                            <input type="checkbox" id="popup-enabled" class="ml-2" checked>
                            <span>نمایش پاپ‌آپ</span>
                        </label>
                    </div>
                    <div class="flex items-center justify-between">
                        <label class="flex items-center">
                            <input type="checkbox" id="browser-notif-enabled" class="ml-2" checked>
                            <span>نوتیفیکیشن مرورگر (دسکتاپ)</span>
                        </label>
                        <div id="browser-notif-status" class="text-sm"></div>
                    </div>
                    <div class="text-center mt-3">
                        <button type="button" onclick="app.requestNotificationPermission()" class="px-3 py-1 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 transition">
                            درخواست دسترسی نوتیفیکیشن
                        </button>
                    </div>
                </div>
            `,
            confirmButtonText: 'ذخیره',
            cancelButtonText: 'لغو',
            showCancelButton: true,
            preConfirm: () => {
                const soundEnabled = document.getElementById('sound-enabled').checked;
                const popupEnabled = document.getElementById('popup-enabled').checked;
                const browserNotifEnabled = document.getElementById('browser-notif-enabled').checked;

                // Save settings
                localStorage.setItem('notificationSoundEnabled', soundEnabled);
                localStorage.setItem('notificationPopupEnabled', popupEnabled);
                localStorage.setItem('notificationBrowserEnabled', browserNotifEnabled);

                // Update current settings
                this.notificationSoundEnabled = soundEnabled;
                this.notificationPopupEnabled = popupEnabled;
                this.notificationBrowserEnabled = browserNotifEnabled;

                // Update status display
                this._updateNotificationStatus();

                this.updateSoundButton();

                return {
                    soundEnabled,
                    popupEnabled,
                    browserNotifEnabled
                };
            }
        }).then((result) => {
            if (result.isConfirmed) {
                Swal.fire({
                    toast: true,
                    position: 'bottom-end',
                    icon: 'success',
                    title: 'تنظیمات ذخیره شد',
                    timer: 2000,
                    showConfirmButton: false
                });
            }
        });

        // Load current settings
        setTimeout(() => {
            document.getElementById('sound-enabled').checked = this.notificationSoundEnabled;
            document.getElementById('popup-enabled').checked = localStorage.getItem('notificationPopupEnabled') !== 'false';
            document.getElementById('browser-notif-enabled').checked = localStorage.getItem('notificationBrowserEnabled') !== 'false';

            // Show current browser permission status
            this._updateNotificationStatus();
        }, 100);
    },

    updateSoundButton() {
        const button = document.getElementById('sound-toggle-btn');
        if (button) {
            const icon = button.querySelector('i');
            if (icon) {
                icon.className = this.notificationSoundEnabled ?
                    'fa-solid fa-volume-up text-xl' :
                    'fa-solid fa-volume-mute text-xl';
            }
            button.className = this.notificationSoundEnabled ?
                'text-green-400 hover:text-green-300 transition p-2 rounded-full hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-gray-700' :
                'text-gray-400 hover:text-gray-300 transition p-2 rounded-full hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-gray-700';
            button.title = this.notificationSoundEnabled ? 'صدا فعال است - کلیک برای غیرفعال کردن' : 'صدا غیرفعال است - کلیک برای فعال کردن';
        }
    },

    // NOTIFICATION POPUP FUNCTIONS
    notificationPopups: [],

    showNotificationPopup(notification) {
        // Check if popup should be shown
        console.log('Show notification popup called:', notification);

        // Check user settings
        if (!this.notificationPopupEnabled) {
            console.log('Popup notifications disabled by user settings');
            return;
        }

        // Default to show popup if not specified
        if (notification.show_popup !== undefined && !notification.show_popup) {
            console.log('Popup disabled for this notification');
            return;
        }

        // Play sound if enabled
        if (this.notificationSoundEnabled) {
            this.playNotificationSound();
        }

        const popupId = 'popup-' + notification.id + '-' + Date.now();

        // Create popup element
        const popup = document.createElement('div');
        popup.className = 'notification-popup';
        popup.id = popupId;

        const icon = this.getNotificationIcon(notification.type);
        const timeAgo = this.getTimeAgo(notification.created_at);

        popup.innerHTML = `
            <div class="notification-popup-content">
                <div class="notification-popup-header">
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <span style="font-size: 18px;">${icon}</span>
                        <h4 class="notification-popup-title">${this.escapeHtml(notification.title)}</h4>
                    </div>
                        <div class="flex items-center gap-2">
                            <button class="notification-popup-close" onclick="app.closeNotificationPopup('${popupId}')" title="بستن این اعلان">
                                <i class="fa-solid fa-times"></i>
                            </button>
                            ${this.notificationPopups.length > 1 ? `<button class="notification-popup-close-all" onclick="app.closeAllNotificationPopups()" title="بستن همه اعلان‌ها">
                                <i class="fa-solid fa-times-circle"></i>
                            </button>` : ''}
                        </div>
                </div>
                <p class="notification-popup-message">${this.escapeHtml(notification.message || '')}</p>
                <div class="notification-popup-footer">
                    <span class="notification-popup-time">${timeAgo}</span>
                    <div class="notification-popup-actions">
                        <button class="notification-popup-action primary" onclick="app.handleNotificationClick('${notification.id}', '${notification.entity_type || ''}', '${notification.entity_id || ''}'); app.closeNotificationPopup('${popupId}')">
                            مشاهده
                        </button>
                        <button class="notification-popup-action secondary" onclick="app.closeNotificationPopup('${popupId}')">
                            رد
                        </button>
                    </div>
                </div>
            </div>
        `;

        // Add to page
        document.body.appendChild(popup);

        // Add to tracking array
        this.notificationPopups.push({
            id: popupId,
            element: popup,
            notification: notification
        });

        // Show with animation
        console.log('Adding popup to page:', popupId);
        setTimeout(() => {
            console.log('Adding show class to popup:', popupId);
            popup.classList.add('show');
        }, 100);

        // Remove auto-close timer - popups stay until user interaction

        // No limit on popup count - all notifications stay visible until user interaction
    },

    closeNotificationPopup(popupId) {
        const popup = document.getElementById(popupId);
        if (!popup) return;

        popup.classList.remove('show');
        popup.classList.add('hide');

        setTimeout(() => {
            if (popup.parentNode) {
                popup.parentNode.removeChild(popup);
            }
        }, 300);

        // Remove from tracking array
        this.notificationPopups = this.notificationPopups.filter(p => p.id !== popupId);
    },

    closeAllNotificationPopups() {
        const currentPopups = [...this.notificationPopups]; // Copy array to avoid modification during iteration
        currentPopups.forEach(popup => {
            this.closeNotificationPopup(popup.id);
        });
    },

    // Get count of active popups
    getActivePopupCount() {
        return this.notificationPopups.length;
    },

    showBrowserNotification(notification) {
        console.log('Attempting to show browser notification:', notification);

        // Check user settings
        if (!this.notificationBrowserEnabled) {
            console.log('Browser notifications disabled by user settings');
            return;
        }

        if (!('Notification' in window)) {
            console.log('This browser does not support notifications');
            return;
        }

        // Check permission and show notification
        if (Notification.permission === 'granted') {
            this._createBrowserNotification(notification);
        } else if (Notification.permission === 'default') {
            console.log('Requesting notification permission...');
            this.requestNotificationPermission().then((permission) => {
                if (permission === 'granted') {
                    console.log('Permission granted, creating notification');
                    this._createBrowserNotification(notification);
                } else {
                    console.log('Permission denied or failed');
                    this._showPermissionDeniedMessage();
                }
            });
        } else {
            console.log('Notifications are blocked by user');
            this._showPermissionDeniedMessage();
        }
    },

    _createBrowserNotification(notification) {
        try {
            const browserNotif = new Notification(notification.title, {
                body: notification.message,
                icon: '/public/logo.png',
                badge: '/public/logo.png',
                tag: 'saleos-' + notification.id,
                requireInteraction: true, // Keep notification until user interacts
                silent: false,
                dir: 'rtl',
                lang: 'fa',
                silent: false,
                timestamp: Date.now()
            });

            console.log('Browser notification created:', browserNotif);

            // No auto-close - notification stays until user interacts with it
            let autoCloseTimeout;

            // Handle click - focus window and navigate
            browserNotif.onclick = () => {
                console.log('Notification clicked');
                if (autoCloseTimeout) {
                    clearTimeout(autoCloseTimeout);
                }
                window.focus();

                // Try to bring window to front
                if (window.focus) {
                    window.focus();
                }

                // Navigate to relevant page if possible
                if (notification.entity_type && notification.entity_id) {
                    console.log('Should navigate to:', notification.entity_type, notification.entity_id);
                    // You can add navigation logic here
                    // For example: window.location.href = `/activities/${notification.entity_id}`;
                }

                browserNotif.close();
            };

            // Handle close
            browserNotif.onclose = () => {
                console.log('Notification closed');
                clearTimeout(autoCloseTimeout);
            };

            // Handle show
            browserNotif.onshow = () => {
                console.log('Notification shown successfully');
            };

            // Handle error
            browserNotif.onerror = (error) => {
                console.error('Notification error:', error);
            };

        } catch (error) {
            console.error('Failed to create browser notification:', error);
        }
    },

    _showPermissionDeniedMessage() {
        const browser = this._getBrowserName();
        const instructions = this._getBrowserInstructions(browser);

        Swal.fire({
            title: 'دسترسی نوتیفیکیشن مرورگر',
            text: 'برای دریافت نوتیفیکیشن‌های دسکتاپ، لطفاً دسترسی نوتیفیکیشن مرورگر را فعال کنید.',
            icon: 'warning',
            confirmButtonText: 'راهنمایی',
            showCancelButton: true,
            cancelButtonText: 'بعداً'
        }).then((result) => {
            if (result.isConfirmed) {
                Swal.fire({
                    title: `فعال کردن نوتیفیکیشن در ${browser}`,
                    html: instructions,
                    confirmButtonText: 'باشه',
                    width: 600
                });
            }
        });
    },

    _getBrowserName() {
        const userAgent = navigator.userAgent;
        if (userAgent.includes('Chrome') && !userAgent.includes('Edg')) {
            return 'Chrome';
        } else if (userAgent.includes('Firefox')) {
            return 'Firefox';
        } else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
            return 'Safari';
        } else if (userAgent.includes('Edg')) {
            return 'Edge';
        }
        return 'مرورگر';
    },

    _getBrowserInstructions(browser) {
        const instructions = {
            'Chrome': `
                <div class="text-right">
                    <p class="mb-2">در مرورگر Chrome:</p>
                    <ol class="text-sm text-gray-600 mb-3" style="direction: rtl; text-align: right;">
                        <li>1. روی آیکون قفل (🔒) در نوار آدرس کلیک کنید</li>
                        <li>2. گزینه "Site settings" را انتخاب کنید</li>
                        <li>3. بخش "Notifications" را پیدا کنید</li>
                        <li>4. روی "Allow" کلیک کنید</li>
                    </ol>
                    <p class="text-xs text-gray-500">یا در تنظیمات مرورگر: chrome://settings/content/notifications</p>
                </div>
            `,
            'Firefox': `
                <div class="text-right">
                    <p class="mb-2">در مرورگر Firefox:</p>
                    <ol class="text-sm text-gray-600 mb-3" style="direction: rtl; text-align: right;">
                        <li>1. روی آیکون قفل (🔒) در نوار آدرس کلیک کنید</li>
                        <li>2. گزینه "Allow notifications" را فعال کنید</li>
                    </ol>
                    <p class="text-xs text-gray-500">یا در تنظیمات: about:preferences#privacy</p>
                </div>
            `,
            'Edge': `
                <div class="text-right">
                    <p class="mb-2">در مرورگر Edge:</p>
                    <ol class="text-sm text-gray-600 mb-3" style="direction: rtl; text-align: right;">
                        <li>1. روی آیکون قفل (🔒) در نوار آدرس کلیک کنید</li>
                        <li>2. گزینه "Site permissions" را انتخاب کنید</li>
                        <li>3. بخش "Notifications" را پیدا کنید</li>
                        <li>4. روی "Allow" کلیک کنید</li>
                    </ol>
                </div>
            `,
            'Safari': `
                <div class="text-right">
                    <p class="mb-2">در مرورگر Safari:</p>
                    <ol class="text-sm text-gray-600 mb-3" style="direction: rtl; text-align: right;">
                        <li>1. Safari → Preferences را انتخاب کنید</li>
                        <li>2. تب Websites را انتخاب کنید</li>
                        <li>3. از منوی سمت چپ "Notifications" را انتخاب کنید</li>
                        <li>4. این سایت را پیدا کرده و روی "Allow" کلیک کنید</li>
                    </ol>
                </div>
            `
        };

        return instructions[browser] || `
            <div class="text-right">
                <p class="mb-2">در مرورگر شما:</p>
                <ol class="text-sm text-gray-600" style="direction: rtl; text-align: right;">
                    <li>1. روی آیکون قفل در نوار آدرس کلیک کنید</li>
                    <li>2. تنظیمات نوتیفیکیشن را پیدا کنید</li>
                    <li>3. دسترسی را فعال کنید</li>
                </ol>
            </div>
        `;
    },

    _updateNotificationStatus() {
        const statusEl = document.getElementById('browser-notif-status');
        if (!statusEl) return;

        let status = 'نامشخص';
        let color = 'text-gray-500';

        if (!('Notification' in window)) {
            status = 'مرورگر پشتیبانی نمی‌کند';
            color = 'text-red-500';
        } else {
            switch (Notification.permission) {
                case 'granted':
                    status = '✅ فعال';
                    color = 'text-green-500';
                    break;
                case 'denied':
                    status = '❌ بلاک شده';
                    color = 'text-red-500';
                    break;
                case 'default':
                    status = '⏳ نیاز به اجازه';
                    color = 'text-yellow-500';
                    break;
            }
        }

        statusEl.innerHTML = `<span class="${color} text-sm">${status}</span>`;
    },

    async requestNotificationPermission() {
        if (!('Notification' in window)) {
            console.log('Notifications not supported');
            return 'denied';
        }

        if (Notification.permission === 'granted') {
            return 'granted';
        }

        if (Notification.permission === 'denied') {
            return 'denied';
        }

            try {
                console.log('Requesting permission...');
                const permission = await Notification.requestPermission();
                console.log('Permission result:', permission);

                if (permission === 'granted') {
                    console.log('✅ Browser notification permission granted');
                    Swal.fire({
                        toast: true,
                        position: 'bottom-end',
                        icon: 'success',
                        title: 'دسترسی نوتیفیکیشن فعال شد',
                        timer: 2000,
                        showConfirmButton: false
                    });
                } else if (permission === 'denied') {
                    console.log('❌ Browser notification permission denied');
                    Swal.fire({
                        toast: true,
                        position: 'bottom-end',
                        icon: 'error',
                        title: 'دسترسی نوتیفیکیشن رد شد',
                        timer: 3000,
                        showConfirmButton: false
                    });
                } else {
                    console.log('⚠️ Browser notification permission default');
                }

                // Update status display if settings modal is open
                this._updateNotificationStatus();

                return permission;
            } catch (error) {
                console.error('❌ Error requesting notification permission:', error);
                return 'denied';
            }
    },

    // REMINDER CALCULATION FUNCTIONS
    calculateReminderOffset(form) {
        const hours = parseInt(form.reminder_offset_hours?.value || '24') || 0;
        const minutes = parseInt(form.reminder_offset_minutes?.value || '0') || 0;
        const totalMinutes = (hours * 60) + minutes;
        return totalMinutes > 0 ? totalMinutes : 1440; // Default to 24 hours if invalid
    },

    updateReminderOffsetTotal(formPrefix = '') {
        const hoursSelect = document.getElementById(`${formPrefix}activity-reminder-hours`);
        const minutesSelect = document.getElementById(`${formPrefix}activity-reminder-minutes`);
        const totalInput = document.getElementById(`${formPrefix}reminder-offset-total`);

        if (hoursSelect && minutesSelect && totalInput) {
            const hours = parseInt(hoursSelect.value) || 0;
            const minutes = parseInt(minutesSelect.value) || 0;
            const totalMinutes = (hours * 60) + minutes;
            totalInput.value = totalMinutes > 0 ? totalMinutes : 1440;
        }
    },

    initReminderOffsetListeners() {
        // Create activity reminder listeners
        const createHours = document.getElementById('activity-reminder-hours');
        const createMinutes = document.getElementById('activity-reminder-minutes');

        if (createHours) {
            createHours.addEventListener('change', () => this.updateReminderOffsetTotal(''));
        }
        if (createMinutes) {
            createMinutes.addEventListener('change', () => this.updateReminderOffsetTotal(''));
        }

        // Edit activity reminder listeners
        const editHours = document.getElementById('edit-activity-reminder-hours');
        const editMinutes = document.getElementById('edit-activity-reminder-minutes');

        if (editHours) {
            editHours.addEventListener('change', () => this.updateReminderOffsetTotal('edit-'));
        }
        if (editMinutes) {
            editMinutes.addEventListener('change', () => this.updateReminderOffsetTotal('edit-'));
        }
    },

    // ACTIVITY SCHEDULE FUNCTIONS
    toggleActivityScheduleFields() {
        const checkbox = document.getElementById('activity-is-scheduled');
        const fields = document.getElementById('activity-schedule-fields');
        const virtualStageSelect = document.getElementById('activity-virtual-stage');
        const resultNoteTextarea = document.getElementById('activity-result-note');
        const virtualStageRequired = document.getElementById('virtual-stage-required');
        
        if (!checkbox || !fields) return;
        
        if (checkbox.checked) {
            fields.classList.remove('hidden');
            // Initialize date picker
            this.initActivityDatePicker();
            
            // Make result_note optional for scheduled activities
            if (virtualStageSelect) {
                virtualStageSelect.removeAttribute('required');
            }
            if (resultNoteTextarea) {
                resultNoteTextarea.removeAttribute('required');
            }
            if (virtualStageRequired) {
                virtualStageRequired.classList.add('hidden');
            }
        } else {
            fields.classList.add('hidden');
            
            // Make result_note required again
            if (virtualStageSelect) {
                virtualStageSelect.setAttribute('required', 'required');
            }
            if (resultNoteTextarea) {
                resultNoteTextarea.setAttribute('required', 'required');
            }
            if (virtualStageRequired) {
                virtualStageRequired.classList.remove('hidden');
            }
        }
    },
    
    toggleActivityReminderFields() {
        const checkbox = document.getElementById('activity-reminder-enabled');
        const fields = document.getElementById('activity-reminder-fields');
        if (!checkbox || !fields) return;

        if (checkbox.checked) {
            fields.classList.remove('hidden');
            // Set default values if not already set
            const hoursSelect = document.getElementById('activity-reminder-hours');
            const minutesSelect = document.getElementById('activity-reminder-minutes');
            if (hoursSelect && hoursSelect.value === '') {
                hoursSelect.value = '24'; // 1 day default
            }
            if (minutesSelect && minutesSelect.value === '') {
                minutesSelect.value = '0';
            }
            // Update total
            this.updateReminderOffsetTotal('');
        } else {
            fields.classList.add('hidden');
        }
    },
    
    initActivityDatePicker() {
        const dateInput = document.getElementById('activity-due-date');
        const hourSelect = document.getElementById('activity-due-hour');
        const minuteSelect = document.getElementById('activity-due-minute');
        
        if (!dateInput) return;
        
        // Initialize hour and minute selects
        if (hourSelect) {
            hourSelect.innerHTML = '<option value="">ساعت</option>';
            for (let h = 0; h < 24; h++) {
                const hour = String(h).padStart(2, '0');
                const hourLabel = h < 12 ? `${hour} صبح` : `${hour} بعدازظهر`;
                hourSelect.innerHTML += `<option value="${hour}">${hourLabel}</option>`;
            }
            // Set default to current hour + 1 (next hour)
            const now = getTehranNow();
            const nextHour = (now.getHours() + 1) % 24;
            hourSelect.value = String(nextHour).padStart(2, '0');
        }
        
        if (minuteSelect) {
            minuteSelect.innerHTML = '<option value="">دقیقه</option>';
            for (let m = 0; m < 60; m += 5) {
                const minute = String(m).padStart(2, '0');
                minuteSelect.innerHTML += `<option value="${minute}">${minute}</option>`;
            }
            // Set default to 00
            minuteSelect.value = '00';
        }
        
        // Wait for jQuery and persianDatepicker
        if (typeof $ === 'undefined' || typeof $.fn.persianDatepicker === 'undefined') {
            setTimeout(() => this.initActivityDatePicker(), 200);
            return;
        }
        
        // Destroy existing picker
        try {
            if ($(dateInput).data('persianDatepicker')) {
                $(dateInput).persianDatepicker('destroy');
            }
        } catch(e) {
            console.log('No existing picker to destroy');
        }
        
        // Get today's date in Tehran timezone using moment-jalaali (accurate conversion)
        let todayFormatted = '';
        
        if (typeof moment !== 'undefined') {
            const testMoment = moment();
            if (typeof testMoment.jYear === 'function') {
                // Use moment-jalaali for accurate date conversion (fixes leap year bug)
                const now = new Date();
                const formatter = new Intl.DateTimeFormat('en-CA', {
                    timeZone: 'Asia/Tehran',
                    year: 'numeric',
                    month: '2-digit',
                    day: '2-digit'
                });
                const parts = formatter.formatToParts(now);
                const gYear = parseInt(parts.find(p => p.type === 'year').value);
                const gMonth = parseInt(parts.find(p => p.type === 'month').value);
                const gDay = parseInt(parts.find(p => p.type === 'day').value);
                
                const gregorianMoment = moment(`${gYear}-${String(gMonth).padStart(2, '0')}-${String(gDay).padStart(2, '0')}`, 'YYYY-MM-DD');
                todayFormatted = gregorianMoment.format('jYYYY/jMM/jDD').replace(/\//g, '/');
            } else {
                // Fallback to persianDate
                const todayTehran = getTehranPersianDate();
                if (todayTehran) {
                    todayFormatted = todayTehran.format('YYYY/MM/DD');
                }
            }
        }
        
        // Set default value to today if input is empty
        if (!dateInput.value || dateInput.value.trim() === '') {
            dateInput.value = todayFormatted;
        }
        
        // Initialize Persian date picker with better styling
        try {
            $(dateInput).persianDatepicker({
                observer: true,
                format: 'YYYY/MM/DD',
                altField: '#activity-due-date-gregorian',
                altFormat: 'YYYY-MM-DD',
                calendarType: 'persian',
                timePicker: {
                    enabled: false  // We'll use separate hour/minute selects
                },
                initialValue: dateInput.value || todayFormatted, // Use current value or today
                autoClose: true,
                toolbox: {
                    calendarSwitch: {
                        enabled: false
                    }
                },
                onlyTimePicker: false,
                onlySelectOnDate: true,
                calendar: {
                    persian: {
                        locale: 'fa',
                        dow: [0, 1, 2, 3, 4, 5, 6]  // Start week from Saturday (0=Saturday in persianDate)
                    }
                },
                onSelect: function(unixDate) {
                    // Update the input with formatted date
                    const pDate = new persianDate(unixDate);
                    dateInput.value = pDate.format('YYYY/MM/DD');
                }
            });
            this.syncActivityDatePickerToTehran();
            
            // Apply custom styles to the date picker
            setTimeout(() => {
                const pickerContainer = $('.pdp-container');
                if (pickerContainer.length) {
                    pickerContainer.css({
                        'background': 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)',
                        'border': '1px solid rgba(59, 130, 246, 0.3)',
                        'border-radius': '12px',
                        'box-shadow': '0 10px 40px rgba(0, 0, 0, 0.5)',
                        'padding': '15px',
                        'z-index': '10000'
                    });
                    
                    // Style calendar cells
                    pickerContainer.find('.pdp-day').css({
                        'color': '#e2e8f0',
                        'border-radius': '6px',
                        'transition': 'all 0.2s'
                    });
                    
                    pickerContainer.find('.pdp-day-selected').css({
                        'background': 'linear-gradient(135deg, #3b82f6, #8b5cf6)',
                        'color': '#fff',
                        'font-weight': 'bold',
                        'box-shadow': '0 2px 8px rgba(59, 130, 246, 0.4)'
                    });
                    
                    pickerContainer.find('.pdp-day-today').css({
                        'border': '2px solid #3b82f6',
                        'font-weight': 'bold'
                    });
                    
                    pickerContainer.find('.pdp-day:hover').css({
                        'background': 'rgba(59, 130, 246, 0.2)',
                        'transform': 'scale(1.05)'
                    });
                }
            }, 100);
            
        } catch(e) {
            console.error('Error initializing date picker:', e);
        }
    },

    syncActivityDatePickerToTehran() {
        const dateInput = document.getElementById('activity-due-date');
        if (!dateInput) return;
        if (typeof $ === 'undefined' || typeof $.fn.persianDatepicker === 'undefined') return;

        try {
            const tehranTime = getTehranNow().valueOf();
            $(dateInput).persianDatepicker('setDate', tehranTime);
        } catch (error) {
            console.error('Error syncing activity date picker to Tehran time:', error);
        }
    },
    
    // EDIT SCHEDULED ACTIVITY FUNCTIONS
    async openEditScheduledActivityModal(activityId) {
        try {
            // Get activity details from Didar API
            const res = await this.req('get_activity_details', { activity_id: activityId });
            if (res.status !== 'success' || !res.activity) {
                Swal.fire('خطا', 'فعالیت یافت نشد', 'error');
                return;
            }
            
            const activity = res.activity;
            
            // Fill form
            document.getElementById('edit-activity-id').value = activityId;
            document.getElementById('edit-activity-contact-id').value = activity.ContactIds?.[0] || activity.contact_didar_id || '';
            
            // Load activity types
            await this.loadActivityTypesForEditModal();
            
            // Set activity type
            const typeSelect = document.getElementById('edit-activity-type-id');
            if (typeSelect && activity.ActivityTypeId) {
                typeSelect.value = activity.ActivityTypeId;
            }
            
            // Set due date if exists
            if (activity.DueDate) {
                const dueDate = new Date(activity.DueDate);
                const pDate = new persianDate(dueDate);
                const dateStr = pDate.format('YYYY/MM/DD');
                const hour = String(dueDate.getHours()).padStart(2, '0');
                const minute = String(dueDate.getMinutes()).padStart(2, '0');
                
                document.getElementById('edit-activity-due-date').value = dateStr;
                document.getElementById('edit-activity-due-hour').value = hour;
                document.getElementById('edit-activity-due-minute').value = minute;
                
                // Initialize date picker (will update with current date)
                setTimeout(() => {
                    this.initEditActivityDatePicker();
                }, 100);
            } else {
                // If no due date, initialize with today's date
                setTimeout(() => {
                    this.initEditActivityDatePicker();
                }, 100);
            }
            
            // Set reminder
            if (activity.Notifies && activity.Notifies.length > 0) {
                document.getElementById('edit-activity-reminder-enabled').checked = true;
                this.toggleEditActivityReminderFields();
                const offset = activity.Notifies[0].NotifyOffset || 1440;

                // Convert minutes to hours and minutes
                const hours = Math.floor(offset / 60);
                const minutes = offset % 60;

                document.getElementById('edit-activity-reminder-hours').value = hours;
                document.getElementById('edit-activity-reminder-minutes').value = minutes;
                document.getElementById('edit-reminder-offset-total').value = offset;
            }
            
            this.openModal('modalEditScheduledActivity');
        } catch (error) {
            console.error('Error opening edit modal:', error);
            Swal.fire('خطا', 'خطا در بارگذاری اطلاعات فعالیت', 'error');
        }
    },
    
    async loadActivityTypesForEditModal() {
        try {
            const res = await this.req('get_activity_types', {});
            if (res.status === 'success' && res.types) {
                const select = document.getElementById('edit-activity-type-id');
                if (select) {
                    select.innerHTML = '<option value="">-- انتخاب نوع فعالیت --</option>';
                    res.types.forEach(type => {
                        const option = document.createElement('option');
                        option.value = type.Id || type.ActivityTypeId;
                        option.textContent = type.Title || 'فعالیت';
                        select.appendChild(option);
                    });
                }
            }
        } catch (error) {
            console.error('Error loading activity types:', error);
        }
    },
    
    toggleEditActivityReminderFields() {
        const checkbox = document.getElementById('edit-activity-reminder-enabled');
        const fields = document.getElementById('edit-activity-reminder-fields');
        if (!checkbox || !fields) return;

        if (checkbox.checked) {
            fields.classList.remove('hidden');
            // Set default values if not already set
            const hoursSelect = document.getElementById('edit-activity-reminder-hours');
            const minutesSelect = document.getElementById('edit-activity-reminder-minutes');
            if (hoursSelect && hoursSelect.value === '') {
                hoursSelect.value = '24'; // 1 day default
            }
            if (minutesSelect && minutesSelect.value === '') {
                minutesSelect.value = '0';
            }
            // Update total
            this.updateReminderOffsetTotal('edit-');
        } else {
            fields.classList.add('hidden');
        }
    },
    
    initEditActivityDatePicker() {
        const dateInput = document.getElementById('edit-activity-due-date');
        const hourSelect = document.getElementById('edit-activity-due-hour');
        const minuteSelect = document.getElementById('edit-activity-due-minute');
        
        if (!dateInput) return;
        
        // Initialize hour and minute selects
        if (hourSelect) {
            hourSelect.innerHTML = '<option value="">ساعت</option>';
            for (let h = 0; h < 24; h++) {
                const hour = String(h).padStart(2, '0');
                const hourLabel = h < 12 ? `${hour} صبح` : `${hour} بعدازظهر`;
                hourSelect.innerHTML += `<option value="${hour}">${hourLabel}</option>`;
            }
        }
        
        if (minuteSelect) {
            minuteSelect.innerHTML = '<option value="">دقیقه</option>';
            for (let m = 0; m < 60; m += 5) {
                const minute = String(m).padStart(2, '0');
                minuteSelect.innerHTML += `<option value="${minute}">${minute}</option>`;
            }
        }
        
        // Wait for jQuery and persianDatepicker
        if (typeof $ === 'undefined' || typeof $.fn.persianDatepicker === 'undefined') {
            setTimeout(() => this.initEditActivityDatePicker(), 200);
            return;
        }
        
        // Destroy existing picker
        try {
            if ($(dateInput).data('persianDatepicker')) {
                $(dateInput).persianDatepicker('destroy');
            }
        } catch(e) {
            console.log('No existing picker to destroy');
        }
        
        // Get today's date in Tehran timezone (as fallback if no value set)
        const todayTehran = getTehranPersianDate();
        const todayFormatted = todayTehran.format('YYYY/MM/DD');
        
        // Initialize Persian date picker
        try {
            $(dateInput).persianDatepicker({
                observer: true,
                format: 'YYYY/MM/DD',
                altField: '#edit-activity-due-date-gregorian',
                altFormat: 'YYYY-MM-DD',
                calendarType: 'persian',
                timePicker: {
                    enabled: false
                },
                initialValue: dateInput.value || todayFormatted, // Use current value or today
                autoClose: true,
                calendar: {
                    persian: {
                        locale: 'fa',
                        dow: [0, 1, 2, 3, 4, 5, 6]  // Start week from Saturday (0=Saturday in persianDate)
                    }
                },
                onSelect: function(unixDate) {
                    const pDate = new persianDate(unixDate);
                    dateInput.value = pDate.format('YYYY/MM/DD');
                }
            });
        } catch(e) {
            console.error('Error initializing date picker:', e);
        }
    },
    
    async updateScheduledActivity(e) {
        e.preventDefault();
        
        try {
            const form = e.target;
            const activityId = form.activity_id.value;
            const activityTypeId = form.activity_type_id.value;
            const dueDateInput = form.due_date.value;
            const hour = document.getElementById('edit-activity-due-hour').value;
            const minute = document.getElementById('edit-activity-due-minute').value;
            
            if (!activityId || !activityTypeId) {
                Swal.fire('خطا', 'نوع فعالیت الزامی است', 'error');
                return;
            }
            
            if (!dueDateInput || !hour || !minute) {
                Swal.fire('خطا', 'لطفاً تاریخ و زمان را کامل وارد کنید', 'error');
                return;
            }
            
            // Convert Persian date to Gregorian
            const persianToEnglish = (str) => {
                const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
                const englishDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
                let result = str;
                for (let i = 0; i < persianDigits.length; i++) {
                    result = result.replace(new RegExp(persianDigits[i], 'g'), englishDigits[i]);
                }
                return result;
            };
            
            const englishDateValue = persianToEnglish(dueDateInput.trim());
            const timeString = `${hour}:${minute}`;
            let dueDate = persianToGregorian(englishDateValue, timeString);
            
            if (!dueDate) {
                Swal.fire('خطا', 'تاریخ وارد شده معتبر نیست', 'error');
                return;
            }
            
            dueDate = dueDate.replace('T', ' ') + ':00';
            
            const data = {
                activity_id: activityId,
                activity_type_id: activityTypeId,
                due_date: dueDate,
                reminder_enabled: form.reminder_enabled.checked ? '1' : '0',
                reminder_offset_minutes: this.calculateReminderOffset(form)
            };
            
            Swal.showLoading();
            const res = await this.req('update_activity', data);
            Swal.close();
            
            if (res.status === 'success') {
                Swal.fire({toast:true, position:'bottom-end', icon:'success', title:'فعالیت با موفقیت به‌روزرسانی شد', showConfirmButton:false, timer:2000});
                this.closeModal('modalEditScheduledActivity');
                
                // Reload activities if we're viewing a person profile
                if (window.currentContactId) {
                    await this.openPersonProfile(window.currentContactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطای نامشخص', 'error');
            }
        } catch (error) {
            console.error('Error updating activity:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در به‌روزرسانی فعالیت: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    // OPEN ACTIVITY DETAILS MODAL
    async openActivityDetailsModal(activityId) {
        if (!activityId) {
            console.error('Activity ID is required');
            return;
        }
        
        try {
            Swal.fire({
                title: 'در حال بارگذاری...',
                allowOutsideClick: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
            
            const res = await this.req('get_activity_details', {activity_id: activityId});
            
            if (res.status === 'success' && res.activity) {
                const act = res.activity;
                const isDone = act.IsDone === true || act.IsDone === 1;
                const isNote = act.ActivityTypeId === '00000000-0000-0000-0000-000000000000';
                const activityType = act.ActivityType?.Title || act.ActivityTypeTitle || (isNote ? 'یادداشت' : 'فعالیت');
                const creator = act.Creator?.DisplayName || act.CreatorName || 'نامشخص';
                const owner = act.Owner?.DisplayName || act.OwnerName || 'نامشخص';
                const note = act.Note || '';
                const resultNote = act.ResultNote || '';
                const registerDate = act.RegisterDate ? toPersianDateTime(act.RegisterDate) : '';
                const doneDate = act.DoneDate ? toPersianDateTime(act.DoneDate) : '';
                
                // Check for due date
                let dueDate = '';
                if (act.DueDate) {
                    try {
                        dueDate = toPersianDateTime(act.DueDate);
                    } catch (e) {
                        dueDate = act.DueDate;
                    }
                } else if (act.due_date) {
                    try {
                        dueDate = toPersianDateTime(act.due_date);
                    } catch (e) {
                        dueDate = act.due_date;
                    }
                }
                
                const hasDueDate = dueDate && dueDate.trim() !== '';
                const isScheduledFlag = act.is_scheduled === 1 || act.is_scheduled === true || act.is_scheduled === '1';
                const isScheduled = isScheduledFlag || (!isDone && hasDueDate);
                
                let statusText = '';
                if (isDone) {
                    statusText = '<span class="text-green-300">✓ انجام شده</span>';
                } else if (isScheduled) {
                    statusText = '<span class="text-yellow-300">⏰ برنامه‌ریزی شده</span>';
                } else {
                    statusText = '<span class="text-blue-300">📝 ثبت شده</span>';
                }
                
                Swal.fire({
                    title: 'جزئیات فعالیت',
                    html: `
                        <div class="text-right space-y-3">
                            <div class="border-b border-gray-700 pb-2">
                                <div class="flex items-center justify-between mb-2">
                                    <span class="text-lg font-bold text-blue-300">
                                        <i class="fa-solid fa-${isNote ? 'sticky-note' : (isDone ? 'check-circle' : isScheduled ? 'clock' : 'calendar-check')} ml-1"></i>
                                        ${this.escapeHtml(activityType)}
                                    </span>
                                    ${statusText}
                                </div>
                                <div class="text-sm text-gray-400">${this.escapeHtml(act.Title || 'بدون عنوان')}</div>
                            </div>
                            
                            <div class="space-y-2 text-sm">
                                ${registerDate ? `<div><span class="text-gray-400">تاریخ ثبت:</span> <span class="text-gray-300">${registerDate}</span></div>` : ''}
                                ${dueDate && isScheduled ? `<div><span class="text-gray-400">تاریخ/ساعت برنامه‌ریزی:</span> <span class="text-yellow-300">${dueDate}</span></div>` : ''}
                                ${doneDate && isDone ? `<div><span class="text-gray-400">تاریخ انجام:</span> <span class="text-green-300">${doneDate}</span></div>` : ''}
                                <div><span class="text-gray-400">ایجاد کننده:</span> <span class="text-gray-300">${this.escapeHtml(creator)}</span></div>
                                ${owner !== creator ? `<div><span class="text-gray-400">مسئول:</span> <span class="text-gray-300">${this.escapeHtml(owner)}</span></div>` : ''}
                            </div>
                            
                            ${note ? `
                                <div class="border-t border-gray-700 pt-2">
                                    <div class="text-sm text-gray-400 mb-1">یادداشت:</div>
                                    <div class="text-sm text-gray-300 bg-gray-800/50 p-2 rounded whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(note))}</div>
                                </div>
                            ` : ''}
                            
                            ${resultNote ? `
                                <div class="border-t border-gray-700 pt-2">
                                    <div class="text-sm text-gray-400 mb-1">نتیجه:</div>
                                    <div class="text-sm text-gray-300 bg-gray-800/50 p-2 rounded whitespace-pre-wrap">${this.escapeHtml(this.stripHtml(resultNote))}</div>
                                </div>
                            ` : ''}
                            
                            ${!note && !resultNote ? `
                                <div class="text-sm text-gray-500 italic text-center py-2">بدون توضیحات</div>
                            ` : ''}
                        </div>
                    `,
                    width: '600px',
                    confirmButtonText: 'بستن',
                    customClass: {
                        popup: 'swal2-popup-dark',
                        htmlContainer: 'text-right'
                    }
                });
            } else {
                Swal.fire('خطا', res.message || 'فعالیت یافت نشد', 'error');
            }
        } catch (error) {
            console.error('Error loading activity details:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در دریافت جزئیات فعالیت: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    // MANUAL POLLING TEST (Admin only)
    async testPolling() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند polling را تست کند', 'error');
            return;
        }

        try {
            Swal.fire({
                title: 'در حال چک کردن یادآوری‌ها...',
                allowOutsideClick: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });

            const res = await this.req('check_reminders', {});

            Swal.close();

            if (res.status === 'success') {
                let message = 'سیستم polling چک شد\n';
                message += `🔔 نوتیفیکیشن‌های جدید: ${res.new_notifications ? res.new_notifications.length : 0}\n`;
                message += `🗯️ پاپ‌آپ نوتیفیکیشن‌ها: ${res.popup_notifications ? res.popup_notifications.length : 0}\n`;
                message += `📊 تعداد نوتیفیکیشن‌های خوانده نشده: ${res.unread_count || 0}`;

                Swal.fire({
                    icon: 'info',
                    title: 'نتیجه تست polling',
                    text: message,
                    confirmButtonText: 'باشه'
                });

                // Show any popup notifications found
                if (res.popup_notifications && res.popup_notifications.length > 0) {
                    res.popup_notifications.forEach(notif => {
                        this.showNotificationPopup(notif);
                    });
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در چک کردن یادآوری‌ها', 'error');
            }
        } catch (error) {
            console.error('Error testing polling:', error);
            Swal.fire('خطا', 'خطا در تست polling', 'error');
        }
    },

    // TEST POPUP DIRECTLY (Admin only)
    testPopupDirect() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند پاپ‌آپ را تست کند', 'error');
            return;
        }

        console.log('Testing popup directly');
        const testNotif = {
            id: 'test-' + Date.now(),
            title: 'تست کامل نوتیفیکیشن',
            message: 'این تست شامل پاپ‌آپ، صدا و نوتیفیکیشن مرورگر است',
            type: 'test',
            show_popup: 1,
            created_at: new Date().toISOString()
        };
        console.log('Test notification object:', testNotif);

        // Test all notification methods
        this.showNotificationPopup(testNotif);
        this.showBrowserNotification(testNotif);
        this.playNotificationSound();
    },

    // TEST BROWSER NOTIFICATION ONLY (Admin only)
    testBrowserNotification() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند نوتیفیکیشن مرورگر را تست کند', 'error');
            return;
        }

        console.log('Testing browser notification only');
        const testNotif = {
            id: 'browser-test-' + Date.now(),
            title: 'تست نوتیفیکیشن مرورگر',
            message: 'این نوتیفیکیشن باید روی دسکتاپ نمایش داده شود',
            type: 'browser_test',
            created_at: new Date().toISOString()
        };

        this.showBrowserNotification(testNotif);
    },

    // TEST NOTIFICATION (Admin only)
    async testNotification() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند اعلان را تست کند', 'error');
            return;
        }
        
        try {
            Swal.fire({
                title: 'در حال ایجاد اعلان تستی...',
                allowOutsideClick: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
            
            const res = await this.req('test_notification', {});
            
            if (res.status === 'success') {
                Swal.fire({
                    icon: 'success',
                    title: 'موفق',
                    text: 'اعلان تستی با موفقیت ایجاد شد. پاپ‌آپ را بررسی کنید.',
                    confirmButtonText: 'باشه'
                });

                // Refresh notifications and show popup
                await this.loadNotifications();

                // Show popup notification immediately for testing
                if (res.notification) {
                    const testNotif = {
                        id: res.notification.id,
                        title: res.notification.title,
                        message: res.notification.message,
                        type: res.notification.type,
                        entity_type: res.notification.entity_type,
                        entity_id: res.notification.entity_id,
                        created_at: res.notification.created_at,
                        show_popup: 1
                    };

                    // Show popup
                    this.showNotificationPopup(testNotif);

                    // Show browser notification
                    this.showBrowserNotification(testNotif);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در ایجاد اعلان تستی', 'error');
            }
        } catch (error) {
            console.error('Error testing notification:', error);
            Swal.fire('خطا', 'خطا در ایجاد اعلان تستی: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },
    
    // SYNC DIDAR USERS (Admin only)
    async syncDidarUsers() {
        if (!this.isDidarUiEnabled()) {
            this.showDidarUiDisabledMessage();
            return;
        }
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر سیستم می‌تواند کاربران را همگام‌سازی کند', 'error');
            return;
        }
        
        try {
            const result = await Swal.fire({
                title: 'همگام‌سازی کاربران',
                text: 'آیا می‌خواهید کاربران از Didar همگام‌سازی کنید؟ این عملیات باعث بروزرسانی شناسه‌های کاربران می‌شود.',
                icon: 'question',
                showCancelButton: true,
                confirmButtonText: 'بله، همگام‌سازی کن',
                cancelButtonText: 'انصراف'
            });
            
            if (!result.isConfirmed) return;
            
            Swal.fire({
                title: 'در حال همگام‌سازی...',
                allowOutsideClick: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
            
            const res = await this.req('sync_didar_users', {});
            
            if (res.status === 'success') {
                Swal.fire({
                    icon: 'success',
                    title: 'موفق',
                    html: `
                        <div class="text-right">
                            <p>کاربران با موفقیت همگام‌سازی شدند</p>
                            <div class="mt-3 space-y-1 text-sm">
                                <p>جمع کل: <strong>${res.total_didar_users}</strong></p>
                                <p>به‌روزرسانی شده: <strong class="text-green-600">${res.updated}</strong></p>
                                <p>یافت نشده: <strong class="text-yellow-600">${res.not_found}</strong></p>
                            </div>
                        </div>
                    `,
                    confirmButtonText: 'باشه'
                });
            } else {
                Swal.fire('خطا', res.message || 'خطا در همگام‌سازی کاربران', 'error');
            }
        } catch (error) {
            console.error('Error syncing Didar users:', error);
            Swal.fire('خطا', 'خطا در همگام‌سازی کاربران: ' + (error.message || 'خطای نامشخص'), 'error');
        }
    },

    // IMPORT LEADS FUNCTIONS
    importLeadsState: {
        file: null,
        preview: null,
        columnMapping: null
    },

    openImportLeadsModal() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند لیدها را وارد کند', 'error');
            return;
        }
        this.openModal('importLeadsModal');
        document.getElementById('import-step-1').classList.remove('hidden');
        document.getElementById('import-step-2').classList.add('hidden');
        document.getElementById('import-step-3').classList.add('hidden');
        document.getElementById('import-btn-preview').classList.add('hidden');
        document.getElementById('import-btn-execute').classList.add('hidden');
        document.getElementById('import-btn-done').classList.add('hidden');
        document.getElementById('import-file-selected').classList.add('hidden');
        document.getElementById('import-file-input').value = '';
        this.importLeadsState = { file: null, preview: null, columnMapping: null };
    },

    downloadImportLeadsTemplate() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'فقط مدیر می‌تواند فایل نمونه را دانلود کند', 'error');
            return;
        }
        window.open('?api=download_leads_import_template', '_blank');
    },

    handleImportFileSelect(event) {
        const file = event.target.files?.[0];
        if (!file) return;

        const ext = (file.name.split('.').pop() || '').toLowerCase();
        if (!['xlsx', 'xls', 'csv'].includes(ext)) {
            Swal.fire('خطا', 'فقط فایل‌های Excel یا CSV قابل قبول هستند', 'error');
            return;
        }

        if (file.size > 5 * 1024 * 1024) {
            Swal.fire('خطا', 'حجم فایل نباید بیشتر از 5 مگابایت باشد', 'error');
            return;
        }

        this.importLeadsState.file = file;
        document.getElementById('import-file-name').textContent = file.name;
        document.getElementById('import-file-selected').classList.remove('hidden');
        document.getElementById('import-btn-preview').classList.remove('hidden');

        Swal.fire({
            toast: true,
            position: 'bottom-end',
            icon: 'success',
            title: 'فایل انتخاب شد',
            timer: 2000,
            showConfirmButton: false
        });
    },

    async previewImportLeads() {
        if (!this.importLeadsState.file) {
            Swal.fire('خطا', 'فایلی انتخاب نشده است', 'error');
            return;
        }

        const formData = new FormData();
        formData.append('file', this.importLeadsState.file);

        Swal.fire({
            title: 'در حال بررسی فایل...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await fetch('?api=import_leads_preview', {
                method: 'POST',
                body: formData
            });

            let data;
            try {
                data = await res.json();
            } catch (_) {
                const raw = await res.text();
                throw new Error(raw || 'پاسخ نامعتبر از سرور دریافت شد');
            }
            Swal.close();

            if (data.status !== 'success') {
                Swal.fire('خطا', data.message || 'خطای نامشخص', 'error');
                return;
            }

            this.importLeadsState.preview = data;
            this.renderImportPreview(data);

            document.getElementById('import-step-1').classList.add('hidden');
            document.getElementById('import-step-2').classList.remove('hidden');
            document.getElementById('import-btn-preview').classList.add('hidden');
            document.getElementById('import-btn-execute').classList.remove('hidden');

        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در بررسی فایل: ' + error.message, 'error');
        }
    },

    renderImportPreview(data) {
        document.getElementById('import-preview-success').textContent = data.valid_count || 0;
        document.getElementById('import-preview-error').textContent = data.error_count || 0;

        const tbody = document.getElementById('import-preview-table');
        tbody.innerHTML = (data.preview || []).map((row) => {
            const hasError = row.errors && row.errors.length > 0;
            return `
                <tr class="${hasError ? 'bg-red-900/20' : 'bg-green-900/20'}">
                    <td class="border border-gray-600 p-2">${row.row_number}</td>
                    <td class="border border-gray-600 p-2">${this.escapeHtml(row.mobile_phone || '')}</td>
                    <td class="border border-gray-600 p-2 text-xs">${this.escapeHtml((row.owner_username || '').substring(0, 24))}</td>
                    <td class="border border-gray-600 p-2 text-xs">
                        ${hasError ? `<span class="text-red-400">${this.escapeHtml(row.errors[0])}</span>` : '<span class="text-green-400">✓</span>'}
                    </td>
                </tr>
            `;
        }).join('');
    },

    async executeImportLeads() {
        if (!this.importLeadsState.file) {
            Swal.fire('خطا', 'فایلی انتخاب نشده است', 'error');
            return;
        }

        const result = await Swal.fire({
            title: 'آیا مطمئن هستید؟',
            text: `${this.importLeadsState.preview?.valid_count || 0} لید وارد خواهد شد`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'بله، ادامه بده',
            cancelButtonText: 'لغو'
        });

        if (!result.isConfirmed) return;

        const formData = new FormData();
        formData.append('file', this.importLeadsState.file);

        Swal.fire({
            title: 'در حال وارد کردن لیدها...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });

        try {
            const res = await fetch('?api=import_leads_execute', {
                method: 'POST',
                body: formData
            });

            let data;
            try {
                data = await res.json();
            } catch (_) {
                const raw = await res.text();
                throw new Error(raw || 'پاسخ نامعتبر از سرور دریافت شد');
            }
            Swal.close();

            if (data.status !== 'success') {
                Swal.fire('خطا', data.message || 'خطای نامشخص', 'error');
                return;
            }

            this.renderImportResults(data);

            document.getElementById('import-step-2').classList.add('hidden');
            document.getElementById('import-step-3').classList.remove('hidden');
            document.getElementById('import-btn-execute').classList.add('hidden');
            document.getElementById('import-btn-done').classList.remove('hidden');

        } catch (error) {
            Swal.close();
            Swal.fire('خطا', 'خطا در وارد کردن لیدها: ' + error.message, 'error');
        }
    },

    renderImportResults(data) {
        document.getElementById('import-result-success').textContent = data.success_count || 0;
        document.getElementById('import-result-error').textContent = data.failure_count || 0;

        const resultsDiv = document.getElementById('import-result-table');
        resultsDiv.innerHTML = (data.results || []).map(r => {
            const isSuccess = r.status === 'success';
            const actionLabelMap = {
                lead_created: 'ایجاد لید',
                deal_created: 'ایجاد معامله',
                skip: 'رد شد'
            };
            const actionLabel = actionLabelMap[r.action] || (r.action || '-');
            return `
                <div class="p-2 rounded ${isSuccess ? 'bg-green-900/20 border-l-2 border-green-600' : 'bg-red-900/20 border-l-2 border-red-600'}">
                    <p class="text-xs font-mono text-gray-300">ردیف ${r.row} | موبایل: ${this.escapeHtml(r.mobile_phone || '')} | مسئول: ${this.escapeHtml(r.owner_username || '-')}</p>
                    <p class="text-[11px] text-gray-400">${this.escapeHtml(actionLabel)}</p>
                    <p class="text-xs ${isSuccess ? 'text-green-300' : 'text-red-300'}">${this.escapeHtml(r.message)}</p>
                </div>
            `;
        }).join('');
    },

    // FIELD CONFIGURATION MANAGER (Admin)
    switchLookupTab(tab) {
        // Hide all tabs
        document.querySelectorAll('.lookup-tab-content').forEach(el => el.classList.add('hidden'));
        document.querySelectorAll('.lookup-tab').forEach(el => el.classList.remove('active', 'border-blue-500'));

        // Show selected tab
        const tabEl = document.getElementById(`lookup-${tab}-tab`);
        if (tabEl) {
            tabEl.classList.remove('hidden');
        }

        // Mark button as active
        const btn = document.querySelector(`.lookup-tab[data-tab="${tab}"]`);
        if (btn) {
            btn.classList.add('active', 'border-blue-500');
            btn.classList.remove('border-transparent');
        }

        // Load data if needed
        if (tab === 'fields') {
            this.loadFieldConfigurations();
        }
    },

    async loadFieldConfigurations(forceRefresh = false) {
        try {
            const res = await this.req('get_field_configurations');
            if (res.status === 'success') {
                this.state.fieldConfigurations = res.configurations || [];
                this.renderFieldConfigurationList();
                this.populateFieldConfigSelect();
            }
        } catch (error) {
            console.error('Error loading field configurations:', error);
            Swal.fire('خطا', 'خطا در بارگذاری تنظیمات فیلدها', 'error');
        }
    },

    renderFieldConfigurationList() {
        const container = document.getElementById('field-config-list');
        if (!container) return;

        container.innerHTML = (this.state.fieldConfigurations || []).map(config => `
            <div class="bg-gray-800/30 p-3 rounded border border-gray-700/50 hover:border-gray-600 cursor-pointer transition" onclick="app.editFieldConfiguration('${config.field_name}')">
                <div class="flex justify-between items-start">
                    <div class="flex-1">
                        <h4 class="font-medium text-white text-sm">${this.escapeHtml(config.field_label)}</h4>
                        <p class="text-xs text-gray-400 mt-1">
                            <span class="bg-gray-700/50 px-2 py-1 rounded">${config.field_name}</span>
                            <span class="ml-2 text-gray-500">نوع: ${config.field_type}</span>
                        </p>
                        <p class="text-xs text-gray-500 mt-1">
                            ${config.is_required ? '✓ الزامی' : '○ اختیاری'}
                            ${config.is_editable ? ' | ✓ قابل ویرایش' : ' | ✗ فقط خواندنی'}
                            ${config.is_active ? ' | ✓ فعال' : ' | ✗ غیرفعال'}
                        </p>
                    </div>
                    <button onclick="event.stopPropagation(); app.deleteFieldConfiguration('${config.field_name}')" class="text-red-400 hover:text-red-300 text-xs p-2">
                        <i class="fa-solid fa-trash"></i>
                    </button>
                </div>
            </div>
        `).join('');
    },

    populateFieldConfigSelect() {
        const select = document.getElementById('field-config-select');
        if (!select) return;

        select.innerHTML = '<option value="">-- انتخاب فیلد --</option>' +
            (this.state.fieldConfigurations || []).map(config =>
                `<option value="${config.field_name}">${this.escapeHtml(config.field_label)}</option>`
            ).join('');
    },

    onFieldConfigSelect() {
        const fieldName = document.getElementById('field-config-select')?.value;
        if (!fieldName) {
            document.getElementById('field-config-editor').classList.add('hidden');
            return;
        }

        this.editFieldConfiguration(fieldName);
    },

    editFieldConfiguration(fieldName) {
        const config = (this.state.fieldConfigurations || []).find(c => c.field_name === fieldName);
        if (!config) return;

        // Populate form
        document.getElementById('fc-field-name').value = config.field_name;
        document.getElementById('fc-field-label').value = config.field_label || '';
        document.getElementById('fc-field-type').value = config.field_type || 'text';
        document.getElementById('fc-entity-type').value = config.entity_type || 'deal';
        document.getElementById('fc-lookup-group').value = config.lookup_group_code || '';
        document.getElementById('fc-sort-order').value = config.sort_order || 0;
        document.getElementById('fc-is-required').checked = config.is_required === 1;
        document.getElementById('fc-is-editable').checked = config.is_editable === 1;
        document.getElementById('fc-is-active').checked = config.is_active === 1;

        // Set roles
        const roles = config.edit_allow_roles || [];
        document.querySelectorAll('#fc-edit-allow-roles option').forEach(opt => {
            opt.selected = roles.includes(opt.value);
        });

        // Show editor
        document.getElementById('field-config-editor').classList.remove('hidden');
        document.getElementById('field-config-select').value = fieldName;
    },

    resetFieldConfigEditor() {
        document.getElementById('field-config-editor').classList.add('hidden');
        document.getElementById('field-config-select').value = '';
        document.getElementById('fc-field-name').value = '';
        document.getElementById('fc-field-label').value = '';
        document.getElementById('fc-is-required').checked = false;
        document.getElementById('fc-is-editable').checked = true;
        document.getElementById('fc-is-active').checked = true;
    },

    async saveFieldConfiguration() {
        const fieldName = document.getElementById('fc-field-name').value;
        if (!fieldName) {
            Swal.fire('خطا', 'نام فیلد الزامی است', 'error');
            return;
        }

        // Get selected roles
        const selectedRoles = Array.from(
            document.querySelectorAll('#fc-edit-allow-roles option:checked')
        ).map(opt => opt.value);

        const data = {
            field_name: fieldName,
            field_label: document.getElementById('fc-field-label').value,
            field_type: document.getElementById('fc-field-type').value,
            entity_type: document.getElementById('fc-entity-type').value,
            is_required: document.getElementById('fc-is-required').checked ? 1 : 0,
            is_editable: document.getElementById('fc-is-editable').checked ? 1 : 0,
            is_active: document.getElementById('fc-is-active').checked ? 1 : 0,
            edit_allow_roles: JSON.stringify(selectedRoles),
            lookup_group_code: document.getElementById('fc-lookup-group').value || null,
            sort_order: parseInt(document.getElementById('fc-sort-order').value) || 0
        };

        try {
            const res = await this.req('save_field_configuration', data);
            if (res.status === 'success') {
                Swal.fire('موفق', 'تنظیمات فیلد ذخیره شد', 'success');
                this.loadFieldConfigurations(true);
                this.resetFieldConfigEditor();
            } else {
                Swal.fire('خطا', res.message || 'خطا در ذخیره‌سازی', 'error');
            }
        } catch (error) {
            console.error('Error saving field configuration:', error);
            Swal.fire('خطا', 'خطا در ذخیره‌سازی تنظیمات: ' + error.message, 'error');
        }
    },

    async deleteFieldConfiguration(fieldName) {
        const confirm = await Swal.fire({
            title: 'حذف تنظیمات فیلد',
            text: `آیا مطمئن هستید که می‌خواهید ${fieldName} را حذف کنید؟`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'بله، حذف کن',
            cancelButtonText: 'خیر'
        });

        if (!confirm.isConfirmed) return;

        try {
            const res = await this.req('delete_field_configuration', { field_name: fieldName });
            if (res.status === 'success') {
                Swal.fire('موفق', 'تنظیمات فیلد حذف شد', 'success');
                this.loadFieldConfigurations(true);
                this.resetFieldConfigEditor();
            } else {
                Swal.fire('خطا', res.message || 'خطا در حذف', 'error');
            }
        } catch (error) {
            console.error('Error deleting field configuration:', error);
            Swal.fire('خطا', 'خطا در حذف: ' + error.message, 'error');
        }
    },

    // ========== PERFORMANCE DASHBOARD ==========
    getPerformanceAllowedRange() {
        const start = new Date();
        start.setDate(1);
        start.setMonth(start.getMonth() - 1);
        const end = new Date();
        end.setMonth(end.getMonth() + 1, 0);
        return {
            date_from: start.toISOString().slice(0, 10),
            date_to: end.toISOString().slice(0, 10)
        };
    },

    getCurrentMonthRangeGregorian() {
        const d = new Date();
        const from = new Date(d.getFullYear(), d.getMonth(), 1);
        const to = new Date(d.getFullYear(), d.getMonth() + 1, 0);
        return { date_from: from.toISOString().slice(0, 10), date_to: to.toISOString().slice(0, 10) };
    },

    getPreviousMonthRangeGregorian() {
        const d = new Date();
        const from = new Date(d.getFullYear(), d.getMonth() - 1, 1);
        const to = new Date(d.getFullYear(), d.getMonth(), 0);
        return { date_from: from.toISOString().slice(0, 10), date_to: to.toISOString().slice(0, 10) };
    },

    isDateWithinRange(dateStr, minDateStr, maxDateStr) {
        if (!dateStr) return false;
        const d = new Date(dateStr + 'T00:00:00');
        const min = new Date(minDateStr + 'T00:00:00');
        const max = new Date(maxDateStr + 'T23:59:59');
        return d >= min && d <= max;
    },

    async setPerformanceQuickRange(rangeType) {
        let range;
        if (rangeType === 'today') {
            const today = new Date().toISOString().slice(0, 10);
            range = { date_from: today, date_to: today };
        } else if (rangeType === 'yesterday') {
            const y = new Date();
            y.setDate(y.getDate() - 1);
            const day = y.toISOString().slice(0, 10);
            range = { date_from: day, date_to: day };
        } else if (rangeType === 'prev_month') {
            range = this.getPreviousMonthRangeGregorian();
        } else {
            range = this.getCurrentMonthRangeGregorian();
        }

        this.state.performanceFilters = {
            ...(this.state.performanceFilters || {}),
            date_from: range.date_from,
            date_to: range.date_to
        };

        await this.loadPerformanceDashboard({ useState: true });
    },

    initPerformanceDatePickers() {
        const self = this;
        setTimeout(function () {
            if (typeof $ === 'undefined' || typeof $.fn?.persianDatepicker !== 'function') return;
            const $from = $('#performance-date-from');
            const $to = $('#performance-date-to');
            const filters = self.state.performanceFilters || {};
            const initOnce = ($input, desiredValue) => {
                if (!$input || $input.length === 0) return;
                const inputEl = $input.get(0);
                if (!inputEl) return;
                if ($input.data('perfDatepickerInit') !== 1) {
                    try { $input.persianDatepicker('destroy'); } catch (e) {}
                    $input.persianDatepicker({
                        format: 'YYYY/MM/DD',
                        initialValue: false,
                        autoClose: true,
                        observer: false
                    });
                    $input.data('perfDatepickerInit', 1);
                }
                if (document.activeElement === inputEl) return;
                const normalizedDesired = desiredValue ? (toPersianDate(desiredValue) || desiredValue) : '';
                if (normalizedDesired && String($input.val() || '') !== String(normalizedDesired)) {
                    $input.val(normalizedDesired);
                }
            };
            initOnce($from, filters.date_from || '');
            initOnce($to, filters.date_to || '');
        }, 150);
    },

    getPerformanceOwnerData() {
        const options = [{ value: '__all__', label: 'همه مسئول‌ها' }];
        if (this.state.kpiUsers && this.state.kpiUsers.length > 0) {
            this.state.kpiUsers
                .filter(user => Number(user.is_active) === 1)
                .forEach(user => {
                    const value = user.didar_user_id || user.owner_id || '';
                    if (!value) return;
                    options.push({ value: String(value), label: user.display_name || user.email || 'کاربر' });
                });
        }
        return options;
    },

    getPerformanceProductData() {
        const options = [{ value: '__all__', label: 'همه محصولات' }];
        (this.state.kpiProducts || []).forEach((product) => {
            if (!product?.id) return;
            options.push({ value: String(product.id), label: product.name || 'محصول' });
        });
        return options;
    },

    getDefaultKpiCriteria() {
        return {
            kpi_sales_date_basis: 'ui_payment_date',
            kpi_sales_count_rule: 'first_payment_only',
            kpi_payment_date_basis: 'ui_payment_date',
            kpi_payment_count_rule: 'all_payments',
            kpi_payment_status_scope: 'confirmed_only',
            legacy_deal_scope: 'exclude'
        };
    },

    normalizeKpiCriteria(criteria = {}) {
        const defaults = this.getDefaultKpiCriteria();
        return {
            ...defaults,
            ...(criteria || {})
        };
    },

    getKpiCriteriaSelectOptions(key) {
        const map = {
            kpi_sales_date_basis: [
                { value: 'ui_payment_date', label: 'فروش: تاریخ واریزی UI' },
                { value: 'db_payment_date', label: 'فروش: تاریخ پرداخت DB' },
                { value: 'system_created_at', label: 'فروش: تاریخ ثبت سیستمی' }
            ],
            kpi_sales_count_rule: [
                { value: 'first_payment_only', label: 'فروش: فقط اولین واریزی' },
                { value: 'all_payments', label: 'فروش: همه پرداخت‌ها' }
            ],
            kpi_payment_date_basis: [
                { value: 'ui_payment_date', label: 'مالی: تاریخ واریزی UI' },
                { value: 'db_payment_date', label: 'مالی: تاریخ پرداخت DB' },
                { value: 'system_created_at', label: 'مالی: تاریخ ثبت سیستمی' }
            ],
            kpi_payment_count_rule: [
                { value: 'all_payments', label: 'مالی: همه پرداخت‌ها' },
                { value: 'first_payment_only', label: 'مالی: فقط اولین واریزی' }
            ],
            kpi_payment_status_scope: [
                { value: 'confirmed_only', label: 'وضعیت پرداخت: فقط تاییدشده' },
                { value: 'all_statuses', label: 'وضعیت پرداخت: همه وضعیت‌ها' }
            ],
            legacy_deal_scope: [
                { value: 'exclude', label: 'معاملات legacy: حذف شود' },
                { value: 'all', label: 'معاملات legacy: همه' },
                { value: 'only', label: 'معاملات legacy: فقط legacy' }
            ]
        };
        return map[key] || [];
    },

    renderKpiCriteriaSelect(key, value, compact = false) {
        const options = this.getKpiCriteriaSelectOptions(key);
        const labelMap = {
            kpi_sales_date_basis: 'مبنای تاریخ فروش',
            kpi_sales_count_rule: 'قاعده احتساب فروش',
            kpi_payment_date_basis: 'مبنای تاریخ مالی',
            kpi_payment_count_rule: 'قاعده احتساب مالی',
            kpi_payment_status_scope: 'وضعیت پرداخت',
            legacy_deal_scope: 'فیلتر legacy deal'
        };
        const id = `${key}${compact ? '-perf' : ''}`;
        return `
            <div>
                <label class="block text-xs text-gray-400 mb-1">${labelMap[key] || key}</label>
                <select id="${id}" class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-white text-xs">
                    ${options.map(opt => `<option value="${opt.value}" ${String(value) === String(opt.value) ? 'selected' : ''}>${this.escapeHtml(opt.label)}</option>`).join('')}
                </select>
            </div>
        `;
    },

    collectKpiCriteriaFromDom(compact = false) {
        const defaults = this.getDefaultKpiCriteria();
        const suffix = compact ? '-perf' : '';
        const out = { ...defaults };
        Object.keys(defaults).forEach((key) => {
            const element = document.getElementById(`${key}${suffix}`);
            if (element && element.value) {
                out[key] = element.value;
            }
        });
        return out;
    },

    appendCriteriaToKpiPayload(payload, mode = 'kpi') {
        const sourceFilters = mode === 'performance'
            ? (this.state.performanceFilters || {})
            : (this.state.kpiFilters || {});
        const criteria = this.normalizeKpiCriteria(sourceFilters.criteria || {});
        payload.kpi_sales_date_basis = criteria.kpi_sales_date_basis;
        payload.kpi_sales_count_rule = criteria.kpi_sales_count_rule;
        payload.kpi_payment_date_basis = criteria.kpi_payment_date_basis;
        payload.kpi_payment_count_rule = criteria.kpi_payment_count_rule;
        payload.kpi_payment_status_scope = criteria.kpi_payment_status_scope;
        payload.legacy_deal_scope = criteria.legacy_deal_scope;
        return payload;
    },

    renderDrilldownCriteriaControls() {
        if (!this.isAdminLikeForAnalytics()) return '';
        const ctx = this.state.kpiDrilldownContext || null;
        if (!ctx) return '';
        const mode = ctx.mode === 'performance' ? 'performance' : 'kpi';
        const filters = mode === 'performance' ? (this.state.performanceFilters || {}) : (this.state.kpiFilters || {});
        const criteria = this.normalizeKpiCriteria(filters.criteria || {});
        return `
            <div class="bg-gray-900/60 border border-gray-700 rounded p-3 mb-4">
                <div class="text-xs text-gray-300 font-bold mb-2">فیلترهای مبنای تاریخ برای Drill-down</div>
                <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-6 gap-2">
                    ${this.renderKpiCriteriaSelect('kpi_sales_date_basis', criteria.kpi_sales_date_basis, true).replace('kpi_sales_date_basis-perf', 'kpi_sales_date_basis-drill')}
                    ${this.renderKpiCriteriaSelect('kpi_sales_count_rule', criteria.kpi_sales_count_rule, true).replace('kpi_sales_count_rule-perf', 'kpi_sales_count_rule-drill')}
                    ${this.renderKpiCriteriaSelect('kpi_payment_date_basis', criteria.kpi_payment_date_basis, true).replace('kpi_payment_date_basis-perf', 'kpi_payment_date_basis-drill')}
                    ${this.renderKpiCriteriaSelect('kpi_payment_count_rule', criteria.kpi_payment_count_rule, true).replace('kpi_payment_count_rule-perf', 'kpi_payment_count_rule-drill')}
                    ${this.renderKpiCriteriaSelect('kpi_payment_status_scope', criteria.kpi_payment_status_scope, true).replace('kpi_payment_status_scope-perf', 'kpi_payment_status_scope-drill')}
                    ${this.renderKpiCriteriaSelect('legacy_deal_scope', criteria.legacy_deal_scope, true).replace('legacy_deal_scope-perf', 'legacy_deal_scope-drill')}
                </div>
                <div class="mt-3 flex justify-end">
                    <button type="button" class="px-3 py-1.5 text-xs rounded bg-blue-600 hover:bg-blue-500 text-white" onclick="app.applyDrilldownCriteriaFilters()">اعمال فیلتر Drill-down</button>
                </div>
            </div>
        `;
    },

    collectKpiCriteriaFromDrilldownDom() {
        const defaults = this.getDefaultKpiCriteria();
        const out = { ...defaults };
        Object.keys(defaults).forEach((key) => {
            const element = document.getElementById(`${key}-drill`);
            if (element && element.value) out[key] = element.value;
        });
        return out;
    },

    async applyDrilldownCriteriaFilters() {
        const ctx = this.state.kpiDrilldownContext;
        if (!ctx) return;
        const criteria = this.collectKpiCriteriaFromDrilldownDom();
        if (ctx.mode === 'performance') {
            this.state.performanceFilters = this.state.performanceFilters || {};
            this.state.performanceFilters.criteria = criteria;
        } else {
            this.state.kpiFilters = this.state.kpiFilters || {};
            this.state.kpiFilters.criteria = criteria;
        }
        await this.reloadCurrentKpiDrilldown();
    },

    async reloadCurrentKpiDrilldown() {
        const ctx = this.state.kpiDrilldownContext;
        if (!ctx) return;
        if (ctx.kind === 'performance_chart') {
            return this.openPerformanceChartDrilldown(ctx.tableId, ctx.metricKey, ctx.rowContext || {});
        }
        if (ctx.kind === 'performance_table') {
            return this.openPerformanceDrilldown(ctx.tableId, ctx.metricKey, ctx.sectionKey, ctx.rowIndex);
        }
        if (ctx.kind === 'kpi_advanced') {
            return this.openKpiAdvancedDrilldown(ctx.tableId, ctx.metricKey, ctx.rowIndex);
        }
    },

    async loadPerformanceDashboard(options = {}) {
        if (!this.canPerformanceFeatureClient('performance.view')) {
            Swal.fire('خطا', 'دسترسی به داشبورد عملکرد ندارید', 'error');
            return;
        }
        const content = document.getElementById('performance-dashboard-content');
        if (!content) return;

        const useState = options.useState === true;
        await this.loadKpiFilterData(); // re-use existing loaders for users/products

        const nowRange = this.getCurrentMonthRangeGregorian();
        let dateFrom = this.state.performanceFilters?.date_from || nowRange.date_from;
        let dateTo = this.state.performanceFilters?.date_to || nowRange.date_to;
        let ownerIds = Array.isArray(this.state.performanceFilters?.owner_didar_ids) ? [...this.state.performanceFilters.owner_didar_ids] : [];
        let productIds = Array.isArray(this.state.performanceFilters?.product_ids) ? [...this.state.performanceFilters.product_ids] : [];
        let criteria = this.normalizeKpiCriteria(this.state.performanceFilters?.criteria || {});

        if (!useState) {
            const dateFromJ = (document.getElementById('performance-date-from')?.value || '').trim();
            const dateToJ = (document.getElementById('performance-date-to')?.value || '').trim();

            if (dateFromJ) {
                const g = persianToGregorian(this.toEnglishDigits(dateFromJ), '00:00');
                if (g) dateFrom = String(g).split(' ')[0];
                else if (/^\d{4}-\d{2}-\d{2}$/.test(dateFromJ)) dateFrom = dateFromJ;
            }
            if (dateToJ) {
                const g = persianToGregorian(this.toEnglishDigits(dateToJ), '23:59');
                if (g) dateTo = String(g).split(' ')[0];
                else if (/^\d{4}-\d{2}-\d{2}$/.test(dateToJ)) dateTo = dateToJ;
            }

            ownerIds = this.getCustomMultiSelectValues('performance-owner-filter');
            productIds = this.getCustomMultiSelectValues('performance-product-filter');
            criteria = this.collectKpiCriteriaFromDom(true);
        }

        if (!this.isAdminLikeForAnalytics()) {
            ownerIds = []; // server forces owner for agent-like users
        }

        this.state.performanceFilters = {
            date_from: dateFrom,
            date_to: dateTo,
            owner_didar_ids: ownerIds,
            product_ids: productIds,
            criteria
        };

        content.innerHTML = '<div class="text-sm text-gray-400">در حال بارگذاری داشبورد عملکرد...</div>';
        const perfLoadRequestId = (Number(this.state.performanceLoadRequestId || 0) + 1);
        this.state.performanceLoadRequestId = perfLoadRequestId;

        const payload = {
            date_from: dateFrom,
            date_to: dateTo,
            owner_didar_ids: ownerIds.join(','),
            product_ids: productIds.join(',')
        };
        this.appendCriteriaToKpiPayload(payload, 'performance');
        this.state.performanceSplitPayload = { ...payload };
        this.state.performanceTabsLoaded = false;
        const sectionReq = async (section) => this.req('get_performance_dashboard_section', { ...payload, section });
        let usedSplit = false;
        try {
            const summaryRes = await sectionReq('summary');
            if (summaryRes.status !== 'success') throw new Error(summaryRes.message || 'summary failed');
            if (this.state.performanceLoadRequestId !== perfLoadRequestId) return;
            usedSplit = true;
            if (summaryRes.filters && summaryRes.filters.criteria) {
                this.state.performanceFilters = {
                    ...(this.state.performanceFilters || {}),
                    criteria: this.normalizeKpiCriteria(summaryRes.filters.criteria)
                };
            }
            this.state.performanceDashboard = this.mergePerformanceDashboardSection({}, summaryRes.data || {}, 'summary');
            this.renderPerformanceDashboard(this.state.performanceDashboard);

            const chartsRes = await sectionReq('charts');
            if (chartsRes.status === 'success') {
                if (this.state.performanceLoadRequestId !== perfLoadRequestId) return;
                this.state.performanceDashboard = this.mergePerformanceDashboardSection(this.state.performanceDashboard || {}, chartsRes.data || {}, 'charts');
                this.renderPerformanceDashboard(this.state.performanceDashboard);
            }

            setTimeout(() => {
                this.loadPerformanceDashboardTabsSection({ requestId: perfLoadRequestId });
            }, 0);
        } catch (e) {
            console.warn('Performance split load failed, fallback to full endpoint', e);
        }

        if (!usedSplit) {
            const res = await this.req('get_performance_dashboard', payload);
            if (res.status !== 'success') {
                Swal.fire('خطا', res.message || 'خطا در بارگذاری داشبورد عملکرد', 'error');
                return;
            }
            if (res.filters && res.filters.criteria) {
                this.state.performanceFilters = {
                    ...(this.state.performanceFilters || {}),
                    criteria: this.normalizeKpiCriteria(res.filters.criteria)
                };
            }
            this.state.performanceDashboard = res.dashboard || null;
            this.state.performanceTabsLoaded = true;
            this.renderPerformanceDashboard(res.dashboard || {});
        }
    },

    async loadPerformanceDashboardTabsSection(options = {}) {
        const requestId = Number(options.requestId || this.state.performanceLoadRequestId || 0);
        if (!this.state.performanceSplitPayload) return;
        if (this.state.performanceTabsLoaded) return;
        if (this.state.performanceTabsLoading) return;
        this.state.performanceTabsLoading = true;
        try {
            const res = await this.req('get_performance_dashboard_section', { ...this.state.performanceSplitPayload, section: 'tabs' });
            if (res.status === 'success' && this.state.performanceLoadRequestId === requestId) {
                this.state.performanceDashboard = this.mergePerformanceDashboardSection(this.state.performanceDashboard || {}, res.data || {}, 'tabs');
                this.state.performanceTabsLoaded = true;
                this.renderPerformanceDashboard(this.state.performanceDashboard || {});
            }
        } catch (e) {
            console.warn('Performance tabs lazy load failed', e);
        } finally {
            this.state.performanceTabsLoading = false;
        }
    },

    mergePerformanceDashboardSection(current, sectionData, section) {
        const out = current ? JSON.parse(JSON.stringify(current)) : {};
        if (section === 'summary') {
            out.summary_cards = sectionData.summary_cards || {};
            out.comparison = sectionData.comparison || {};
            out.charts = out.charts || {};
            out.tabs = out.tabs || {};
            out.raw = out.raw || {};
        } else if (section === 'charts') {
            out.charts = { ...(out.charts || {}), ...(sectionData.charts || {}) };
        } else if (section === 'tabs') {
            out.tabs = sectionData.tabs || {};
            out.raw = { ...(out.raw || {}), ...(sectionData.raw || {}) };
        }
        return out;
    },

    renderPerformanceDashboard(data) {
        const content = document.getElementById('performance-dashboard-content');
        if (!content) return;
        const filters = this.state.performanceFilters || {};
        const canChoosePerformanceOwners = this.hasCrossUserScopeClient('performance');
        const criteria = this.normalizeKpiCriteria(filters.criteria || {});
        const summary = data.summary_cards || {};
        const tabs = data.tabs || {};
        const activityPerformance = summary.activity_performance || {};
        const lostReasonBreakdown = Array.isArray(summary.lost_reason_breakdown) ? summary.lost_reason_breakdown : [];

        const dateFromJ = filters.date_from ? (toPersianDate(filters.date_from) || filters.date_from) : '';
        const dateToJ = filters.date_to ? (toPersianDate(filters.date_to) || filters.date_to) : '';

        const formatToman = (amount) => `${this.formatNumber(Number(amount || 0))} تومان`;

        const tabButtons = [
            { key: 'activity', label: 'فعالیت' },
            { key: 'sales_deals', label: 'فروش و معاملات' },
            { key: 'payments', label: 'پرداخت' },
            { key: 'products', label: 'محصولات' },
            { key: 'failure_reasons', label: 'دلایل شکست' }
        ].filter(t => {
            const sectionMap = {
                activity: ['performance.table.activity_summary', 'performance.table.activity_type_details'],
                sales_deals: ['performance.table.deal_status', 'performance.table.lead_status', 'performance.table.conversion_status'],
                payments: ['performance.table.payment_mix', 'performance.table.payment_status'],
                products: ['performance.table.product_status', 'performance.table.product_sold_by_owner_day', 'performance.table.product_requests_by_owner_day', 'performance.table.product_failed_by_owner_day'],
                failure_reasons: ['performance.table.failure_reasons']
            };
            const keys = sectionMap[t.key] || [];
            if (!keys.length) return true;
            return keys.some(k => this.canPerformanceFeatureClient(k));
        }).map(t => {
            const isActive = this.state.performanceActiveTab === t.key;
            return `<button class="px-3 py-2 rounded text-xs border ${isActive ? 'bg-blue-600 border-blue-500 text-white' : 'bg-gray-800 border-gray-700 text-gray-300'}" onclick="app.switchPerformanceTab('${t.key}')">${t.label}</button>`;
        }).join('');
        const perfCanDailySales = this.canPerformanceFeatureClient('performance.chart.daily_sales');
        const perfCanDailyActivities = this.canPerformanceFeatureClient('performance.chart.daily_activities');
        const perfCanDealStatus = this.canPerformanceFeatureClient('performance.chart.deal_status');
        const perfCanProductShare = this.canPerformanceFeatureClient('performance.chart.product_share');
        const perfCanFunnel = this.canPerformanceFeatureClient('performance.chart.conversion_funnel');

        const lostReasonHtml = lostReasonBreakdown.length
            ? lostReasonBreakdown.slice(0, 3).map(item => {
                const reason = this.escapeHtml(item.reason || 'نامشخص');
                const count = this.formatNumber(Number(item.count || 0));
                return `<div class="text-[10px] text-gray-500">${reason}: ${count}</div>`;
            }).join('')
            : '';

        content.innerHTML = `
            <div class="glass p-4 md:p-6 mb-6 border border-gray-700/70 relative overflow-visible" style="z-index: 1500;">
                <div class="text-sm text-gray-300 font-bold mb-3">فیلترهای داشبورد عملکرد</div>
                <div class="grid grid-cols-1 md:grid-cols-4 gap-3">
                    <div>
                        <label class="block text-xs text-gray-400 mb-1">از تاریخ</label>
                        <input type="text" id="performance-date-from" class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-white" value="${dateFromJ}">
                    </div>
                    <div>
                        <label class="block text-xs text-gray-400 mb-1">تا تاریخ</label>
                        <input type="text" id="performance-date-to" class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-white" value="${dateToJ}">
                    </div>
                    ${canChoosePerformanceOwners ? `<div>${this.renderCustomMultiSelect('performance-owner-filter', 'مسئول (چندانتخابی)', this.getPerformanceOwnerData(), filters.owner_didar_ids || [])}</div>` : '<div></div>'}
                    <div>${this.renderCustomMultiSelect('performance-product-filter', 'محصول (چندانتخابی)', this.getPerformanceProductData(), filters.product_ids || [])}</div>
                </div>
                ${this.isAdminLikeForAnalytics() ? `
                <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-6 gap-3 mt-3">
                    ${this.renderKpiCriteriaSelect('kpi_sales_date_basis', criteria.kpi_sales_date_basis, true)}
                    ${this.renderKpiCriteriaSelect('kpi_sales_count_rule', criteria.kpi_sales_count_rule, true)}
                    ${this.renderKpiCriteriaSelect('kpi_payment_date_basis', criteria.kpi_payment_date_basis, true)}
                    ${this.renderKpiCriteriaSelect('kpi_payment_count_rule', criteria.kpi_payment_count_rule, true)}
                    ${this.renderKpiCriteriaSelect('kpi_payment_status_scope', criteria.kpi_payment_status_scope, true)}
                    ${this.renderKpiCriteriaSelect('legacy_deal_scope', criteria.legacy_deal_scope, true)}
                </div>` : ''}
                <div class="flex flex-wrap gap-2 mt-4">
                    <button onclick="app.setPerformanceQuickRange('today')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">امروز</button>
                    <button onclick="app.setPerformanceQuickRange('yesterday')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">دیروز</button>
                    <button onclick="app.setPerformanceQuickRange('month')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">ماه جاری</button>
                    <button onclick="app.setPerformanceQuickRange('prev_month')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">ماه قبل</button>
                    <button onclick="app.loadPerformanceDashboard()" class="text-xs px-4 py-1.5 rounded bg-blue-600 hover:bg-blue-500 text-white">اعمال فیلتر</button>
                </div>
            </div>

            <div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3 mb-6">
                ${this.renderPerformanceSummaryCard('فروش کل', formatToman(summary.total_sales_amount), `تعداد معامله: ${this.formatNumber(summary.with_sales_deals_count || 0)}`, 'مبلغ قرارداد: ' + formatToman(summary.total_contract_amount || 0), 'border-blue-500', 'performance_cards', 'sales_total')}
                ${this.renderPerformanceSummaryCard('درصد تحقق تارگت', `${this.formatNumber(summary.target_achievement_percent || 0)}%`, summary.target_has_target ? `هدف: ${formatToman(summary.target_amount || 0)}` : 'هدف تعریف نشده', `هدف روزانه: ${formatToman(summary.target_daily_amount || 0)}`, 'border-yellow-500')}
                ${this.renderPerformanceSummaryCard('مانده حساب', formatToman(summary.outstanding_amount), `تعداد بدهکار: ${this.formatNumber(summary.outstanding_count || 0)}`, `قرارداد: ${formatToman(summary.outstanding_contract_amount || 0)}`, 'border-red-500', 'performance_cards', 'outstanding_amount_sum')}
                ${this.renderPerformanceSummaryCard('تسویه‌شده کامل', this.formatNumber(summary.settled_count || 0), `مبلغ کل: ${formatToman(summary.settled_amount || 0)}`, `وصول شده: ${formatToman(summary.settled_collected_amount || 0)}`, 'border-green-500', 'performance_cards', 'settled_count')}
                ${this.renderPerformanceSummaryCard('فروش ناموفق', this.formatNumber(summary.lost_count || 0), `مبلغ کل: ${formatToman(summary.lost_amount || 0)}`, lostReasonHtml, 'border-orange-500', 'performance_cards', 'lost_count')}
                ${this.renderPerformanceSummaryCard('فروش موفق (Won)', this.formatNumber(summary.won_count || 0), `مبلغ کل: ${formatToman(summary.won_amount || 0)}`, '', 'border-emerald-500', 'performance_cards', 'won_count')}
                ${this.renderPerformanceSummaryCard('فروش جاری', this.formatNumber(summary.open_pipeline_count || 0), `پتانسیل: ${formatToman(summary.open_pipeline_amount || 0)}`, '', 'border-purple-500', 'performance_cards', 'open_pipeline_count')}
                ${this.renderPerformanceSummaryCard('نرخ تبدیل معاملات', `${this.formatNumber(summary.deal_conversion_rate || 0)}%`, `از ${this.formatNumber(summary.deal_conversion_total_deals || 0)} معامله`, `عدم تبدیل: ${this.formatNumber(summary.deal_non_conversion_rate || 0)}%`, 'border-pink-500', 'performance_cards', 'deal_conversion_rate')}
                ${this.renderPerformanceSummaryCard('نرخ تبدیل لید جدید', `${this.formatNumber(summary.lead_conversion_rate || 0)}%`, `از ${this.formatNumber(summary.lead_total_new || 0)} لید`, `عدم تبدیل: ${this.formatNumber(summary.lead_non_conversion_rate || 0)}%`, 'border-cyan-500', 'performance_cards', 'lead_conversion_rate')}
                ${this.renderPerformanceSummaryCard('معاملات بدون پرداخت', this.formatNumber(summary.without_payment_count || 0), `مجموع مبلغ: ${formatToman(summary.without_payment_amount || 0)}`, '', 'border-amber-500', 'performance_cards', 'without_payment_count')}
                ${this.renderPerformanceSummaryCard('جزئیات تبدیل لید جدید', this.formatNumber(summary.lead_converted || 0), `لید جدید: ${this.formatNumber(summary.lead_total_new || 0)}`, `عدم تبدیل: ${this.formatNumber(summary.lead_non_conversion_rate || 0)}%`, 'border-indigo-500', 'performance_cards', 'lead_conversion_rate')}
                ${this.renderPerformanceSummaryCard('میانگین زمان تبدیل', `${this.formatNumber(Number(summary.avg_conversion_days || 0).toFixed(2))} روز`, 'از ثبت معامله تا اولین پرداخت', '', 'border-emerald-500')}
                ${this.renderPerformanceComparisonCard(data.comparison || {})}
            </div>

            ${this.renderPerformanceActivityPerformanceSection(activityPerformance)}

            <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-4 relative" style="z-index: 1;">
                ${perfCanDailySales ? `<div class="glass p-4">
                    <div class="flex items-center justify-between mb-3">
                        <div class="text-sm text-gray-300">روند فروش روزانه (مبلغ + تعداد معامله)</div>
                        ${this.renderPerformanceChartActions('dailySales', 'daily_sales')}
                    </div>
                    <div id="perf-chart-daily-sales" class="h-[360px]"></div>
                    ${this.renderPerformanceChartControls('sales', (data.charts?.daily_sales || []).length)}
                </div>` : ''}
                ${perfCanDailyActivities ? `<div class="glass p-4">
                    <div class="flex items-center justify-between mb-3">
                        <div class="text-sm text-gray-300">روند فعالیت روزانه (تجمیعی + تفکیک نوع)</div>
                        ${this.renderPerformanceChartActions('dailyActivities', 'daily_activities')}
                    </div>
                    <div id="perf-chart-daily-activities" class="h-[440px]"></div>
                    ${this.renderPerformanceChartControls('activity', (data.charts?.daily_activities || []).length)}
                </div>` : ''}
            </div>

            <div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6 relative" style="z-index: 1;">
                ${perfCanDealStatus ? `<div class="glass p-4">
                    <div class="flex items-center justify-between mb-3">
                        <div class="text-sm text-gray-300">ترکیب وضعیت معاملات</div>
                        ${this.renderPerformanceChartActions('dealStatus', 'deal_status_breakdown')}
                    </div>
                    <div id="perf-chart-deal-status" class="h-72"></div>
                </div>` : ''}
                ${perfCanProductShare ? `<div class="glass p-4">
                    <div class="flex items-center justify-between mb-3">
                        <div class="text-sm text-gray-300">سهم فروش محصولات</div>
                        ${this.renderPerformanceChartActions('productShare', 'product_sales_share')}
                    </div>
                    <div id="perf-chart-product-share" class="h-72"></div>
                </div>` : ''}
                ${perfCanFunnel ? `<div class="glass p-4">
                    <div class="flex items-center justify-between mb-3">
                        <div class="text-sm text-gray-300">قیف تبدیل</div>
                        ${this.renderPerformanceChartActions('conversionFunnel', 'conversion_funnel')}
                    </div>
                    <div id="perf-chart-conversion-funnel" class="h-72"></div>
                </div>` : ''}
            </div>

            <div class="glass p-4">
                <div class="flex flex-wrap gap-2 mb-4">${tabButtons}</div>
                <div id="performance-tab-content">${this.renderPerformanceTabContent(tabs, this.state.performanceActiveTab)}</div>
            </div>
        `;

        this.initPerformanceDatePickers();
        this.initCustomMultiSelectListeners();
        this.renderPerformanceCharts(data.charts || {});
    },

    switchPerformanceTab(tabKey) {
        this.state.performanceActiveTab = tabKey;
        const tabs = this.state.performanceDashboard?.tabs || {};
        const container = document.getElementById('performance-tab-content');
        if (container) {
            container.innerHTML = this.renderPerformanceTabContent(tabs, tabKey);
        }
        if (!this.state.performanceTabsLoaded) {
            this.loadPerformanceDashboardTabsSection();
        }
    },

    renderPerformanceSummaryCard(title, value, sub1, sub2, borderClass = 'border-gray-500', drillTableId = '', drillMetricKey = '') {
        if (drillMetricKey && !this.canPerformanceFeatureClient(`performance.card.${drillMetricKey}`)) {
            return '';
        }
        const isClickable = drillTableId !== '' && drillMetricKey !== '';
        const clickHandler = isClickable
            ? `onclick="app.openPerformanceChartDrilldown('${drillTableId}','${drillMetricKey}', {})"`
            : '';
        return `
            <div class="glass p-3 border-r-4 ${borderClass} ${isClickable ? 'cursor-pointer hover:border-cyan-400 transition-colors' : ''}" ${clickHandler} title="${isClickable ? 'نمایش جزئیات' : ''}">
                <div class="text-[11px] text-gray-400 mb-1">${title}</div>
                <div class="text-lg md:text-xl font-bold text-white leading-7 mb-1">${value}</div>
                <div class="text-[11px] text-gray-400">${sub1 || ''}</div>
                <div class="text-[11px] text-gray-500">${sub2 || ''}</div>
                ${isClickable ? '<div class="text-[10px] text-cyan-300 mt-2">برای مشاهده جزئیات کلیک کنید</div>' : ''}
            </div>
        `;
    },

    renderPerformanceComparisonCard(comparison) {
        const label = this.escapeHtml(comparison?.label || 'مقایسه با بازه قبل');
        const salesCmp = comparison?.summary?.total_sales_amount || {};
        const sign = Number(salesCmp.delta || 0) >= 0 ? '+' : '';
        const deltaPct = Number(salesCmp.delta_percent || 0).toFixed(2);
        const color = Number(salesCmp.delta || 0) >= 0 ? 'text-green-400' : 'text-red-400';
        return `
            <div class="glass p-3 border-r-4 border-sky-500">
                <div class="text-[11px] text-gray-400 mb-1">${label}</div>
                <div class="text-xs text-gray-400">فروش بازه قبل: ${this.formatNumber(salesCmp.previous || 0)} تومان</div>
                <div class="text-sm font-bold ${color} mt-1">تغییر: ${sign}${this.formatNumber(salesCmp.delta || 0)} تومان</div>
                <div class="text-[11px] ${color}">(${sign}${deltaPct}%)</div>
            </div>
        `;
    },

    renderPerformanceChartActions(chartKey, sourceKey) {
        return `
            <div class="flex items-center gap-1">
                <button class="px-2 py-1 text-[11px] rounded bg-gray-700 hover:bg-gray-600" onclick="app.exportPerformanceChart('${chartKey}','png')">PNG</button>
                <button class="px-2 py-1 text-[11px] rounded bg-gray-700 hover:bg-gray-600" onclick="app.exportPerformanceChart('${chartKey}','svg')">SVG</button>
                <button class="px-2 py-1 text-[11px] rounded bg-gray-700 hover:bg-gray-600" onclick="app.exportPerformanceChart('${chartKey}','pdf')">PDF</button>
                <button class="px-2 py-1 text-[11px] rounded bg-blue-700 hover:bg-blue-600" onclick="app.exportPerformanceChartCsv('${sourceKey}')">CSV</button>
            </div>
        `;
    },

    renderPerformanceChartControls(type, total) {
        const isSales = type === 'sales';
        const sizeKey = isSales ? 'salesSize' : 'activitySize';
        const offsetKey = isSales ? 'salesOffset' : 'activityOffset';
        const state = this.state.performanceChartWindow || {};
        const size = Math.max(7, Number(state[sizeKey] || 14));
        const maxOffset = Math.max(0, Number(total || 0) - size);
        const offset = Math.min(maxOffset, Math.max(0, Number(state[offsetKey] || 0)));

        if (!total || total <= 7) return '';

        return `
            <div class="mt-3 grid grid-cols-1 gap-2 text-xs text-gray-300 bg-gray-900/40 border border-gray-700 rounded-lg p-2">
                <div class="flex items-center flex-wrap gap-2">
                    <span class="text-gray-400">بازه نمایش:</span>
                    <button type="button" class="px-2 py-1 rounded ${size === 7 ? 'bg-blue-600 text-white' : 'bg-gray-700 hover:bg-gray-600'}" onclick="app.setPerformanceChartWindow('${type}', 7)">۷ روز</button>
                    <button type="button" class="px-2 py-1 rounded ${size === 14 ? 'bg-blue-600 text-white' : 'bg-gray-700 hover:bg-gray-600'}" onclick="app.setPerformanceChartWindow('${type}', 14)">۱۴ روز</button>
                    <button type="button" class="px-2 py-1 rounded ${size === 30 ? 'bg-blue-600 text-white' : 'bg-gray-700 hover:bg-gray-600'}" onclick="app.setPerformanceChartWindow('${type}', 30)">۳۰ روز</button>
                    <button type="button" class="px-2 py-1 rounded ${size >= total ? 'bg-blue-600 text-white' : 'bg-gray-700 hover:bg-gray-600'}" onclick="app.setPerformanceChartWindow('${type}', ${Math.max(total, 7)})">همه</button>
                </div>
                <div class="flex items-center justify-between gap-2">
                    <div class="flex items-center gap-2">
                        <button type="button" class="px-2 py-1 rounded bg-gray-700 hover:bg-gray-600" onclick="app.shiftPerformanceChartWindow('${type}', -${Math.max(1, Math.floor(size / 2))})">قبلی</button>
                        <button type="button" class="px-2 py-1 rounded bg-gray-700 hover:bg-gray-600" onclick="app.shiftPerformanceChartWindow('${type}', ${Math.max(1, Math.floor(size / 2))})">بعدی</button>
                        <button type="button" class="px-2 py-1 rounded bg-gray-700 hover:bg-gray-600" onclick="app.jumpPerformanceChartToLatest('${type}')">آخرین بازه</button>
                    </div>
                    <span class="text-gray-400">نقطه ${this.formatNumber(offset + 1)} تا ${this.formatNumber(Math.min(offset + size, total))} از ${this.formatNumber(total)}</span>
                </div>
            </div>
        `;
    },

    togglePerformanceChartDrag(type) {},

    setPerformanceChartWindow(type, size) {
        const isSales = type === 'sales';
        const sizeKey = isSales ? 'salesSize' : 'activitySize';
        const offsetKey = isSales ? 'salesOffset' : 'activityOffset';
        if (!this.state.performanceChartWindow) {
            this.state.performanceChartWindow = { salesSize: 14, salesOffset: 0, activitySize: 14, activityOffset: 0 };
        }
        const num = Math.max(7, Number(size || 14));
        this.state.performanceChartWindow[sizeKey] = num;
        const total = type === 'sales'
            ? ((this.state.performanceDashboard?.charts?.daily_sales || []).length)
            : ((this.state.performanceDashboard?.charts?.daily_activities || []).length);
        const currentSize = Math.max(7, Number(this.state.performanceChartWindow[sizeKey] || 14));
        const maxOffset = Math.max(0, Number(total || 0) - currentSize);
        if (Number(this.state.performanceChartWindow[offsetKey] || 0) > maxOffset) {
            this.state.performanceChartWindow[offsetKey] = maxOffset;
        }
        this.renderPerformanceDashboard(this.state.performanceDashboard || {});
    },

    shiftPerformanceChartWindow(type, delta) {
        const isSales = type === 'sales';
        const offsetKey = isSales ? 'salesOffset' : 'activityOffset';
        const sizeKey = isSales ? 'salesSize' : 'activitySize';
        if (!this.state.performanceChartWindow) return;
        const current = Number(this.state.performanceChartWindow[offsetKey] || 0);
        const size = Math.max(7, Number(this.state.performanceChartWindow[sizeKey] || 14));
        const total = type === 'sales'
            ? ((this.state.performanceDashboard?.charts?.daily_sales || []).length)
            : ((this.state.performanceDashboard?.charts?.daily_activities || []).length);
        const maxOffset = Math.max(0, Number(total || 0) - size);
        this.state.performanceChartWindow[offsetKey] = Math.max(0, Math.min(maxOffset, current + Number(delta || 0)));
        this.renderPerformanceDashboard(this.state.performanceDashboard || {});
    },

    jumpPerformanceChartToLatest(type) {
        const isSales = type === 'sales';
        const offsetKey = isSales ? 'salesOffset' : 'activityOffset';
        const sizeKey = isSales ? 'salesSize' : 'activitySize';
        const total = type === 'sales'
            ? ((this.state.performanceDashboard?.charts?.daily_sales || []).length)
            : ((this.state.performanceDashboard?.charts?.daily_activities || []).length);
        const size = Math.max(7, Number(this.state.performanceChartWindow?.[sizeKey] || 14));
        const maxOffset = Math.max(0, Number(total || 0) - size);
        this.state.performanceChartWindow[offsetKey] = maxOffset;
        this.renderPerformanceDashboard(this.state.performanceDashboard || {});
    },

    async openPerformanceChartDrilldown(tableId, metricKey, rowContext = {}) {
        if (!this.canPerformanceFeatureClient('performance.drilldown')) {
            Swal.fire('خطا', 'دسترسی Drilldown داشبورد عملکرد ندارید', 'error');
            return;
        }
        this.state.kpiDrilldownContext = {
            mode: 'performance',
            kind: 'performance_chart',
            tableId,
            metricKey,
            rowContext: { ...(rowContext || {}) }
        };
        const subtitle = document.getElementById('kpi-drilldown-subtitle');
        if (subtitle) {
            subtitle.textContent = `نمودار | ${tableId} | ${metricKey}`;
        }
        const container = document.getElementById('kpi-drilldown-content');
        if (container) {
            container.innerHTML = '<div class="text-sm text-gray-400">در حال بارگذاری داده‌های مرتبط...</div>';
        }
        this.openModal('modalKpiDrilldown');

        const filters = this.state.performanceFilters || {};
        const payload = {
            table_id: tableId,
            metric_key: metricKey,
            date_from: filters.date_from || '',
            date_to: filters.date_to || '',
            owner_didar_id: '',
            product_id: '',
            owner_didar_ids: Array.isArray(filters.owner_didar_ids) ? filters.owner_didar_ids.join(',') : '',
            product_ids: Array.isArray(filters.product_ids) ? filters.product_ids.join(',') : '',
            deal_statuses: '',
            deal_lead_types: '',
            row_date: rowContext.date || '',
            row_owner_didar_id: rowContext.owner_didar_id || '',
            row_owner_name: rowContext.owner_name || '',
            row_product_name: rowContext.product_name || '',
            row_failure_reason: rowContext.failure_reason || '',
            row_activity_type: rowContext.activity_type || '',
            row_stage: rowContext.stage || ''
        };

        this.appendCriteriaToKpiPayload(payload, 'performance');
        const res = await this.req('get_kpi_drilldown', payload);
        if (res.status !== 'success') {
            if (container) container.innerHTML = `<div class="text-sm text-red-400">${this.escapeHtml(res.message || 'خطا در دریافت جزئیات')}</div>`;
            return;
        }
        this.state.kpiDrilldown = {
            tableId,
            metricKey,
            row: rowContext || null,
            data: res,
            loading: false
        };
        if (container) container.innerHTML = this.renderKpiDrilldownContent(res);
    },

    async openConversionCardDrilldown(source, metricKey) {
        if (source === 'performance') {
            return this.openPerformanceChartDrilldown('performance_cards', metricKey, {});
        }
        if (!this.canKpiFeatureClient('kpi.view')) {
            Swal.fire('خطا', 'دسترسی داشبورد KPI ندارید', 'error');
            return;
        }
        this.state.kpiDrilldownContext = {
            mode: 'kpi',
            kind: 'conversion_card',
            tableId: 'performance_cards',
            metricKey,
            rowContext: {}
        };
        const subtitle = document.getElementById('kpi-drilldown-subtitle');
        if (subtitle) {
            subtitle.textContent = `کارت نرخ تبدیل | performance_cards | ${metricKey}`;
        }
        const container = document.getElementById('kpi-drilldown-content');
        if (container) {
            container.innerHTML = '<div class="text-sm text-gray-400">در حال بارگذاری داده‌های مرتبط...</div>';
        }
        this.openModal('modalKpiDrilldown');
        const filters = this.state.kpiFilters || {};
        const payload = {
            table_id: 'performance_cards',
            metric_key: metricKey,
            date_from: filters.date_from || '',
            date_to: filters.date_to || '',
            owner_didar_id: '',
            product_id: '',
            owner_didar_ids: Array.isArray(filters.owner_didar_ids) ? filters.owner_didar_ids.join(',') : '',
            product_ids: Array.isArray(filters.product_ids) ? filters.product_ids.join(',') : '',
            deal_statuses: Array.isArray(filters.deal_statuses) ? filters.deal_statuses.join(',') : '',
            deal_lead_types: Array.isArray(filters.deal_lead_types) ? filters.deal_lead_types.join(',') : '',
            row_date: '',
            row_owner_didar_id: '',
            row_owner_name: '',
            row_product_name: '',
            row_failure_reason: '',
            row_activity_type: '',
            row_stage: ''
        };
        this.appendCriteriaToKpiPayload(payload, 'kpi');
        const res = await this.req('get_kpi_drilldown', payload);
        if (res.status !== 'success') {
            if (container) container.innerHTML = `<div class="text-sm text-red-400">${this.escapeHtml(res.message || 'خطا در دریافت جزئیات')}</div>`;
            return;
        }
        this.state.kpiDrilldown = {
            tableId: 'performance_cards',
            metricKey,
            row: null,
            data: res,
            loading: false
        };
        if (container) container.innerHTML = this.renderKpiDrilldownContent(res);
    },

    exportPerformanceChartCsv(sourceKey) {
        const source = this.state.performanceChartSource?.[sourceKey];
        if (!Array.isArray(source) || source.length === 0) {
            Swal.fire('خطا', 'داده‌ای برای خروجی نمودار وجود ندارد', 'error');
            return;
        }
        const headers = Object.keys(source[0] || {});
        const lines = [headers.map((h) => this.csvEscape(h)).join(',')];
        source.forEach((row) => {
            const cells = headers.map((h) => this.csvEscape(row[h]));
            lines.push(cells.join(','));
        });
        const csvText = "\uFEFF" + lines.join('\n');
        const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `performance_${sourceKey}_${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(link.href);
    },

    async exportPerformanceChart(chartKey, format) {
        const chart = this.state.performanceCharts?.[chartKey];
        if (!chart) {
            Swal.fire('خطا', 'نمودار در دسترس نیست', 'error');
            return;
        }
        const ts = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');
        if (format === 'svg') {
            const svg = chart?.el?.querySelector('svg');
            if (!svg) {
                Swal.fire('خطا', 'SVG نمودار پیدا نشد', 'error');
                return;
            }
            const blob = new Blob([svg.outerHTML], { type: 'image/svg+xml;charset=utf-8' });
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = `performance_${chartKey}_${ts}.svg`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            URL.revokeObjectURL(link.href);
            return;
        }

        const uri = await chart.dataURI();
        if (format === 'png') {
            const link = document.createElement('a');
            link.href = uri.imgURI;
            link.download = `performance_${chartKey}_${ts}.png`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            return;
        }

        if (format === 'pdf') {
            if (!window.jspdf || !window.jspdf.jsPDF) {
                Swal.fire('خطا', 'کتابخانه PDF بارگذاری نشده است', 'error');
                return;
            }
            const pdf = new window.jspdf.jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' });
            const pageWidth = pdf.internal.pageSize.getWidth();
            const pageHeight = pdf.internal.pageSize.getHeight();
            const margin = 24;
            pdf.setFont('helvetica', 'bold');
            pdf.setFontSize(12);
            pdf.text(`Performance Chart: ${chartKey}`, margin, 24);
            pdf.addImage(uri.imgURI, 'PNG', margin, 36, pageWidth - margin * 2, pageHeight - 70, undefined, 'FAST');
            pdf.save(`performance_${chartKey}_${ts}.pdf`);
        }
    },

    renderPerformanceTabContent(tabs, activeTab) {
        const sections = [];

        if (activeTab === 'activity') {
            sections.push(
                { key: 'activity_summary', title: 'خلاصه فعالیت روزانه', tableId: 'activity_summary', rows: tabs?.activity?.activity_summary || [] },
                { key: 'activity_type_details', title: 'جزئیات نوع فعالیت', tableId: 'activity_type_details', rows: tabs?.activity?.activity_type_details || [] }
            );
        } else if (activeTab === 'sales_deals') {
            sections.push(
                { key: 'deal_status', title: 'وضعیت معاملات', tableId: 'deal_status', rows: tabs?.sales_deals?.deal_status || [] },
                { key: 'lead_status', title: 'وضعیت لیدها', tableId: 'lead_status', rows: tabs?.sales_deals?.lead_status || [] },
                { key: 'conversion_status', title: 'وضعیت نرخ تبدیل', tableId: 'conversion_status', rows: tabs?.sales_deals?.conversion_status || [] }
            );
        } else if (activeTab === 'payments') {
            sections.push(
                { key: 'payment_mix', title: 'پرداخت‌ها', tableId: 'payment_mix', rows: tabs?.payments?.payment_mix || [] },
                { key: 'payment_status', title: 'وضعیت پرداخت‌ها', tableId: 'payment_status', rows: tabs?.payments?.payment_status || [] }
            );
        } else if (activeTab === 'products') {
            sections.push(
                { key: 'product_status', title: 'وضعیت محصولات', tableId: 'product_status', rows: tabs?.products?.product_status || [] },
                { key: 'product_sold_by_owner_day', title: 'محصولات فروخته‌شده', tableId: 'product_sold_by_owner_day', rows: tabs?.products?.product_sold_by_owner_day || [] },
                { key: 'product_requests_by_owner_day', title: 'محصولات درخواستی', tableId: 'product_requests_by_owner_day', rows: tabs?.products?.product_requests_by_owner_day || [] },
                { key: 'product_failed_by_owner_day', title: 'محصولات فروش‌نرفته', tableId: 'product_failed_by_owner_day', rows: tabs?.products?.product_failed_by_owner_day || [] }
            );
        } else {
            sections.push(
                { key: 'failure_reasons', title: 'دلایل شکست', tableId: 'failure_reasons', rows: tabs?.failure_reasons?.failure_reasons || [] }
            );
        }

        const visibleSections = sections.filter(section => this.canPerformanceFeatureClient(`performance.table.${section.tableId}`));
        if (!visibleSections.length) {
            return `<div class="text-center text-gray-500 py-6 text-sm">دسترسی به جدول‌های این تب ندارید</div>`;
        }
        return visibleSections.map(section => `
            <div class="mb-5">
                <div class="text-sm font-bold text-cyan-300 mb-2">${section.title}</div>
                ${this.renderPerformanceSimpleTable(section)}
            </div>
        `).join('');
    },

    formatPerformanceCell(value, type) {
        if (type === 'amount') return `${this.formatNumber(Number(value || 0))} تومان`;
        if (type === 'percent') return `${Number(value || 0).toFixed(2)}%`;
        if (type === 'day') return `${Number(value || 0).toFixed(2)} روز`;
        if (type === 'count') return this.formatNumber(Number(value || 0));
        if (type === 'date') return this.formatKpiDate(value);
        return this.escapeHtml(String(value ?? '-'));
    },

    async openPerformanceDrilldown(tableId, metricKey, sectionKey, rowIndex) {
        if (!this.canPerformanceFeatureClient('performance.drilldown')) {
            Swal.fire('خطا', 'دسترسی Drilldown داشبورد عملکرد ندارید', 'error');
            return;
        }
        const rows = this.state.performanceRenderedRows?.[sectionKey] || [];
        const row = rows?.[rowIndex];
        if (!row) return;
        this.state.kpiDrilldownContext = {
            mode: 'performance',
            kind: 'performance_table',
            tableId,
            metricKey,
            sectionKey,
            rowIndex
        };

        const definition = this.getKpiAdvancedTableDefinition(tableId);
        const col = definition?.columns?.find(c => c.key === metricKey);
        const metricLabel = col?.label || metricKey;
        const tableTitle = definition?.title || tableId;

        const subtitle = document.getElementById('kpi-drilldown-subtitle');
        if (subtitle) {
            subtitle.textContent = `${tableTitle} | ${metricLabel} | تاریخ: ${row.date ? this.formatKpiDate(row.date) : 'همه'} | کارشناس: ${row.owner_name || 'همه'}`;
        }
        const container = document.getElementById('kpi-drilldown-content');
        if (container) {
            container.innerHTML = '<div class="text-sm text-gray-400">در حال بارگذاری داده‌های مرتبط...</div>';
        }
        this.openModal('modalKpiDrilldown');

        const filters = this.state.performanceFilters || {};
        const payload = {
            table_id: tableId,
            metric_key: metricKey,
            date_from: filters.date_from || '',
            date_to: filters.date_to || '',
            owner_didar_id: '',
            product_id: '',
            owner_didar_ids: Array.isArray(filters.owner_didar_ids) ? filters.owner_didar_ids.join(',') : '',
            product_ids: Array.isArray(filters.product_ids) ? filters.product_ids.join(',') : '',
            deal_statuses: '',
            deal_lead_types: '',
            row_date: row.date || '',
            row_owner_didar_id: row.owner_didar_id || '',
            row_owner_name: row.owner_name || '',
            row_product_name: row.product_name || '',
            row_failure_reason: row.failure_reason || '',
            row_activity_type: row.activity_type || ''
        };

        this.appendCriteriaToKpiPayload(payload, 'performance');
        const res = await this.req('get_kpi_drilldown', payload);
        if (res.status !== 'success') {
            if (container) container.innerHTML = `<div class="text-sm text-red-400">${this.escapeHtml(res.message || 'خطا در دریافت جزئیات')}</div>`;
            return;
        }
        this.state.kpiDrilldown = {
            tableId,
            metricKey,
            row: row || null,
            data: res,
            loading: false
        };
        if (container) container.innerHTML = this.renderKpiDrilldownContent(res);
    },

    renderPerformanceSimpleTable(section) {
        const data = Array.isArray(section?.rows) ? section.rows : [];
        if (data.length === 0) {
            return '<div class="text-xs text-gray-500 py-3">داده‌ای برای این بخش وجود ندارد.</div>';
        }
        const tableId = section.tableId;
        const definition = this.getKpiAdvancedTableDefinition(tableId);
        const definitionColumns = Array.isArray(definition?.columns) ? definition.columns : [];
        const rowKeys = Object.keys(data[0] || {});
        let cols = definitionColumns.filter(col => rowKeys.includes(col.key) && col.key !== 'owner_didar_id');
        if (cols.length === 0) {
            cols = rowKeys
                .filter(k => k !== 'owner_didar_id')
                .map(k => ({ key: k, label: k, type: k === 'date' ? 'date' : (typeof data[0][k] === 'number' ? 'count' : 'text') }));
        }

        this.state.performanceRenderedRows[section.key] = data;

        const header = cols.map(col => `<th class="border border-gray-700 p-2 text-xs">${this.escapeHtml(col.label)}</th>`).join('');
        const numericColMax = {};
        cols.forEach(col => {
            if (['count', 'amount', 'percent', 'day'].includes(col.type)) {
                numericColMax[col.key] = Math.max(0, ...data.map(r => Number(r[col.key] || 0)));
            }
        });

        const body = data.slice(0, 250).map((row, idx) => {
            const tds = cols.map(col => {
                const formatted = this.formatPerformanceCell(row[col.key], col.type);
                const clickable = !['text', 'date'].includes(col.type);
                let heatStyle = '';
                if (numericColMax[col.key] && numericColMax[col.key] > 0) {
                    const v = Number(row[col.key] || 0);
                    const ratio = Math.max(0, Math.min(1, v / numericColMax[col.key]));
                    const alpha = Math.min(0.40, ratio * 0.40);
                    if (col.type === 'percent') {
                        heatStyle = `background-color: rgba(168,85,247,${alpha.toFixed(3)});`;
                    } else if (col.type === 'amount') {
                        heatStyle = `background-color: rgba(16,185,129,${alpha.toFixed(3)});`;
                    } else if (col.type === 'day') {
                        heatStyle = `background-color: rgba(245,158,11,${alpha.toFixed(3)});`;
                    } else {
                        heatStyle = `background-color: rgba(59,130,246,${alpha.toFixed(3)});`;
                    }
                }
                if (!clickable) {
                    return `<td class="border border-gray-700/60 p-2 text-xs text-center" style="${heatStyle}">${formatted}</td>`;
                }
                return `<td class="border border-gray-700/60 p-2 text-xs text-center" style="${heatStyle}">
                    <button type="button" class="text-cyan-300 hover:text-cyan-200 hover:underline transition-colors w-full" onclick="app.openPerformanceDrilldown('${tableId}','${col.key}','${section.key}',${idx})">${formatted}</button>
                </td>`;
            }).join('');
            return `<tr class="${idx % 2 === 0 ? 'bg-gray-900/30' : 'bg-gray-900/10'}">${tds}</tr>`;
        }).join('');
        return `<div class="overflow-x-auto"><table class="w-full min-w-[900px] text-xs"><thead><tr class="bg-gray-800">${header}</tr></thead><tbody>${body}</tbody></table></div>`;
    },

    destroyPerformanceChart(key) {
        if (this.state.performanceCharts?.[key]) {
            try { this.state.performanceCharts[key].destroy(); } catch (e) {}
            this.state.performanceCharts[key] = null;
        }
    },

    renderPerformanceCharts(charts) {
        if (!this.canPerformanceFeatureClient('performance.view')) {
            return;
        }
        if (typeof ApexCharts === 'undefined') {
            console.warn('ApexCharts library not loaded.');
            return;
        }

        const common = {
            chart: {
                toolbar: { show: false },
                zoom: { enabled: false },
                selection: { enabled: false },
                animations: { enabled: true },
                fontFamily: 'inherit',
                background: 'transparent'
            },
            theme: { mode: 'dark' },
            grid: { borderColor: '#374151' },
            legend: { labels: { colors: '#d1d5db' } },
            xaxis: { labels: { style: { colors: '#9ca3af' } } },
            yaxis: { labels: { style: { colors: '#9ca3af' } } },
            tooltip: { theme: 'dark' }
        };

        this.destroyPerformanceChart('dailySales');
        this.destroyPerformanceChart('dailyActivities');
        this.destroyPerformanceChart('dealStatus');
        this.destroyPerformanceChart('productShare');
        this.destroyPerformanceChart('conversionFunnel');

        const canChartDailySales = this.canPerformanceFeatureClient('performance.chart.daily_sales');
        const canChartDailyActivities = this.canPerformanceFeatureClient('performance.chart.daily_activities');
        const canChartDealStatus = this.canPerformanceFeatureClient('performance.chart.deal_status');
        const canChartProductShare = this.canPerformanceFeatureClient('performance.chart.product_share');
        const canChartFunnel = this.canPerformanceFeatureClient('performance.chart.conversion_funnel');

        const dailySalesRaw = Array.isArray(charts.daily_sales) ? charts.daily_sales : [];
        const dailySalesPrevRaw = Array.isArray(charts.daily_sales_previous) ? charts.daily_sales_previous : [];
        this.state.performanceChartSource.daily_sales = dailySalesRaw;
        const salesWindow = this.state.performanceChartWindow || {};
        const salesSize = Math.max(7, Number(salesWindow.salesSize || 14));
        const salesOffset = Math.max(0, Number(salesWindow.salesOffset || 0));
        const salesMaxOffset = Math.max(0, dailySalesRaw.length - salesSize);
        const salesSlice = dailySalesRaw.slice(Math.min(salesOffset, salesMaxOffset), Math.min(salesOffset, salesMaxOffset) + salesSize);
        const salesPrevSlice = dailySalesPrevRaw.slice(Math.min(salesOffset, Math.max(0, dailySalesPrevRaw.length - salesSize)), Math.min(salesOffset, Math.max(0, dailySalesPrevRaw.length - salesSize)) + salesSize);
        const salesLabels = salesSlice.map(i => this.formatKpiDate(i.payment_date || i.date || ''));
        const salesAmountSeries = salesSlice.map(i => Number(i.collected_amount || 0));
        const salesDealsSeries = salesSlice.map(i => Number(i.deal_count || 0));
        const salesPrevSeries = salesPrevSlice.map(i => Number(i.collected_amount || 0));
        const targetDaily = Number((this.state.performanceDashboard?.summary_cards?.target_daily_amount) || 0);
        if (canChartDailySales && document.querySelector('#perf-chart-daily-sales')) this.state.performanceCharts.dailySales = new ApexCharts(document.querySelector('#perf-chart-daily-sales'), {
            ...common,
            chart: {
                ...common.chart,
                type: 'line',
                height: 320,
                events: {
                    dataPointSelection: (event, chartContext, config) => {
                        const point = salesSlice[config.dataPointIndex];
                        if (!point) return;
                        this.openPerformanceChartDrilldown('performance_charts', 'daily_sales_point', {
                            date: point.date || point.payment_date || ''
                        });
                    }
                }
            },
            annotations: targetDaily > 0 ? {
                yaxis: [{
                    y: targetDaily,
                    borderColor: '#f59e0b',
                    label: { text: `هدف روزانه: ${this.formatNumber(targetDaily)}`, style: { color: '#111827', background: '#f59e0b' } }
                }]
            } : undefined,
            series: [
                { name: 'فروش (تومان)', type: 'line', data: salesAmountSeries },
                { name: 'تعداد معامله', type: 'column', data: salesDealsSeries },
                { name: 'فروش بازه قبل', type: 'line', data: salesPrevSeries }
            ],
            legend: {
                ...common.legend,
                formatter: (seriesName, opts) => {
                    const series = opts?.w?.globals?.series?.[opts.seriesIndex] || [];
                    const total = Array.isArray(series)
                        ? series.reduce((sum, val) => sum + Number(val || 0), 0)
                        : Number(series || 0);
                    return `${seriesName} (${this.formatNumber(Math.round(total))})`;
                }
            },
            dataLabels: { enabled: false },
            xaxis: { categories: salesLabels, labels: { style: { colors: '#9ca3af' } } },
            stroke: { width: [3, 0, 2], dashArray: [0, 0, 6] },
            yaxis: [
                {
                    title: { text: 'مبلغ فروش', style: { color: '#9ca3af' } },
                    labels: {
                        style: { colors: '#9ca3af' },
                        formatter: (val) => this.formatNumber(Math.round(Number(val || 0)))
                    }
                },
                {
                    opposite: true,
                    title: { text: 'تعداد معامله', style: { color: '#9ca3af' } },
                    labels: {
                        style: { colors: '#9ca3af' },
                        formatter: (val) => this.formatNumber(Math.round(Number(val || 0)))
                    }
                }
            ],
            tooltip: {
                y: {
                    formatter: (val, opts) => {
                        const name = opts?.seriesIndex === 1 ? 'معامله' : 'تومان';
                        return `${this.formatNumber(Math.round(Number(val || 0)))} ${name}`;
                    }
                }
            }
        });
        if (canChartDailySales && this.state.performanceCharts.dailySales) this.state.performanceCharts.dailySales.render();

        const dailyActivitiesRaw = Array.isArray(charts.daily_activities) ? charts.daily_activities : [];
        const dailyActivitiesPrevRaw = Array.isArray(charts.daily_activities_previous) ? charts.daily_activities_previous : [];
        const dailyActivitiesByTypeRaw = Array.isArray(charts.daily_activities_by_type) ? charts.daily_activities_by_type : [];
        this.state.performanceChartSource.daily_activities = dailyActivitiesRaw;
        this.state.performanceChartSource.daily_activities_by_type = dailyActivitiesByTypeRaw;
        const activitySize = Math.max(7, Number(salesWindow.activitySize || 14));
        const activityOffset = Math.max(0, Number(salesWindow.activityOffset || 0));
        const actMaxOffset = Math.max(0, dailyActivitiesRaw.length - activitySize);
        const activitySlice = dailyActivitiesRaw.slice(Math.min(activityOffset, actMaxOffset), Math.min(activityOffset, actMaxOffset) + activitySize);
        const activityPrevSlice = dailyActivitiesPrevRaw.slice(Math.min(activityOffset, Math.max(0, dailyActivitiesPrevRaw.length - activitySize)), Math.min(activityOffset, Math.max(0, dailyActivitiesPrevRaw.length - activitySize)) + activitySize);
        const actLabels = activitySlice.map(i => this.formatKpiDate(i.date || ''));
        const activityDateRawLabels = activitySlice.map(i => (i.date || ''));
        const typeTotals = {};
        dailyActivitiesByTypeRaw.forEach((r) => {
            const k = r.activity_type || 'فعالیت';
            typeTotals[k] = (typeTotals[k] || 0) + Number(r.activities_count || 0);
        });
        const topTypes = Object.entries(typeTotals)
            .sort((a, b) => Number(b[1]) - Number(a[1]))
            .slice(0, 4)
            .map((entry) => entry[0]);
        const activityTypeSeries = topTypes.map((typeName) => {
            const data = activityDateRawLabels.map((rawDate) => {
                const found = dailyActivitiesByTypeRaw.find((r) => (r.date || '') === rawDate && (r.activity_type || 'فعالیت') === typeName);
                return Number(found?.activities_count || 0);
            });
            return { name: typeName, data };
        });
        const otherSeries = {
            name: 'سایر',
            data: activityDateRawLabels.map((rawDate) => {
                return dailyActivitiesByTypeRaw
                    .filter((r) => (r.date || '') === rawDate && !topTypes.includes(r.activity_type || 'فعالیت'))
                    .reduce((sum, r) => sum + Number(r.activities_count || 0), 0);
            })
        };
        if (otherSeries.data.some((v) => Number(v) > 0)) {
            activityTypeSeries.push(otherSeries);
        }
        const totalActivitySeries = {
            name: 'کل فعالیت‌ها',
            type: 'line',
            data: activitySlice.map((i) => Number(i.activities_count || 0))
        };
        const prevActivitySeries = {
            name: 'کل فعالیت بازه قبل',
            type: 'line',
            data: activityPrevSlice.map((i) => Number(i.activities_count || 0))
        };
        if (canChartDailyActivities && document.querySelector('#perf-chart-daily-activities')) this.state.performanceCharts.dailyActivities = new ApexCharts(document.querySelector('#perf-chart-daily-activities'), {
            ...common,
            chart: {
                ...common.chart,
                type: 'bar',
                stacked: true,
                height: 420,
                events: {
                    dataPointSelection: (event, chartContext, config) => {
                        const rawDate = activityDateRawLabels[config.dataPointIndex];
                        const typeName = topTypes[config.seriesIndex] || '';
                        this.openPerformanceChartDrilldown('activity_type_details', 'activity_count_total', {
                            date: rawDate || '',
                            activity_type: typeName
                        });
                    }
                }
            },
            plotOptions: {
                bar: {
                    columnWidth: '55%'
                }
            },
            series: [...activityTypeSeries, totalActivitySeries, prevActivitySeries],
            dataLabels: { enabled: false },
            xaxis: { categories: actLabels, labels: { style: { colors: '#9ca3af' } } },
            stroke: { width: activityTypeSeries.map(() => 0).concat([3, 2]), dashArray: activityTypeSeries.map(() => 0).concat([0, 6]) },
            colors: ['#60a5fa','#34d399','#f59e0b','#a78bfa','#14b8a6','#ef4444','#94a3b8'],
            yaxis: {
                labels: {
                    style: { colors: '#9ca3af' },
                    formatter: (val) => this.formatNumber(Math.round(Number(val || 0)))
                }
            },
            legend: {
                ...common.legend,
                position: 'bottom',
                horizontalAlign: 'center',
                itemMargin: { horizontal: 8, vertical: 6 },
                formatter: (seriesName, opts) => {
                    const series = opts?.w?.globals?.series?.[opts.seriesIndex] || [];
                    const total = Array.isArray(series)
                        ? series.reduce((sum, val) => sum + Number(val || 0), 0)
                        : Number(series || 0);
                    return `${seriesName} (${this.formatNumber(Math.round(total))})`;
                }
            },
            tooltip: {
                y: {
                    formatter: (val) => `${this.formatNumber(Math.round(Number(val || 0)))} فعالیت`
                }
            }
        });
        if (canChartDailyActivities && this.state.performanceCharts.dailyActivities) this.state.performanceCharts.dailyActivities.render();

        const dealStatus = Array.isArray(charts.deal_status_breakdown) ? charts.deal_status_breakdown : [];
        this.state.performanceChartSource.deal_status_breakdown = dealStatus;
        if (canChartDealStatus && document.querySelector('#perf-chart-deal-status')) this.state.performanceCharts.dealStatus = new ApexCharts(document.querySelector('#perf-chart-deal-status'), {
            ...common,
            chart: {
                ...common.chart,
                type: 'donut',
                height: 320,
                events: {
                    dataPointSelection: (event, chartContext, config) => {
                        const item = dealStatus[config.dataPointIndex];
                        if (!item) return;
                        const map = {
                            'جاری': ['performance_cards', 'open_pipeline_count'],
                            'دارای فروش': ['performance_cards', 'sales_total'],
                            'بدون فروش': ['performance_cards', 'without_payment_count'],
                            'ناموفق': ['performance_cards', 'lost_count']
                        };
                        const route = map[item.status] || ['performance_cards', 'open_pipeline_count'];
                        this.openPerformanceChartDrilldown(route[0], route[1], {});
                    }
                }
            },
            labels: dealStatus.map(i => i.status || '-'),
            series: dealStatus.map(i => Number(i.count || 0)),
            legend: {
                ...common.legend,
                formatter: (seriesName, opts) => `${seriesName} (${this.formatNumber(opts.w.globals.series[opts.seriesIndex] || 0)})`
            },
            tooltip: {
                y: {
                    formatter: (val) => `${this.formatNumber(Math.round(Number(val || 0)))} مورد`
                }
            }
        });
        if (canChartDealStatus && this.state.performanceCharts.dealStatus) this.state.performanceCharts.dealStatus.render();

        const productShare = Array.isArray(charts.product_sales_share) ? charts.product_sales_share : [];
        this.state.performanceChartSource.product_sales_share = productShare;
        if (canChartProductShare && document.querySelector('#perf-chart-product-share')) this.state.performanceCharts.productShare = new ApexCharts(document.querySelector('#perf-chart-product-share'), {
            ...common,
            chart: {
                ...common.chart,
                type: 'pie',
                height: 320,
                events: {
                    dataPointSelection: (event, chartContext, config) => {
                        const item = productShare[config.dataPointIndex];
                        if (!item) return;
                        this.openPerformanceChartDrilldown('performance_charts', 'product_sales_share_item', {
                            product_name: item.product_name || ''
                        });
                    }
                }
            },
            labels: productShare.map(i => i.product_name || '-'),
            series: productShare.map(i => Number(i.deal_count || 0)),
            legend: {
                ...common.legend,
                formatter: (seriesName, opts) => `${seriesName} (${this.formatNumber(opts.w.globals.series[opts.seriesIndex] || 0)})`
            },
            tooltip: {
                y: {
                    formatter: (val) => `${this.formatNumber(Math.round(Number(val || 0)))} فروش`
                }
            }
        });
        if (canChartProductShare && this.state.performanceCharts.productShare) this.state.performanceCharts.productShare.render();

        const funnel = Array.isArray(charts.conversion_funnel) ? charts.conversion_funnel : [];
        this.state.performanceChartSource.conversion_funnel = funnel;
        if (canChartFunnel && document.querySelector('#perf-chart-conversion-funnel')) this.state.performanceCharts.conversionFunnel = new ApexCharts(document.querySelector('#perf-chart-conversion-funnel'), {
            ...common,
            chart: {
                ...common.chart,
                type: 'bar',
                height: 320,
                events: {
                    dataPointSelection: (event, chartContext, config) => {
                        const item = funnel[config.dataPointIndex];
                        if (!item) return;
                        this.openPerformanceChartDrilldown('performance_charts', 'conversion_funnel_stage', { stage: item.stage || '' });
                    }
                }
            },
            plotOptions: { bar: { horizontal: true, distributed: true, barHeight: '68%' } },
            series: [{ name: 'تعداد', data: funnel.map((f) => Number(f.count || 0)) }],
            xaxis: {
                categories: funnel.map((f) => f.stage || '-'),
                labels: { style: { colors: '#9ca3af' } }
            },
            yaxis: { labels: { style: { colors: '#9ca3af' } } },
            dataLabels: {
                enabled: true,
                formatter: (v) => this.formatNumber(v)
            },
            tooltip: {
                y: { formatter: (v) => `${this.formatNumber(v)} مورد` }
            }
        });
        if (canChartFunnel && this.state.performanceCharts.conversionFunnel) this.state.performanceCharts.conversionFunnel.render();
    },

    initPerformanceChartPan(type, selector, total) {
        return;
    },

    // ========== KPI DASHBOARD ==========
    async showKPIDashboard() {
        if (!this.canKpiFeatureClient('kpi.view')) {
            Swal.fire('خطا', 'دسترسی به داشبورد KPI ندارید', 'error');
            return;
        }
        this.openModal('modalKPI');
        if (!this.state.kpiFilters.date_from) {
            const range = this.getJalaliMonthRange();
            this.state.kpiFilters.date_from = range.date_from;
        }
        if (!this.state.kpiFilters.date_to) {
            const range = this.getJalaliMonthRange();
            this.state.kpiFilters.date_to = range.date_to;
        }
        await this.loadKpiFilterData();
        await this.loadKPIDashboard({ useState: true });
    },

    initKpiDatePickers() {
        console.log('[DEBUG] Initializing KPI datepickers...');
        console.log('[DEBUG] jQuery available:', typeof $ !== 'undefined');
        console.log('[DEBUG] $.fn available:', typeof $?.fn !== 'undefined');
        console.log('[DEBUG] $.fn.persianDatepicker available:', typeof $.fn?.persianDatepicker === 'function');

        // Wait for elements to be rendered
        const self = this;
        setTimeout(function() {
            if (typeof $ === 'undefined') {
                console.warn('[DEBUG] jQuery not available for KPI datepicker');
                return;
            }

            const $dateFrom = $('#kpi-date-from');
            const $dateTo = $('#kpi-date-to');

            console.log('[DEBUG] $dateFrom element:', $dateFrom.length > 0 ? 'found' : 'not found');
            console.log('[DEBUG] $dateTo element:', $dateTo.length > 0 ? 'found' : 'not found');

            const initOnce = ($input, desiredValue, label) => {
                if (!($input.length > 0) || typeof $.fn?.persianDatepicker !== 'function') {
                    return false;
                }
                if ($input.data('kpiDatepickerInit') !== 1) {
                    try {
                        $input.persianDatepicker('destroy');
                    } catch (error) {
                    }
                    $input.persianDatepicker({
                        format: 'YYYY/MM/DD',
                        initialValue: false,
                        autoClose: true,
                        observer: false
                    });
                    $input.data('kpiDatepickerInit', 1);
                    console.log(`[DEBUG] KPI ${label} datepicker initialized`);
                }
                const inputEl = $input.get(0);
                if (document.activeElement !== inputEl) {
                    const normalizedDesired = desiredValue ? (toPersianDate(desiredValue) || desiredValue) : '';
                    if (normalizedDesired && String($input.val() || '') !== String(normalizedDesired)) {
                        $input.val(normalizedDesired);
                    }
                }
                return true;
            };

            if (!initOnce($dateFrom, (self.state.kpiFilters || {}).date_from || '', 'Date From')) {
                console.warn('[DEBUG] KPI Date From datepicker initialization failed', {
                    exists: $dateFrom.length > 0,
                    hasPersianDatepicker: typeof $.fn?.persianDatepicker === 'function'
                });
            }

            if (!initOnce($dateTo, (self.state.kpiFilters || {}).date_to || '', 'Date To')) {
                console.warn('[DEBUG] KPI Date To datepicker initialization failed', {
                    exists: $dateTo.length > 0,
                    hasPersianDatepicker: typeof $.fn?.persianDatepicker === 'function'
                });
            }
        }, 1000);
    },

    async loadKpiFilterData() {
        // Load users for owner filter
        if (this.isAdminLikeForAnalytics()) {
            try {
                const usersRes = await this.req('get_didar_users');
                if (usersRes.status === 'success') {
                    this.state.kpiUsers = (usersRes.users || []).map(user => ({
                        didar_user_id: user.owner_id || user.didar_user_id || '',
                        display_name: user.display_name || user.email || '',
                        email: user.email || '',
                        is_active: user.is_active ?? 1
                    }));
                } else {
                    console.warn('Failed to load users:', usersRes.message);
                    this.state.kpiUsers = [];
                }
            } catch (error) {
                console.error('Error loading users:', error);
                this.state.kpiUsers = [];
            }

            if (this.state.kpiUsers.length === 0) {
                try {
                    const usersRes = await this.req('get_users_list');
                    if (usersRes.status === 'success') {
                        this.state.kpiUsers = usersRes.users || [];
                    } else {
                        console.warn('Failed to load users:', usersRes.message);
                        this.state.kpiUsers = [];
                    }
                } catch (error) {
                    console.error('Error loading users:', error);
                    this.state.kpiUsers = [];
                }
            }
        } else {
            this.state.kpiUsers = [{
                didar_user_id: this.state.user?.didar_id || '',
                display_name: this.state.user?.name || '',
                is_active: 1
            }];
        }
        this.populateKpiOwnerFilter();

        // Load products for product filter
        try {
            const productsRes = await this.req('get_kpi_products');
            if (productsRes.status === 'success') {
                this.state.kpiProducts = productsRes.products || [];
                this.populateKpiProductFilter();
            } else {
                console.warn('Failed to load products:', productsRes.message);
                this.state.kpiProducts = [];
                this.populateKpiProductFilter();
            }
        } catch (error) {
            console.error('Error loading products:', error);
            this.state.kpiProducts = [];
            this.populateKpiProductFilter();
        }
    },

    populateKpiOwnerFilter() {
        const select = document.getElementById('kpi-owner-filter');
        if (!select) return;

        const selectedValues = Array.isArray(this.state.kpiFilters?.owner_didar_ids)
            ? this.state.kpiFilters.owner_didar_ids.map(v => String(v))
            : [];
        select.innerHTML = '';

        const allOption = document.createElement('option');
        allOption.value = '__all__';
        allOption.textContent = 'همه مسئول‌ها';
        allOption.selected = selectedValues.length === 0 || selectedValues.includes('__all__');
        select.appendChild(allOption);

        // فقط کاربران active را فیلتر کن
        const activeUsers = (this.state.kpiUsers || []).filter(user => Number(user.is_active) === 1);

        activeUsers.forEach(user => {
            const opt = document.createElement('option');
            opt.value = user.didar_user_id || user.owner_id || '';
            if (!opt.value) return;
            const name = user.display_name || user.email || (user.first_name + ' ' + user.last_name);
            opt.textContent = name;
            if (selectedValues.includes(String(opt.value))) {
                opt.selected = true;
            }
            select.appendChild(opt);
        });
    },

    populateKpiProductFilter() {
        const select = document.getElementById('kpi-product-filter');
        if (!select) return;

        const selectedValues = Array.isArray(this.state.kpiFilters?.product_ids)
            ? this.state.kpiFilters.product_ids.map(v => String(v))
            : [];
        select.innerHTML = '';

        const allOption = document.createElement('option');
        allOption.value = '__all__';
        allOption.textContent = 'همه محصولات';
        allOption.selected = selectedValues.length === 0 || selectedValues.includes('__all__');
        select.appendChild(allOption);

        this.state.kpiProducts.forEach(product => {
            const opt = document.createElement('option');
            opt.value = product.id;
            opt.textContent = product.name;
            if (!opt.value) return;
            if (selectedValues.includes(String(product.id))) {
                opt.selected = true;
            }
            select.appendChild(opt);
        });
    },

    async loadKPIDashboard(options = {}) {
        if (!this.canKpiFeatureClient('kpi.view')) {
            Swal.fire('خطا', 'دسترسی به داشبورد KPI ندارید', 'error');
            return;
        }
        const useState = options.useState === true;
        const stateFilters = this.state.kpiFilters || {};
        const defaultRange = this.getJalaliMonthRange();
        let dateFrom = stateFilters.date_from || defaultRange.date_from;
        let dateTo = stateFilters.date_to || defaultRange.date_to;
        let ownerIds = Array.isArray(stateFilters.owner_didar_ids) ? [...stateFilters.owner_didar_ids] : [];
        let productIds = Array.isArray(stateFilters.product_ids) ? [...stateFilters.product_ids] : [];
        let dealStatuses = Array.isArray(stateFilters.deal_statuses) ? [...stateFilters.deal_statuses] : [];
        let dealLeadTypes = Array.isArray(stateFilters.deal_lead_types) ? [...stateFilters.deal_lead_types] : [];
        let criteria = this.normalizeKpiCriteria(stateFilters.criteria || {});
        if (ownerIds.length === 0 && stateFilters.owner_didar_id) {
            ownerIds = [stateFilters.owner_didar_id];
        }
        if (productIds.length === 0 && stateFilters.product_id) {
            productIds = [stateFilters.product_id];
        }

        if (!useState) {
            const dateFromJ = (document.getElementById('kpi-date-from')?.value || '').trim();
            const dateToJ = (document.getElementById('kpi-date-to')?.value || '').trim();

            if (dateFromJ) {
                const dateFromG = persianToGregorian(this.toEnglishDigits(dateFromJ), '00:00');
                if (dateFromG) {
                    dateFrom = String(dateFromG).split(' ')[0];
                } else if (/^\d{4}-\d{2}-\d{2}$/.test(dateFromJ)) {
                    dateFrom = dateFromJ;
                }
            }

            if (dateToJ) {
                const dateToG = persianToGregorian(this.toEnglishDigits(dateToJ), '23:59');
                if (dateToG) {
                    dateTo = String(dateToG).split(' ')[0];
                } else if (/^\d{4}-\d{2}-\d{2}$/.test(dateToJ)) {
                    dateTo = dateToJ;
                }
            }

            ownerIds = this.getCustomMultiSelectValues('kpi-owner-filter');
            productIds = this.getCustomMultiSelectValues('kpi-product-filter');
            dealStatuses = this.getCustomMultiSelectValues('kpi-deal-status-filter');
            dealLeadTypes = this.getCustomMultiSelectValues('kpi-deal-lead-type-filter');
            criteria = this.collectKpiCriteriaFromDom(false);
        }

        const ownerId = ownerIds.length === 1 ? ownerIds[0] : '';
        const productId = productIds.length === 1 ? productIds[0] : '';

        this.state.kpiFilters = {
            date_from: dateFrom,
            date_to: dateTo,
            owner_didar_id: ownerId,
            product_id: productId,
            owner_didar_ids: ownerIds,
            product_ids: productIds,
            deal_statuses: dealStatuses,
            deal_lead_types: dealLeadTypes,
            criteria
        };

        const payload = {
            date_from: dateFrom,
            date_to: dateTo,
            owner_didar_id: ownerId,
            product_id: productId,
            owner_didar_ids: ownerIds.join(','),
            product_ids: productIds.join(','),
            deal_statuses: dealStatuses.join(','),
            deal_lead_types: dealLeadTypes.join(',')
        };
        this.appendCriteriaToKpiPayload(payload, 'kpi');
        const kpiLoadRequestId = (Number(this.state.kpiLoadRequestId || 0) + 1);
        this.state.kpiLoadRequestId = kpiLoadRequestId;
        this.state.kpiSplitPayload = { ...payload };
        this.state.kpiTabsLoaded = false;
        let usedSplit = false;
        try {
            const sectionReq = async (section) => this.req('get_kpi_section', { ...payload, section });
            const summaryRes = await sectionReq('summary');
            if (summaryRes.status !== 'success') throw new Error(summaryRes.message || 'summary failed');
            if (this.state.kpiLoadRequestId !== kpiLoadRequestId) return;
            usedSplit = true;
            if (summaryRes.filters && summaryRes.filters.criteria) {
                this.state.kpiFilters = {
                    ...(this.state.kpiFilters || {}),
                    criteria: this.normalizeKpiCriteria(summaryRes.filters.criteria)
                };
            }
            this.state.kpiLastData = this.mergeKpiSection({}, summaryRes.data || {}, 'summary');
            this.renderKPIDashboard(this.state.kpiLastData);

            const chartsRes = await sectionReq('charts');
            if (chartsRes.status === 'success') {
                if (this.state.kpiLoadRequestId !== kpiLoadRequestId) return;
                this.state.kpiLastData = this.mergeKpiSection(this.state.kpiLastData || {}, chartsRes.data || {}, 'charts');
                this.renderKPIDashboard(this.state.kpiLastData);
            }

            setTimeout(() => {
                this.loadKpiDashboardTabsSection({ requestId: kpiLoadRequestId });
            }, 0);
        } catch (e) {
            console.warn('KPI split load failed, fallback to full endpoint', e);
        }

        if (!usedSplit) {
            const res = await this.req('get_kpi', payload);
            if (res.status === 'success') {
                if (res.filters && res.filters.criteria) {
                    this.state.kpiFilters = {
                        ...(this.state.kpiFilters || {}),
                        criteria: this.normalizeKpiCriteria(res.filters.criteria)
                    };
                }
                this.state.kpiTabsLoaded = true;
                this.renderKPIDashboard(res.kpi);
            } else {
                Swal.fire('خطا', res.message || 'خطا در بارگذاری KPI', 'error');
            }
        }
    },

    async loadKpiDashboardTabsSection(options = {}) {
        const requestId = Number(options.requestId || this.state.kpiLoadRequestId || 0);
        if (!this.state.kpiSplitPayload) return;
        if (this.state.kpiTabsLoaded) return;
        if (this.state.kpiTabsLoading) return;
        this.state.kpiTabsLoading = true;
        try {
            const res = await this.req('get_kpi_section', { ...this.state.kpiSplitPayload, section: 'tabs' });
            if (res.status === 'success' && this.state.kpiLoadRequestId === requestId) {
                this.state.kpiLastData = this.mergeKpiSection(this.state.kpiLastData || {}, res.data || {}, 'tabs');
                this.state.kpiTabsLoaded = true;
                this.renderKPIDashboard(this.state.kpiLastData || {});
            }
        } catch (e) {
            console.warn('KPI tabs lazy load failed', e);
        } finally {
            this.state.kpiTabsLoading = false;
        }
    },

    mergeKpiSection(current, sectionData, section) {
        const out = current ? JSON.parse(JSON.stringify(current)) : {};
        Object.assign(out, sectionData || {});
        if (section === 'summary') {
            out.daily_sales = out.daily_sales || [];
            out.lost_sales = out.lost_sales || [];
            out.product_sales = out.product_sales || [];
            out.daily_operational = out.daily_operational || {};
            out.advanced_tables = out.advanced_tables || {};
        }
        return out;
    },

    renderKPIDashboard(kpi) {
        const content = document.getElementById('kpi-dashboard-content');
        if (!content) return;
        this.state.kpiLastData = kpi || {};

        const filters = this.state.kpiFilters || {};
        const criteria = this.normalizeKpiCriteria(filters.criteria || {});
        const sales = kpi.sales || {};
        const settled = kpi.settled || {};
        const outstanding = kpi.outstanding || {};
        const lost = kpi.lost_sales || [];
        const openPipeline = kpi.open_pipeline || {};
        const wonSales = kpi.won_sales || {};
        const dealsWithoutPayments = kpi.deals_without_payments || {};
        const productSales = kpi.product_sales || [];
        const dealConversion = kpi.deal_conversion || {};
        const leadConversion = kpi.lead_conversion || {};
        const timeToConversion = kpi.time_to_conversion || {};
        const target = kpi.target_achievement || {};
        const dailyOperational = kpi.daily_operational || {};
        const advancedTables = kpi.advanced_tables || {};

        this.state.kpiAdvancedTables = advancedTables;
        this.ensureKpiAdvancedUiState();

        // Calculate percentages for progress bar
        const achievementPercent = target.achievement_percent || 0;
        const progressColor = achievementPercent >= 100 ? 'bg-green-500' :
                             achievementPercent >= 75 ? 'bg-blue-500' :
                             achievementPercent >= 50 ? 'bg-yellow-500' : 'bg-red-500';

        // Format Toman amount (already in Toman, no conversion needed)
        const formatToman = (amount) => {
            const toman = parseFloat(amount) || 0;
            return this.formatNumber(toman);
        };
        const lostReasonSummaryHtml = (Array.isArray(lost) ? lost : [])
            .map(item => ({
                reason: item.failure_reason_text || item.failure_reason_code || 'نامشخص',
                count: Number(item.total_lost || 0)
            }))
            .filter(item => item.count > 0)
            .sort((a, b) => b.count - a.count)
            .slice(0, 3)
            .map(item => `<div class="text-[10px] text-gray-500">${this.escapeHtml(item.reason)}: ${this.formatNumber(item.count)}</div>`)
            .join('');

        const dateFromJalali = filters.date_from ? (toPersianDate(filters.date_from) || filters.date_from) : '';
        const dateToJalali = filters.date_to ? (toPersianDate(filters.date_to) || filters.date_to) : '';
        
        const tableIds = Object.keys(advancedTables || {});
        const navMenu = tableIds.length > 0 ? `
            <div class="flex overflow-x-auto gap-2 mt-4 pb-2 no-scrollbar scroll-smooth border-t border-gray-700 pt-4">
                ${tableIds.map(tableId => {
                    const def = this.getKpiAdvancedTableDefinition(tableId);
                    const title = (def?.title || '').replace(/^\d+\)\s*/, '');
                    const isVisible = this.state.kpiAdvancedVisibleTables?.[tableId] !== false;
                    return `
                        <div class="whitespace-nowrap bg-gray-800 border border-gray-700 text-gray-300 px-2 py-1.5 rounded-full text-[11px] transition-all flex items-center gap-2 shadow-sm">
                            <input type="checkbox" ${isVisible ? 'checked' : ''} onchange="app.toggleKpiAdvancedTableVisibility('${tableId}', this.checked)">
                            <button onclick="app.scrollToKpiTable('kpi-adv-panel-${tableId}')" 
                                    class="text-gray-300 hover:text-blue-200 transition-colors"
                                    title="رفتن به جدول ${title}">
                                ${title}
                            </button>
                        </div>
                    `;
                }).join('')}
            </div>
        ` : '';

        const activeFilterChips = this.renderKpiActiveFilterChips(filters);

        content.innerHTML = `
            <!-- Header Filters -->
            <div class="glass p-4 md:p-6 mb-6 border border-gray-700/70 shadow-xl bg-gray-900/95 backdrop-blur-md">
                <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3 mb-4">
                    <div class="text-sm text-gray-300 font-bold">فیلترهای داشبورد KPI</div>
                    <div class="flex flex-wrap items-center gap-2">
                        <button onclick="app.setKpiQuickRange('today')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">امروز</button>
                        <button onclick="app.setKpiQuickRange('week')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">۷ روز اخیر</button>
                        <button onclick="app.setKpiQuickRange('month')" class="text-xs px-3 py-1.5 rounded bg-gray-700 hover:bg-gray-600 text-white">ماه جاری</button>
                        <button onclick="app.clearKpiFilters()" class="text-xs px-3 py-1.5 rounded bg-red-700 hover:bg-red-600 text-white">پاک‌کردن فیلترها</button>
                    </div>
                </div>

                <div id="kpi-filters-container">
                    <div class="grid grid-cols-1 md:grid-cols-4 gap-3">
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">از تاریخ</label>
                            <input type="text" id="kpi-date-from" class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-white"
                                   value="${dateFromJalali}">
                        </div>
                        <div>
                            <label class="block text-xs text-gray-400 mb-1">تا تاریخ</label>
                            <input type="text" id="kpi-date-to" class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-white"
                                   value="${dateToJalali}">
                        </div>
                        <div>
                            ${this.renderCustomMultiSelect('kpi-owner-filter', 'مسئول (چندانتخابی)', this.getKpiOwnerData(), filters.owner_didar_ids || [])}
                        </div>
                        <div>
                            ${this.renderCustomMultiSelect('kpi-product-filter', 'محصول (چندانتخابی)', this.getKpiProductData(), filters.product_ids || [])}
                        </div>
                    </div>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
                        <div>
                            ${this.renderCustomMultiSelect('kpi-deal-status-filter', 'وضعیت معامله (چندانتخابی)', this.getKpiDealStatusData(), filters.deal_statuses || [])}
                        </div>
                        <div>
                            ${this.renderCustomMultiSelect('kpi-deal-lead-type-filter', 'نوع معامله (چندانتخابی)', this.getKpiDealLeadTypeData(), filters.deal_lead_types || [])}
                        </div>
                    </div>
                    ${this.isAdminLikeForAnalytics() ? `
                    <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-6 gap-3 mt-4">
                        ${this.renderKpiCriteriaSelect('kpi_sales_date_basis', criteria.kpi_sales_date_basis)}
                        ${this.renderKpiCriteriaSelect('kpi_sales_count_rule', criteria.kpi_sales_count_rule)}
                        ${this.renderKpiCriteriaSelect('kpi_payment_date_basis', criteria.kpi_payment_date_basis)}
                        ${this.renderKpiCriteriaSelect('kpi_payment_count_rule', criteria.kpi_payment_count_rule)}
                        ${this.renderKpiCriteriaSelect('kpi_payment_status_scope', criteria.kpi_payment_status_scope)}
                        ${this.renderKpiCriteriaSelect('legacy_deal_scope', criteria.legacy_deal_scope)}
                    </div>` : ''}
                    
                    <div class="mt-4 flex justify-end">
                        <button onclick="app.loadKPIDashboard()" class="bg-blue-600 hover:bg-blue-500 text-white px-6 py-2 rounded-lg font-bold transition">
                            <i class="fa-solid fa-search ml-2"></i> اعمال فیلتر
                        </button>
                    </div>
                </div>

                ${navMenu}
                <div class="mt-4">${activeFilterChips}</div>
            </div>

            <!-- KPI Cards Row 1 -->
            <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
                <!-- Sales Card -->
                <div class="glass p-6 border-r-4 border-blue-500">
                    <div class="text-sm text-gray-400 mb-2">فروش کل</div>
                    <div class="text-3xl font-bold bg-gradient-to-r from-amber-200 to-yellow-500 bg-clip-text text-transparent mb-2">${formatToman(sales.total_collected_amount || 0)} تومان</div>
                    <div class="text-sm text-gray-400">تعداد معامله: ${sales.deal_count || 0}</div>
                    <div class="text-xs text-gray-500 mt-2">مبلغ قرارداد: ${formatToman(sales.total_contract_amount || 0)} تومان</div>
                </div>

                <!-- Target Achievement Card -->
                <div class="glass p-6 border-r-4 ${progressColor}">
                    <div class="text-sm text-gray-400 mb-2">درصد تحقق تارگت</div>
                    <div class="text-3xl font-bold text-white mb-2">${achievementPercent}%</div>
                    ${target.has_target ? `
                        <div class="w-full bg-gray-700 rounded-full h-2.5 mb-2">
                            <div class="${progressColor} h-2.5 rounded-full" style="width: ${Math.min(achievementPercent, 100)}%"></div>
                        </div>
                        <div class="text-xs text-gray-400">
                            هدف: ${formatToman(target.target_amount)} تومان | واقعی: ${formatToman(target.actual_amount)} تومان
                        </div>
                    ` : `
                        <div class="text-xs text-gray-400">هدف تعریف نشده</div>
                    `}
                </div>

                <!-- Outstanding Card -->
                <div class="glass p-6 border-r-4 border-red-500">
                    <div class="text-sm text-gray-400 mb-2">مانده حساب</div>
                    <div class="text-3xl font-bold text-red-400 mb-2">${formatToman(outstanding.outstanding_amount || 0)} تومان</div>
                    <div class="text-sm text-gray-400">تعداد معامله بدهکار: ${outstanding.deal_count || 0}</div>
                    <div class="text-xs text-gray-500 mt-2">قرارداد: ${formatToman(outstanding.total_contract_amount || 0)} تومان | وصول شده: ${formatToman(outstanding.total_collected || 0)} تومان</div>
                </div>
            </div>

            <!-- KPI Cards Row 2 -->
            <div class="grid grid-cols-1 md:grid-cols-6 gap-4 mb-6">
                <div class="glass p-4 border-r-4 border-green-500">
                    <div class="text-sm text-gray-400">تسویه شده کامل</div>
                    <div class="text-xl font-bold text-white">${settled.deal_count || 0}</div>
                    <div class="text-xs text-gray-500 mt-1">مبلغ کل: ${formatToman(settled.total_amount || 0)} تومان</div>
                    <div class="text-xs text-gray-500">وصول شده: ${formatToman(settled.total_collected_amount || 0)} تومان</div>
                </div>
                <div class="glass p-4 border-r-4 border-yellow-500">
                    <div class="text-sm text-gray-400">فروش ناموفق</div>
                    <div class="text-xl font-bold text-white">${this.sumLostSales(lost)}</div>
                    <div class="text-xs text-gray-500 mt-1">مبلغ کل: ${formatToman(this.sumLostSalesAmount(lost))} تومان</div>
                    ${lostReasonSummaryHtml}
                </div>
                <div class="glass p-4 border-r-4 border-emerald-500">
                    <div class="text-sm text-gray-400">فروش موفق (Won)</div>
                    <div class="text-xl font-bold text-white">${wonSales.deal_count || 0}</div>
                    <div class="text-xs text-gray-500 mt-1">مبلغ کل: ${formatToman(wonSales.total_amount || 0)} تومان</div>
                </div>
                <div class="glass p-4 border-r-4 border-purple-500">
                    <div class="text-sm text-gray-400 flex items-center gap-2">
                        فروش جاری
                        <span class="flex h-2 w-2 relative">
                            <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-400 opacity-75"></span>
                            <span class="relative inline-flex rounded-full h-2 w-2 bg-purple-500"></span>
                        </span>
                    </div>
                    <div class="text-xl font-bold text-white">${openPipeline.deal_count || 0}</div>
                    <div class="text-xs text-gray-500 mt-1">پتانسیل: ${formatToman(openPipeline.total_potential_amount || 0)} تومان</div>
                </div>
                <div class="glass p-4 border-r-4 border-pink-500 cursor-pointer hover:border-cyan-400 transition-colors"
                     onclick="app.openConversionCardDrilldown('kpi','deal_conversion_rate')"
                     title="نمایش جزئیات">
                    <div class="text-sm text-gray-400">نرخ تبدیل معاملات</div>
                    <div class="text-xl font-bold text-white">${dealConversion.conversion_rate || 0}%</div>
                    <div class="text-xs text-gray-500 mt-1">از ${dealConversion.total_deals || 0} معامله</div>
                    <div class="text-xs text-gray-500">عدم تبدیل: ${dealConversion.non_conversion_rate || 0}%</div>
                    <div class="text-[10px] text-cyan-300 mt-2">برای مشاهده جزئیات کلیک کنید</div>
                </div>
                <div class="glass p-4 border-r-4 border-blue-500 cursor-pointer hover:border-cyan-400 transition-colors"
                     onclick="app.openConversionCardDrilldown('kpi','lead_conversion_rate')"
                     title="نمایش جزئیات">
                    <div class="text-sm text-gray-400">نرخ تبدیل لید جدید</div>
                    <div class="text-xl font-bold text-white">${leadConversion.conversion_rate || 0}%</div>
                    <div class="text-xs text-gray-500 mt-1">از ${leadConversion.total_new_leads || 0} لید جدید</div>
                    <div class="text-xs text-gray-500">عدم تبدیل: ${leadConversion.non_conversion_rate || 0}%</div>
                    <div class="text-[10px] text-cyan-300 mt-2">برای مشاهده جزئیات کلیک کنید</div>
                </div>
            </div>

            <!-- Deals Without Payments Row -->
            ${dealsWithoutPayments.deal_count > 0 ? `
            <div class="glass p-6 mb-6 border-r-4 border-yellow-500">
                <div class="flex items-center justify-between mb-4">
                    <div>
                        <div class="text-lg font-bold text-yellow-400 mb-2">⚠️ معاملات بدون پرداخت</div>
                        <div class="text-sm text-gray-400">معاملاتی که مبلغ دارند اما هنوز پرداختی ثبت نشده</div>
                    </div>
                    <div class="text-right">
                        <div class="text-3xl font-bold text-yellow-400">${dealsWithoutPayments.deal_count || 0}</div>
                        <div class="text-sm text-gray-400">معامله</div>
                    </div>
                </div>
                <div class="mt-4 pt-4 border-t border-gray-700">
                    <div class="text-sm text-gray-400 mb-1">مجموع مبلغ قابل پرداخت:</div>
                    <div class="text-2xl font-bold text-white">${formatToman(dealsWithoutPayments.total_potential_amount || 0)} تومان</div>
                </div>
            </div>
            ` : ''}

            <!-- Product Sales Counts -->
            <div class="glass p-6 mb-6 border-r-4 border-indigo-500">
                <div class="flex items-center justify-between mb-4">
                    <div>
                        <div class="text-lg font-bold text-indigo-400 mb-1">فروش محصولات (بر اساس پرداخت)</div>
                        <div class="text-sm text-gray-400">تعداد معاملاتی که در بازه انتخاب‌شده حداقل یک پرداخت دارند</div>
                    </div>
                </div>
                ${productSales.length > 0 ? `
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
                        ${productSales.map(item => `
                            <div class="bg-gray-800/50 p-4 rounded flex items-center justify-between">
                                <div class="text-sm text-gray-300">${item.product_name || '-'}</div>
                                <div class="text-xl font-bold text-white">${item.deal_count || 0}</div>
                            </div>
                        `).join('')}
                    </div>
                ` : `
                    <div class="text-sm text-gray-500">داده‌ای برای این بازه وجود ندارد.</div>
                `}
            </div>

            <!-- Charts Row -->
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
                <!-- Daily Sales Chart -->
                <div class="glass p-6">
                    <h3 class="text-lg font-bold mb-4 text-yellow-500">فروش روزانه</h3>
                    <div id="daily-sales-chart" class="h-64"></div>
                    ${this.renderDailySalesChart(kpi.daily_sales || [])}
                </div>

                <!-- Lost Sales Reasons Chart -->
                <div class="glass p-6">
                    <h3 class="text-lg font-bold mb-4 text-yellow-500">دلایل شکست معاملات</h3>
                    <div id="lost-sales-chart" class="h-64"></div>
                    ${this.renderLostSalesChart(lost)}
                </div>
            </div>

            <!-- Lead Conversion Details -->
            <div class="glass p-6 mb-6">
                <h3 class="text-lg font-bold mb-4 text-yellow-500">جزئیات نرخ تبدیل لید جدید</h3>
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                    <div class="bg-gray-800/50 p-4 rounded">
                        <div class="text-sm text-gray-400">لیدهای جدید</div>
                        <div class="text-2xl font-bold text-white">${leadConversion.total_new_leads || 0}</div>
                    </div>
                    <div class="bg-gray-800/50 p-4 rounded">
                        <div class="text-sm text-gray-400">تبدیل شده</div>
                        <div class="text-2xl font-bold text-green-400">${leadConversion.converted_leads || 0}</div>
                    </div>
                    <div class="bg-gray-800/50 p-4 rounded">
                        <div class="text-sm text-gray-400">نرخ عدم تبدیل</div>
                        <div class="text-2xl font-bold text-red-400">${leadConversion.non_conversion_rate || 0}%</div>
                    </div>
                </div>
            </div>

            <!-- Time to Conversion -->
            <div class="glass p-6">
                <h3 class="text-lg font-bold mb-4 text-yellow-500">میانگین زمان تبدیل</h3>
                <div class="text-3xl font-bold text-white mb-2">
                    ${Math.round(timeToConversion.avg_conversion_days || 0)} روز
                </div>
                <div class="text-sm text-gray-400">
                    مبنای محاسبه: از ثبت معامله تا اولین پرداخت ثبت شده
                </div>
            </div>

            <!-- Daily Operational KPI -->
            <div class="glass p-6 mt-6">
                <h3 class="text-lg font-bold mb-4 text-yellow-500">KPI روزانه عملیاتی (کلی و تفکیک کارشناس)</h3>
                ${this.renderDailyOperationalKpi(dailyOperational)}
            </div>

            <!-- Advanced KPI Tables -->
            <div class="glass p-6 mt-6">
                <h3 class="text-lg font-bold mb-4 text-yellow-500">جدول‌های تحلیلی KPI</h3>
                ${this.renderKpiAdvancedTablesSection()}
            </div>

            <!-- Deal Details Table -->
            <div class="glass p-6 mt-6">
                <div class="flex justify-between items-center mb-4">
                    <h3 class="text-lg font-bold text-yellow-500">جزئیات معاملات</h3>
                    <button onclick="app.toggleKpiDealDetails()" class="bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded transition">
                        <i class="fa-solid fa-eye ml-2"></i> ${this.state.showKpiDealDetails ? 'مخفی کن' : 'نمایش'}
                    </button>
                </div>
                <div id="kpi-deal-details-table" class="${this.state.showKpiDealDetails ? '' : 'hidden'}">
                    <div class="overflow-x-auto">
                        <table class="w-full text-xs border-collapse">
                            <thead class="sticky top-0 bg-gray-700">
                                <tr>
                                    <th class="border border-gray-600 p-2 text-right">عنوان معامله</th>
                                    <th class="border border-gray-600 p-2 text-right">مسئول</th>
                                    <th class="border border-gray-600 p-2 text-right">وضعیت</th>
                                    <th class="border border-gray-600 p-2 text-right">مبلغ قرارداد</th>
                                    <th class="border border-gray-600 p-2 text-right">پرداخت شده</th>
                                    <th class="border border-gray-600 p-2 text-right">مانده</th>
                                    <th class="border border-gray-600 p-2 text-right">اولین پرداخت</th>
                                </tr>
                            </thead>
                            <tbody id="kpi-deals-table-body" class="bg-gray-900">
                                <tr class="text-center text-gray-500">
                                    <td colspan="8" class="py-8">در حال بارگذاری...</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <!-- Back to Top Button -->
            <button onclick="app.scrollToKpiTop()" class="fixed bottom-6 left-6 bg-gray-700 hover:bg-gray-600 text-white p-3 rounded-full shadow-xl z-50 transition-opacity duration-300 opacity-80 hover:opacity-100 border border-gray-600">
                <i class="fa-solid fa-arrow-up"></i>
            </button>
        `;

        this.initCustomMultiSelectListeners();
        this.initKpiDatePickers();
    },

    initKpiNativeMultiSelects() {
        // Legacy native select init - keeping for reference or fallback if needed
        const ids = [
            'kpi-owner-filter',
            'kpi-product-filter',
            'kpi-deal-status-filter',
            'kpi-deal-lead-type-filter'
        ];
        ids.forEach((id) => {
            const el = document.getElementById(id);
            if (!el || el.dataset.kpiNativeBound === '1') return;
            // Native select logic removed in favor of custom component
        });
    },

    // ========== Custom Multi-Select Component ==========

    getKpiOwnerData() {
        const options = [{ value: '__all__', label: 'همه مسئول‌ها' }];
        if (this.state.kpiUsers && this.state.kpiUsers.length > 0) {
            // Ensure we are comparing as numbers and handle potential string values
            const activeUsers = this.state.kpiUsers.filter(user => {
                const isActive = user.is_active !== undefined ? Number(user.is_active) : 1;
                return isActive === 1;
            });
            
            activeUsers.forEach(user => {
                const name = user.display_name || user.email || (user.first_name + ' ' + (user.last_name || ''));
                const didarId = user.didar_user_id || user.owner_id || '';
                if (didarId) {
                    options.push({ value: String(didarId), label: name.trim() });
                }
            });
        }
        return options;
    },

    getKpiProductData() {
        const options = [{ value: '__all__', label: 'همه محصولات' }];
        if (this.state.kpiProducts && this.state.kpiProducts.length > 0) {
            this.state.kpiProducts.forEach(product => {
                if (product.id) {
                    options.push({ value: String(product.id), label: product.name || 'محصول' });
                }
            });
        }
        return options;
    },

    getKpiDealStatusData() {
        return [
            { value: 'pending', label: 'جاری' },
            { value: 'won', label: 'موفق' },
            { value: 'lost', label: 'ناموفق' }
        ];
    },

    getKpiDealLeadTypeData() {
        return [
            { value: 'new', label: 'معامله لید جدید' },
            { value: 'old', label: 'معامله لید قدیمی' }
        ];
    },

    renderCustomMultiSelect(id, label, options, selectedValues) {
        // Ensure selectedValues are strings
        const selectedSet = new Set((selectedValues || []).map(String));
        
        // Handle logic for "All" selection
        const isAllSelected = selectedSet.has('__all__') || selectedSet.size === 0;
        
        // Determine display text
        let buttonText = 'انتخاب کنید...';
        if (isAllSelected && options.some(o => o.value === '__all__')) {
            const allLabel = options.find(o => o.value === '__all__')?.label || 'همه';
            buttonText = allLabel;
        } else if (selectedSet.size > 0) {
            // Find labels for selected values (excluding __all__)
            const selectedLabels = options
                .filter(o => o.value !== '__all__' && selectedSet.has(String(o.value)))
                .map(o => o.label);
            
            if (selectedLabels.length === 1) {
                buttonText = selectedLabels[0];
            } else if (selectedLabels.length > 1) {
                buttonText = `${selectedLabels[0]} (+${selectedLabels.length - 1} مورد دیگر)`;
            }
        }

        const optionsHtml = options.map(opt => {
            const isSelected = isAllSelected && opt.value === '__all__' ? true : selectedSet.has(String(opt.value));
            return `
                <label class="flex items-center px-3 py-2.5 hover:bg-gray-800 cursor-pointer border-b border-gray-700/50 last:border-0 transition-colors select-item group" data-label="${opt.label.toLowerCase()}">
                    <div class="relative flex items-center">
                        <input type="checkbox" value="${opt.value}" 
                            class="peer appearance-none w-4 h-4 border border-gray-500 rounded bg-gray-700 checked:bg-blue-600 checked:border-blue-600 focus:ring-2 focus:ring-blue-500/40 transition-all"
                            ${isSelected ? 'checked' : ''}
                            onchange="app.handleCustomMultiSelectChange('${id}', '${opt.value}', this)">
                        <i class="fa-solid fa-check text-white text-[10px] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 peer-checked:opacity-100 transition-opacity pointer-events-none"></i>
                    </div>
                    <span class="text-sm text-gray-300 group-hover:text-white mr-3 select-none truncate transition-colors">${opt.label}</span>
                </label>
            `;
        }).join('');

        return `
            <div class="relative custom-multiselect-container" id="wrapper-${id}" style="z-index: 1200; overflow: visible;">
                <label class="block text-xs text-gray-400 mb-1.5 font-medium px-1">${label}</label>
                <button type="button" 
                    onclick="app.toggleCustomMultiSelect('${id}')"
                    class="relative w-full bg-gray-800 border border-gray-700 rounded-lg py-2.5 px-3 text-white text-right flex justify-between items-center focus:outline-none focus:border-blue-500 hover:border-gray-600 transition-all shadow-sm group">
                    <span id="text-${id}" class="truncate text-sm font-medium text-gray-200 group-hover:text-white transition-colors">${buttonText}</span>
                    <i id="icon-${id}" class="fa-solid fa-chevron-down text-gray-400 text-xs transition-transform duration-300"></i>
                </button>
                
                <div id="dropdown-${id}" class="hidden absolute top-[calc(100%+6px)] left-0 right-0 min-w-[240px] bg-gray-900 border border-gray-600 rounded-lg shadow-2xl ring-1 ring-white/10 flex flex-col overflow-hidden animate-fade-in-down" style="z-index: 10001;">
                    <div class="p-2 border-b border-gray-700 bg-gray-900/95 backdrop-blur sticky top-0 z-10">
                        <div class="relative">
                            <i class="fa-solid fa-search absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 text-xs pointer-events-none"></i>
                            <input type="text" 
                                placeholder="جستجو..." 
                                class="w-full bg-gray-800 border border-gray-700 rounded-md py-1.5 pr-9 pl-2 text-xs text-white placeholder-gray-500 focus:outline-none focus:border-blue-500 focus:bg-gray-700 transition-all"
                                autocomplete="off"
                                onkeyup="app.filterCustomMultiSelect('${id}', this.value)">
                        </div>
                    </div>
                    <div class="max-h-64 overflow-y-auto custom-scrollbar p-1 space-y-0.5">
                        ${optionsHtml}
                    </div>
                </div>
            </div>
        `;
    },

    filterCustomMultiSelect(id, query) {
        const wrapper = document.getElementById(`wrapper-${id}`);
        if (!wrapper) return;
        
        const items = wrapper.querySelectorAll('.select-item');
        const q = (query || '').toLowerCase().trim();
        
        items.forEach(item => {
            const label = item.getAttribute('data-label') || '';
            if (label.includes(q)) {
                item.classList.remove('hidden');
            } else {
                item.classList.add('hidden');
            }
        });
    },

    toggleCustomMultiSelect(id) {
        const dropdown = document.getElementById(`dropdown-${id}`);
        const icon = document.getElementById(`icon-${id}`);
        if (!dropdown) return;
        
        const isHidden = dropdown.classList.contains('hidden');
        
        // Close others
        document.querySelectorAll('[id^="dropdown-"]').forEach(el => {
            if (el.id !== `dropdown-${id}`) el.classList.add('hidden');
        });
        document.querySelectorAll('[id^="icon-"]').forEach(el => {
            if (el.id !== `icon-${id}`) el.classList.remove('rotate-180');
        });
        
        if (isHidden) {
            dropdown.classList.remove('hidden');
            if (icon) icon.classList.add('rotate-180');
            
            // Focus search input
            const searchInput = dropdown.querySelector('input[type="text"]');
            if (searchInput) {
                setTimeout(() => searchInput.focus(), 50);
            }
        } else {
            dropdown.classList.add('hidden');
            if (icon) icon.classList.remove('rotate-180');
        }
    },

    handleCustomMultiSelectChange(id, value, checkbox) {
        const wrapper = document.getElementById(`wrapper-${id}`);
        const checkboxes = wrapper.querySelectorAll('input[type="checkbox"]');
        
        if (value === '__all__') {
            if (checkbox.checked) {
                // Deselect all others
                checkboxes.forEach(cb => {
                    if (cb.value !== '__all__') cb.checked = false;
                });
            } else {
                // Cannot deselect 'all' if it's the only one, or maybe just let it be empty
            }
        } else {
            // If another option is selected, deselect 'all'
            const allCheckbox = wrapper.querySelector('input[value="__all__"]');
            if (allCheckbox && checkbox.checked) {
                allCheckbox.checked = false;
            }
        }

        // Calculate state
        const checked = Array.from(checkboxes).filter(cb => cb.checked);
        const textSpan = document.getElementById(`text-${id}`);
        
        const allChecked = checked.find(cb => cb.value === '__all__');
        
        if (allChecked) {
            // Find label for __all__
             const label = checkbox.value === '__all__' 
                ? checkbox.closest('label').querySelector('span').textContent 
                : wrapper.querySelector('input[value="__all__"]').closest('label').querySelector('span').textContent;
            textSpan.textContent = label;
        } else if (checked.length === 0) {
            textSpan.textContent = 'انتخاب کنید...';
        } else if (checked.length === 1) {
            textSpan.textContent = checked[0].closest('label').querySelector('span').textContent;
        } else {
            const firstLabel = checked[0].closest('label').querySelector('span').textContent;
            textSpan.textContent = `${firstLabel} (+${checked.length - 1} مورد دیگر)`;
        }
    },
    
    getCustomMultiSelectValues(id) {
        const wrapper = document.getElementById(`wrapper-${id}`);
        if (!wrapper) return [];
        const checkboxes = wrapper.querySelectorAll('input[type="checkbox"]:checked');
        const values = Array.from(checkboxes).map(cb => cb.value);
        
        if (values.includes('__all__')) return []; // Return empty array implies "all" in backend logic usually
        return values;
    },

    initCustomMultiSelectListeners() {
        if (this._customMultiSelectListenersInitialized) return;
        
        document.addEventListener('click', (e) => {
            if (!e.target.closest('.custom-multiselect-container')) {
                document.querySelectorAll('[id^="dropdown-"]').forEach(el => {
                    el.classList.add('hidden');
                });
                document.querySelectorAll('[id^="icon-"]').forEach(el => {
                    el.classList.remove('rotate-180');
                });
            }
        });
        
        this._customMultiSelectListenersInitialized = true;
    },

    closeAllCustomMultiSelectDropdowns() {
        document.querySelectorAll('[id^="dropdown-"]').forEach(el => {
            el.classList.add('hidden');
        });
        document.querySelectorAll('[id^="icon-"]').forEach(el => {
            el.classList.remove('rotate-180');
        });
        if (document.activeElement && typeof document.activeElement.blur === 'function') {
            document.activeElement.blur();
        }
    },

    isDidarUiEnabled() {
        return APP_DIDAR_UI_ENABLED;
    },

    showDidarUiDisabledMessage() {
        Swal.fire('غیرفعال', 'ارتباط با دیدار در این نسخه غیرفعال شده است.', 'info');
    },

    applyDidarUiDisabledState() {
        if (this.isDidarUiEnabled()) {
            return;
        }
        document.querySelectorAll('[onclick*="openDidarSearchModal"], [onclick*="syncDidarUsers"]').forEach((element) => {
            element.classList.add('hidden');
            element.setAttribute('aria-hidden', 'true');
        });
        ['modalDidarSearch', 'modalDidarPerson'].forEach((id) => {
            const modal = document.getElementById(id);
            if (modal) {
                modal.classList.add('hidden');
            }
        });
    },

    normalizeKpiMultiSelect(elementId) {
        const element = document.getElementById(elementId);
        if (!element) return;
        const options = Array.from(element.options || []);
        const allOption = options.find(opt => String(opt.value) === '__all__');
        if (!allOption) return;
        const selectedOthers = options.filter(opt => String(opt.value) !== '__all__' && opt.selected);
        if (selectedOthers.length > 0) {
            allOption.selected = false;
        } else {
            allOption.selected = true;
        }
    },

    toggleKpiDealDetails() {
        this.state.showKpiDealDetails = !this.state.showKpiDealDetails;
        if (this.state.showKpiDealDetails) {
            this.loadKpiDealDetails();
        }
    },

    async loadKpiDealDetails() {
        const tbody = document.getElementById('kpi-deals-table-body');
        if (!tbody) return;

        tbody.innerHTML = '<tr class="text-center"><td colspan="8" class="py-8">در حال بارگذاری...</td></tr>';

        const filters = this.state.kpiFilters || {};
        const dateFrom = filters.date_from || this.getFirstDayOfMonth();
        const dateTo = filters.date_to || this.getLastDayOfMonth();
        const ownerId = filters.owner_didar_id || '';
        const productId = filters.product_id || '';
        const ownerIds = Array.isArray(filters.owner_didar_ids) ? filters.owner_didar_ids : (ownerId ? [ownerId] : []);
        const productIds = Array.isArray(filters.product_ids) ? filters.product_ids : (productId ? [productId] : []);
        const dealStatuses = Array.isArray(filters.deal_statuses) ? filters.deal_statuses : [];
        const dealLeadTypes = Array.isArray(filters.deal_lead_types) ? filters.deal_lead_types : [];

        const res = await this.req('get_kpi_deals', {
            date_from: dateFrom,
            date_to: dateTo,
            owner_didar_id: ownerId,
            product_id: productId,
            owner_didar_ids: ownerIds.join(','),
            product_ids: productIds.join(','),
            deal_statuses: dealStatuses.join(','),
            deal_lead_types: dealLeadTypes.join(',')
        });

        if (res.status === 'success') {
            this.renderKpiDealDetailsTable(res.deals || []);
        } else {
            tbody.innerHTML = '<tr class="text-center text-red-400"><td colspan="8">خطا در بارگذاری: ' + (res.message || 'نامشخص') + '</td></tr>';
        }
    },

    renderKpiDealDetailsTable(deals) {
        const tbody = document.getElementById('kpi-deals-table-body');
        if (!tbody) return;

        if (!deals || deals.length === 0) {
            tbody.innerHTML = '<tr class="text-center text-gray-500"><td colspan="8">معامله‌ای در این بازه وجود ندارد</td></tr>';
            return;
        }

        const formatToman = (amount) => {
            const toman = parseFloat(amount) || 0;
            return this.formatNumber(toman);
        };

        tbody.innerHTML = deals.map((deal, index) => {
            const outstanding = (deal.payable_amount || 0) - (deal.collected_amount || 0);
            const firstPaymentDate = deal.first_payment_date ? this.formatKpiDate(deal.first_payment_date.slice(0, 10)) : '-';

            return `
                <tr class="${index % 2 === 0 ? 'bg-gray-800' : 'bg-gray-900'}">
                    <td class="border border-gray-700 p-2 text-right">${deal.title || '-'}</td>
                    <td class="border border-gray-700 p-2 text-right">${deal.owner_name || '-'}</td>
                    <td class="border border-gray-700 p-2 text-right">
                        <span class="px-2 py-1 rounded text-xs ${
                            deal.status === 'Won' ? 'bg-green-600' : 
                            deal.status === 'Lost' ? 'bg-red-600' : 'bg-blue-600'
                        }">
                            ${deal.status === 'Won' ? 'موفق' : deal.status === 'Lost' ? 'ناموفق' : 'جاری'}
                        </span>
                    </td>
                    <td class="border border-gray-700 p-2 text-right">${formatToman(deal.payable_amount)}</td>
                    <td class="border border-gray-700 p-2 text-right text-green-400">${formatToman(deal.collected_amount || 0)}</td>
                    <td class="border border-gray-700 p-2 text-right ${outstanding > 0 ? 'text-red-400' : ''}">${formatToman(outstanding)}</td>
                    <td class="border border-gray-700 p-2 text-right">${firstPaymentDate}</td>
                </tr>
            `;
        }).join('');
    },

    renderDailySalesChart(dailySales) {
        if (!dailySales || dailySales.length === 0) {
            return '<div class="text-center text-gray-500 py-8">داده‌ای برای نمایش وجود ندارد</div>';
        }

        const amounts = dailySales.map(d => (parseFloat(d.collected_amount) || 0));
        const maxAmount = Math.max(...amounts, 0);
        const summary = dailySales.map(d => {
            const amount = (parseFloat(d.collected_amount) || 0);
            return `${d.date}: ${this.formatNumber(amount)} تومان`;
        }).join('<br>');

        return `
            <div class="flex items-end space-x-1 h-48 overflow-x-auto pb-2">
                ${dailySales.map(d => {
                    const amount = (parseFloat(d.collected_amount) || 0);
                    const height = maxAmount > 0 ? (amount / maxAmount * 100) : 0;
                    const displayAmount = this.formatNumber(amount);

                    return `
                        <div class="flex-shrink-0 flex flex-col items-center">
                            <div class="w-8 bg-blue-500 rounded-t hover:bg-blue-400 transition cursor-pointer"
                                 style="height: ${Math.max(height, 6)}%"
                                 title="${d.date}: ${displayAmount} تومان">
                            </div>
                            <div class="text-xs text-gray-500 mt-1 transform -rotate-45 origin-left">${d.date.slice(5)}</div>
                        </div>
                    `;
                }).join('')}
            </div>
            <div class="text-xs text-gray-400 mt-3 leading-6">${summary}</div>
        `;
    },

    renderLostSalesChart(lostSales) {
        if (!lostSales || lostSales.length === 0) {
            return '<div class="text-center text-gray-500 py-8">فروش ناموفقی در این بازه وجود ندارد</div>';
        }

        const total = lostSales.reduce((sum, item) => sum + Number(item.total_lost || 0), 0);

        return `
            <div class="space-y-3">
                ${lostSales.map(item => {
                    const count = item.total_lost || 0;
                    const percent = total > 0 ? (count / total * 100) : 0;
                    const reason = item.failure_reason_text || item.failure_reason_code || 'نامشخص';

                    return `
                        <div>
                            <div class="flex justify-between text-sm mb-1">
                                <span class="text-gray-300">${reason}</span>
                                <span class="text-white">${count} (${percent.toFixed(1)}%)</span>
                            </div>
                            <div class="w-full bg-gray-700 rounded-full h-2">
                                <div class="bg-red-500 h-2 rounded-full" style="width: ${percent}%"></div>
                            </div>
                        </div>
                    `;
                }).join('')}
            </div>
        `;
    },

    renderDailyOperationalKpi(dailyOperational) {
        const tables = {
            overall_daily: Array.isArray(dailyOperational.overall_daily) ? dailyOperational.overall_daily : [],
            by_expert_daily: Array.isArray(dailyOperational.by_expert_daily) ? dailyOperational.by_expert_daily : [],
            activity_type_by_expert_daily: Array.isArray(dailyOperational.activity_type_by_expert_daily) ? dailyOperational.activity_type_by_expert_daily : [],
            new_leads_activity_by_expert_daily: Array.isArray(dailyOperational.new_leads_activity_by_expert_daily) ? dailyOperational.new_leads_activity_by_expert_daily : []
        };

        if (Object.values(tables).every(rows => rows.length === 0)) {
            return '<div class="text-center text-gray-500 py-6">داده‌ای برای KPI عملیاتی روزانه در این بازه وجود ندارد.</div>';
        }

        this.state.kpiDailyOperationalTables = tables;
        this.ensureKpiDailyOperationalUiState();
        return this.renderKpiDailyOperationalSection();
    },

    ensureKpiDailyOperationalUiState() {
        if (!this.state.kpiDailyOperationalUi) {
            this.state.kpiDailyOperationalUi = {};
        }
        const defs = this.getKpiDailyOperationalDefinitions();
        Object.keys(defs).forEach((tableId) => {
            const def = defs[tableId];
            const existing = this.state.kpiDailyOperationalUi[tableId] || {};
            const visibleColumns = existing.visibleColumns || {};
            def.columns.forEach((col) => {
                if (visibleColumns[col.key] === undefined) {
                    visibleColumns[col.key] = true;
                }
            });
            this.state.kpiDailyOperationalUi[tableId] = {
                search: existing.search || '',
                sortKey: existing.sortKey || def.defaultSortKey || 'date',
                sortDir: existing.sortDir || 'asc',
                hideZeroRows: !!existing.hideZeroRows,
                visibleColumns
            };
        });
    },

    getKpiDailyOperationalDefinitions() {
        return {
            overall_daily: {
                title: 'نمای کلی روزانه',
                rowTotalUnit: 'count',
                defaultSortKey: 'date',
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'new_leads_count', label: 'لید جدید', type: 'count', summable: true },
                    { key: 'new_deals_for_existing_leads_count', label: 'معامله جدید برای لید قبلی', type: 'count', summable: true },
                    { key: 'activities_count', label: 'کل فعالیت‌ها', type: 'count', summable: true },
                    { key: 'new_leads_with_activity_count', label: 'لید جدید با فعالیت', type: 'count', summable: true },
                    { key: 'leads_created_with_payment_count', label: 'لید ثبت‌شده با پرداخت', type: 'count', summable: true },
                    { key: 'deals_created_with_payment_count', label: 'معامله ثبت‌شده با پرداخت', type: 'count', summable: true },
                    { key: 'leads_without_activity_count', label: 'لید بدون فعالیت', type: 'count', summable: true },
                    { key: 'deals_without_activity_count', label: 'معامله بدون فعالیت', type: 'count', summable: true }
                ]
            },
            by_expert_daily: {
                title: 'تفکیک روزانه بر اساس کارشناس',
                rowTotalUnit: 'count',
                defaultSortKey: 'date',
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'new_leads_count', label: 'لید جدید', type: 'count', summable: true },
                    { key: 'new_deals_for_existing_leads_count', label: 'معامله جدید برای لید قبلی', type: 'count', summable: true },
                    { key: 'activities_count', label: 'کل فعالیت‌ها', type: 'count', summable: true },
                    { key: 'new_leads_with_activity_count', label: 'لید جدید با فعالیت', type: 'count', summable: true },
                    { key: 'leads_created_with_payment_count', label: 'لید ثبت‌شده با پرداخت', type: 'count', summable: true },
                    { key: 'deals_created_with_payment_count', label: 'معامله ثبت‌شده با پرداخت', type: 'count', summable: true },
                    { key: 'leads_without_activity_count', label: 'لید بدون فعالیت', type: 'count', summable: true },
                    { key: 'deals_without_activity_count', label: 'معامله بدون فعالیت', type: 'count', summable: true }
                ]
            },
            activity_type_by_expert_daily: {
                title: 'نوع فعالیت روزانه به تفکیک کارشناس',
                rowTotalUnit: 'count',
                defaultSortKey: 'report_date',
                columns: [
                    { key: 'report_date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'activity_type', label: 'نوع فعالیت', type: 'text' },
                    { key: 'total_count', label: 'تعداد', type: 'count', summable: true }
                ]
            },
            new_leads_activity_by_expert_daily: {
                title: 'لیدهای جدید با فعالیت در همان روز (نوع فعالیت + کارشناس)',
                rowTotalUnit: 'count',
                defaultSortKey: 'report_date',
                columns: [
                    { key: 'report_date', label: 'تاریخ', type: 'date' },
                    { key: 'lead_owner_name', label: 'مسئول لید', type: 'text' },
                    { key: 'activity_owner_name', label: 'کارشناس فعالیت', type: 'text' },
                    { key: 'activity_type', label: 'نوع فعالیت', type: 'text' },
                    { key: 'leads_count', label: 'تعداد لید', type: 'count', summable: true },
                    { key: 'activity_count', label: 'تعداد فعالیت', type: 'count', summable: true }
                ]
            }
        };
    },

    renderKpiDailyOperationalSection() {
        const defs = this.getKpiDailyOperationalDefinitions();
        const tableIds = Object.keys(defs).filter((tableId) => {
            const rows = this.state.kpiDailyOperationalTables?.[tableId];
            return Array.isArray(rows) && rows.length > 0;
        });
        if (tableIds.length === 0) {
            return '<div class="text-center text-gray-500 py-6">داده‌ای برای KPI عملیاتی روزانه وجود ندارد.</div>';
        }

        const panels = tableIds
            .map(tableId => `<div id="kpi-daily-panel-${tableId}" class="mt-4">${this.renderKpiDailyOperationalPanel(tableId)}</div>`)
            .join('');

        return `
            <div class="text-xs text-gray-400 mb-3">
                این جداول تعاملی هستند: مرتب‌سازی، فیلتر داخلی، مخفی/نمایش ستون، مخفی‌سازی ردیف‌های صفر و خروجی CSV.
            </div>
            <div class="mb-4 flex justify-end">
                <button onclick="app.exportAllKpiDailyOperationalTables()" class="bg-green-700 hover:bg-green-600 text-white px-4 py-2 rounded text-sm">
                    خروجی همه جدول‌های روزانه (CSV)
                </button>
            </div>
            ${panels}
        `;
    },

    renderKpiDailyOperationalPanel(tableId) {
        const defs = this.getKpiDailyOperationalDefinitions();
        const definition = defs[tableId];
        if (!definition) return '';
        const rows = Array.isArray(this.state.kpiDailyOperationalTables?.[tableId]) ? this.state.kpiDailyOperationalTables[tableId] : [];
        const ui = this.state.kpiDailyOperationalUi?.[tableId] || { search: '', hideZeroRows: false };
        const visibleColumns = this.getKpiDailyOperationalVisibleColumns(tableId);

        const columnToggles = definition.columns.map(col => `
            <label class="inline-flex items-center ml-3 mb-2 text-xs text-gray-300">
                <input type="checkbox" ${visibleColumns.some(v => v.key === col.key) ? 'checked' : ''}
                    onchange="app.toggleKpiDailyOperationalColumn('${tableId}','${col.key}', this.checked)" class="ml-1">
                ${col.label}
            </label>
        `).join('');

        return `
            <div class="border border-gray-700 rounded-lg p-4">
                <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3 mb-3">
                    <div class="text-sm font-bold text-blue-300">${definition.title}</div>
                    <button onclick="app.exportKpiDailyOperationalCsv('${tableId}')" class="bg-blue-700 hover:bg-blue-600 text-white px-3 py-2 rounded text-xs">
                        خروجی CSV
                    </button>
                </div>
                <div class="grid grid-cols-1 md:grid-cols-3 gap-3 mb-3">
                    <input type="text" value="${this.escapeHtml(ui.search || '')}" oninput="app.setKpiDailyOperationalSearch('${tableId}', this.value)"
                        class="bg-gray-800 border border-gray-700 rounded p-2 text-sm text-white" placeholder="فیلتر داخلی جدول...">
                    <label class="inline-flex items-center text-xs text-gray-300 bg-gray-800 border border-gray-700 rounded p-2">
                        <input type="checkbox" class="ml-2" ${ui.hideZeroRows ? 'checked' : ''} onchange="app.toggleKpiDailyOperationalHideZeroRows('${tableId}', this.checked)">
                        مخفی‌کردن ردیف‌های با جمع صفر
                    </label>
                    <div class="bg-gray-800 border border-gray-700 rounded p-2 text-xs">${columnToggles}</div>
                </div>
                ${rows.length === 0 ? '<div class="text-xs text-gray-500">رکوردی در این جدول وجود ندارد.</div>' : this.renderKpiDailyOperationalGrid(tableId)}
            </div>
        `;
    },

    getKpiDailyOperationalVisibleColumns(tableId) {
        const defs = this.getKpiDailyOperationalDefinitions();
        const definition = defs[tableId];
        const ui = this.state.kpiDailyOperationalUi?.[tableId] || {};
        if (!definition) return [];
        return definition.columns.filter(col => ui.visibleColumns?.[col.key] !== false);
    },

    getKpiDailyOperationalProcessedRows(tableId) {
        const defs = this.getKpiDailyOperationalDefinitions();
        const definition = defs[tableId];
        const ui = this.state.kpiDailyOperationalUi?.[tableId] || {};
        const rows = Array.isArray(this.state.kpiDailyOperationalTables?.[tableId]) ? [...this.state.kpiDailyOperationalTables[tableId]] : [];
        if (!definition) return [];

        const search = (ui.search || '').trim().toLowerCase();
        let filtered = rows;
        if (search) {
            filtered = filtered.filter(row => {
                return definition.columns.some(col => String(row[col.key] ?? '').toLowerCase().includes(search));
            });
        }

        if (ui.hideZeroRows) {
            const sumCols = definition.columns.filter(c => c.summable);
            filtered = filtered.filter(row => {
                const rowSum = sumCols.reduce((sum, c) => sum + Number(row[c.key] || 0), 0);
                return rowSum > 0;
            });
        }

        const sortKey = ui.sortKey || definition.defaultSortKey || 'date';
        const sortDir = ui.sortDir === 'desc' ? -1 : 1;
        const sortCol = definition.columns.find(c => c.key === sortKey);
        filtered.sort((a, b) => {
            const va = a[sortKey];
            const vb = b[sortKey];
            if (sortCol && ['count', 'amount', 'percent', 'day'].includes(sortCol.type)) {
                return ((Number(va || 0) - Number(vb || 0)) * sortDir);
            }
            return String(va ?? '').localeCompare(String(vb ?? ''), 'fa', { numeric: true }) * sortDir;
        });

        if (definition.showOwnerTotals) {
            filtered = this.injectKpiAdvancedOwnerTotals(filtered, definition);
        }

        return filtered;
    },

    injectKpiAdvancedOwnerTotals(rows, definition) {
        if (!Array.isArray(rows) || rows.length === 0) return rows;
        const preparedRows = rows.map((row) => {
            const next = { ...row };
            if (definition?.title === '5) وضعیت نرخ تبدیل') {
                next.owner_range_conversion_rate = null;
            }
            return next;
        });
        const output = [...preparedRows];
        const byOwner = {};

        preparedRows.forEach((row) => {
            const ownerKey = `${row.owner_didar_id || ''}__${row.owner_name || ''}`;
            if (!byOwner[ownerKey]) {
                byOwner[ownerKey] = [];
            }
            byOwner[ownerKey].push(row);
        });

        const ownerTotals = {};
        Object.keys(byOwner).forEach((ownerKey) => {
            const ownerRows = byOwner[ownerKey];
            const totalRow = {
                date: 'جمع کارشناس',
                owner_name: `${ownerRows[0].owner_name || '-'} (جمع)`,
                owner_didar_id: ownerRows[0].owner_didar_id || '',
                __owner_total__: true
            };

            definition.columns.forEach((col) => {
                if (col.summable) {
                    totalRow[col.key] = ownerRows.reduce((sum, row) => sum + Number(row[col.key] || 0), 0);
                } else if (col.aggregation === 'rate') {
                    const num = ownerRows.reduce((sum, row) => sum + Number(row[col.numeratorKey] || 0), 0);
                    const den = ownerRows.reduce((sum, row) => sum + Number(row[col.denominatorKey] || 0), 0);
                    totalRow[col.key] = den !== 0 ? (num / den) * 100 : 0;
                } else if (col.aggregation === 'rate_inverse') {
                    const num = ownerRows.reduce((sum, row) => sum + Number(row[col.numeratorKey] || 0), 0);
                    const den = ownerRows.reduce((sum, row) => sum + Number(row[col.denominatorKey] || 0), 0);
                    totalRow[col.key] = den !== 0 ? ((den - num) / den) * 100 : 0;
                } else if (col.aggregation === 'average') {
                    const sumVal = ownerRows.reduce((sum, row) => sum + Number(row[col.sumKey] || 0), 0);
                    const countVal = ownerRows.reduce((sum, row) => sum + Number(row[col.countKey] || 0), 0);
                    totalRow[col.key] = countVal !== 0 ? (sumVal / countVal) : 0;
                }
            });
            if (definition?.title === '5) وضعیت نرخ تبدیل') {
                const ownerNum = ownerRows.reduce((sum, row) => sum + Number(row.sales_total_count || 0), 0);
                const ownerDen = ownerRows.reduce((sum, row) => sum + Number(row.total_deals_count || 0), 0);
                totalRow.owner_range_conversion_rate = ownerDen !== 0 ? (ownerNum / ownerDen) * 100 : 0;
            }
            ownerTotals[ownerKey] = totalRow;
        });

        Object.keys(ownerTotals).forEach((ownerKey) => {
            output.push(ownerTotals[ownerKey]);
        });

        return output;
    },

    renderKpiDailyOperationalGrid(tableId) {
        const defs = this.getKpiDailyOperationalDefinitions();
        const definition = defs[tableId];
        if (!definition) return '';

        const rows = this.getKpiDailyOperationalProcessedRows(tableId);
        const visibleColumns = this.getKpiDailyOperationalVisibleColumns(tableId);
        const ui = this.state.kpiDailyOperationalUi?.[tableId] || {};
        const totals = {};
        
        // Calculate max values for heatmap
        const maxValues = {};
        visibleColumns.forEach(col => {
            if (['count', 'amount'].includes(col.type)) {
                maxValues[col.key] = Math.max(...rows.map(r => Number(r[col.key] || 0)), 0);
            }
            if (col.summable) {
                totals[col.key] = rows.reduce((sum, row) => sum + Number(row[col.key] || 0), 0);
            }
        });

        const hasRowTotal = !!definition.rowTotalUnit;
        
        const headerHtml = visibleColumns.map((col, index) => {
            const sortIcon = ui.sortKey === col.key ? (ui.sortDir === 'asc' ? '▲' : '▼') : '';
            // Sticky logic for first column
            const stickyClass = index === 0 ? 'sticky right-0 z-10 bg-gray-800 border-l border-gray-600 shadow-md' : '';
            return `<th class="border border-gray-700 p-2 cursor-pointer select-none ${stickyClass}" onclick="app.setKpiDailyOperationalSort('${tableId}','${col.key}')">${col.label} ${sortIcon}</th>`;
        }).join('');

        const rowTotalFor = (row) => {
            if (!hasRowTotal) return 0;
            return visibleColumns
                .filter(c => c.summable && c.type === definition.rowTotalUnit)
                .reduce((sum, c) => sum + Number(row[c.key] || 0), 0);
        };

        const bodyHtml = rows.map(row => {
            const cells = visibleColumns.map((col, index) => {
                // Heatmap style
                let style = '';
                if (['count', 'amount'].includes(col.type) && maxValues[col.key] > 0) {
                    const val = Number(row[col.key] || 0);
                    const ratio = val / maxValues[col.key];
                    if (ratio > 0) {
                        const alpha = Math.min(ratio * 0.4, 0.4); 
                        style = `background-color: rgba(16, 185, 129, ${alpha});`;
                    }
                }
                
                // Sticky logic
                // Ensure bg-gray-900 is set if no heatmap, to cover scrolled content
                const stickyClass = index === 0 ? 'sticky right-0 z-10 bg-gray-900 border-l border-gray-700 shadow-md' : '';
                
                return `<td class="border border-gray-700 p-2 ${stickyClass}" style="${style}">${this.formatKpiAdvancedCell(row[col.key], col.type)}</td>`;
            }).join('');
            
            const totalCell = hasRowTotal ? `<td class="border border-gray-700 p-2 font-bold">${this.formatKpiAdvancedCell(rowTotalFor(row), definition.rowTotalUnit)}</td>` : '';
            return `<tr class="hover:bg-gray-800/70 transition-colors">${cells}${totalCell}</tr>`;
        }).join('');

        const footerCells = visibleColumns.map((col, index) => {
            const stickyClass = index === 0 ? 'sticky right-0 z-10 bg-gray-800 border-l border-gray-600 shadow-md' : '';
            if (index === 0) return `<td class="border border-gray-700 p-2 font-bold ${stickyClass}">جمع</td>`;
            if (col.summable) return `<td class="border border-gray-700 p-2 font-bold">${this.formatKpiAdvancedCell(totals[col.key] || 0, col.type)}</td>`;
            return '<td class="border border-gray-700 p-2">-</td>';
        }).join('');
        const footerTotal = hasRowTotal
            ? `<td class="border border-gray-700 p-2 font-bold">${this.formatKpiAdvancedCell(
                Object.keys(totals).reduce((sum, key) => {
                    const col = visibleColumns.find(c => c.key === key);
                    return (col && col.type === definition.rowTotalUnit) ? (sum + Number(totals[key] || 0)) : sum;
                }, 0),
                definition.rowTotalUnit
            )}</td>`
            : '';

        return `
            <div class="overflow-x-auto rounded-lg custom-scrollbar">
                <table class="w-full text-xs border-collapse">
                    <thead class="bg-gray-800"><tr>${headerHtml}${hasRowTotal ? '<th class="border border-gray-700 p-2">جمع ردیف</th>' : ''}</tr></thead>
                    <tbody class="bg-gray-900">${bodyHtml || `<tr><td colspan="${visibleColumns.length + (hasRowTotal ? 1 : 0)}" class="p-3 text-center text-gray-500">رکوردی مطابق فیلتر وجود ندارد.</td></tr>`}</tbody>
                    <tfoot class="bg-gray-800/70"><tr>${footerCells}${footerTotal}</tr></tfoot>
                </table>
            </div>
        `;
    },

    setKpiDailyOperationalSearch(tableId, value) {
        if (!this.state.kpiDailyOperationalUi?.[tableId]) return;
        this.state.kpiDailyOperationalUi[tableId].search = value || '';
        this.refreshKpiDailyOperationalPanel(tableId);
    },

    setKpiDailyOperationalSort(tableId, sortKey) {
        const ui = this.state.kpiDailyOperationalUi?.[tableId];
        if (!ui) return;
        if (ui.sortKey === sortKey) {
            ui.sortDir = ui.sortDir === 'asc' ? 'desc' : 'asc';
        } else {
            ui.sortKey = sortKey;
            ui.sortDir = 'asc';
        }
        this.refreshKpiDailyOperationalPanel(tableId);
    },

    toggleKpiDailyOperationalColumn(tableId, colKey, checked) {
        if (!this.state.kpiDailyOperationalUi?.[tableId]) return;
        this.state.kpiDailyOperationalUi[tableId].visibleColumns[colKey] = !!checked;
        this.refreshKpiDailyOperationalPanel(tableId);
    },

    toggleKpiDailyOperationalHideZeroRows(tableId, checked) {
        if (!this.state.kpiDailyOperationalUi?.[tableId]) return;
        this.state.kpiDailyOperationalUi[tableId].hideZeroRows = !!checked;
        this.refreshKpiDailyOperationalPanel(tableId);
    },

    refreshKpiDailyOperationalPanel(tableId) {
        const panel = document.getElementById(`kpi-daily-panel-${tableId}`);
        if (!panel) return;
        panel.innerHTML = this.renderKpiDailyOperationalPanel(tableId);
    },

    exportKpiDailyOperationalCsv(tableId) {
        const defs = this.getKpiDailyOperationalDefinitions();
        const definition = defs[tableId];
        if (!definition) return;
        const visibleColumns = this.getKpiDailyOperationalVisibleColumns(tableId);
        const rows = this.getKpiDailyOperationalProcessedRows(tableId);
        const hasRowTotal = !!definition.rowTotalUnit;
        const headers = visibleColumns.map(c => c.label);
        if (hasRowTotal) headers.push('جمع ردیف');
        const lines = [headers.join(',')];

        rows.forEach(row => {
            const cells = visibleColumns.map(col => this.csvEscape(this.formatKpiAdvancedCell(row[col.key], col.type)));
            if (hasRowTotal) {
                const rowTotal = visibleColumns
                    .filter(c => c.summable && c.type === definition.rowTotalUnit)
                    .reduce((sum, c) => sum + Number(row[c.key] || 0), 0);
                cells.push(this.csvEscape(this.formatKpiAdvancedCell(rowTotal, definition.rowTotalUnit)));
            }
            lines.push(cells.join(','));
        });

        const csvText = "\uFEFF" + lines.join('\n');
        const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `kpi_daily_${tableId}_${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(link.href);
    },

    exportAllKpiDailyOperationalTables() {
        const defs = this.getKpiDailyOperationalDefinitions();
        const tableIds = Object.keys(defs).filter((tableId) => {
            const rows = this.state.kpiDailyOperationalTables?.[tableId];
            return Array.isArray(rows) && rows.length > 0;
        });
        tableIds.forEach((tableId, index) => {
            setTimeout(() => this.exportKpiDailyOperationalCsv(tableId), index * 250);
        });
    },

    ensureKpiAdvancedUiState() {
        const tables = this.state.kpiAdvancedTables || {};
        if (!this.state.kpiAdvancedUi) {
            this.state.kpiAdvancedUi = {};
        }
        if (!this.state.kpiAdvancedVisibleTables) {
            this.state.kpiAdvancedVisibleTables = {};
        }

        Object.keys(tables).forEach((tableId) => {
            const definition = this.getKpiAdvancedTableDefinition(tableId);
            if (!definition) return;
            const existing = this.state.kpiAdvancedUi[tableId] || {};
            const visibleColumns = existing.visibleColumns || {};
            definition.columns.forEach((col) => {
                if (visibleColumns[col.key] === undefined) {
                    visibleColumns[col.key] = col.defaultVisible !== false;
                }
            });
            this.state.kpiAdvancedUi[tableId] = {
                search: existing.search || '',
                sortKey: existing.sortKey || definition.defaultSortKey || 'date',
                sortDir: existing.sortDir || 'asc',
                hideZeroRows: !!existing.hideZeroRows,
                visibleColumns
            };
            if (this.state.kpiAdvancedVisibleTables[tableId] === undefined) {
                this.state.kpiAdvancedVisibleTables[tableId] = true;
            }
        });

        Object.keys(this.state.kpiAdvancedVisibleTables).forEach((tableId) => {
            if (!tables[tableId]) {
                delete this.state.kpiAdvancedVisibleTables[tableId];
            }
        });
    },

    getKpiAdvancedTableDefinition(tableId) {
        const defs = {
            summary_by_user: {
                title: 'خلاصه عملکرد کارشناسان',
                rowTotalUnit: null,
                defaultSortKey: 'conversion_rate_total',
                description: {
                    definition: 'این جدول یک نمای کلی از عملکرد هر کارشناس در بازه انتخابی می‌دهد؛ یعنی چند معامله داشته، چند فروش واقعی ثبت کرده، چقدر وصولی داشته و چقدر مانده دارد.',
                    source: 'اطلاعات معاملات، پرداخت‌ها، لیدها و مسئول هر رکورد که داخل همین سیستم ثبت شده‌اند.',
                    formula: 'هر معامله‌ای که حداقل یک پرداخت داشته باشد «فروش» حساب می‌شود. نرخ تبدیل هم از تقسیم تعداد فروش بر کل معاملات همان کارشناس به‌دست می‌آید.',
                    goal: 'برای مقایسه عملکرد کارشناسان، تشخیص افراد با پیگیری بهتر، و تصمیم‌گیری برای بهبود فروش استفاده می‌شود.'
                },
                columns: [
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'total_deals_count', label: 'کل معاملات', type: 'count', summable: true },
                    { key: 'sales_total_count', label: 'تعداد فروش', type: 'count', summable: true },
                    { key: 'conversion_rate_total', label: 'نرخ تبدیل کل', type: 'percent', aggregation: 'rate', numeratorKey: 'sales_total_count', denominatorKey: 'total_deals_count' },
                    { key: 'total_new_deals_count', label: 'معاملات لید جدید', type: 'count', summable: true },
                    { key: 'sales_new_count', label: 'فروش لید جدید', type: 'count', summable: true },
                    { key: 'conversion_rate_new', label: 'نرخ تبدیل لید جدید', type: 'percent', aggregation: 'rate', numeratorKey: 'sales_new_count', denominatorKey: 'total_new_deals_count' },
                    { key: 'total_collected_amount', label: 'جمع دریافتی', type: 'amount', summable: true },
                    { key: 'outstanding_amount_sum', label: 'جمع مانده', type: 'amount', summable: true }
                ]
            },
            deal_status: {
                title: '1) وضعیت معاملات',
                rowTotalUnit: 'count',
                rowTotalColumns: ['deal_total'],
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول نشان می‌دهد در هر روز برای هر کارشناس چند معامله ثبت شده و این معاملات در چه وضعیتی هستند: جاری، دارای فروش، بدون فروش و همچنین وضعیت سیستمی (موفق/ناموفق).',
                    source: 'داده‌های معاملات، اطلاعات لید مربوط به هر معامله، اطلاعات مسئول معامله و پرداخت‌های ثبت‌شده داخل سیستم.',
                    formula: 'اگر برای یک معامله حداقل یک پرداخت ثبت شده باشد «دارای فروش» است. اگر هیچ پرداختی نداشته باشد «بدون فروش» است. جدید یا قدیمی بودن هم از مقایسه تاریخ ثبت لید با تاریخ ثبت معامله محاسبه می‌شود.',
                    goal: 'این جدول کمک می‌کند بفهمیم حجم واقعی فروش از کدام بخش می‌آید و کدام معاملات هنوز به فروش نرسیده‌اند.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'deal_total', label: 'کل معاملات', type: 'count', summable: true },
                    { key: 'deal_new_total', label: 'کل معاملات لید جدید', type: 'count', summable: true },
                    { key: 'deal_old_total', label: 'کل معاملات لید قدیمی', type: 'count', summable: true },
                    { key: 'deal_pending_total', label: 'کل معاملات جاری', type: 'count', summable: true },
                    { key: 'deal_pending_new', label: 'جاری لید جدید', type: 'count', summable: true },
                    { key: 'deal_pending_old', label: 'جاری لید قدیمی', type: 'count', summable: true },
                    { key: 'deal_with_sales_total', label: 'کل معاملات دارای فروش', type: 'count', summable: true },
                    { key: 'deal_with_sales_new', label: 'دارای فروش لید جدید', type: 'count', summable: true },
                    { key: 'deal_with_sales_old', label: 'دارای فروش لید قدیمی', type: 'count', summable: true },
                    { key: 'deal_without_sales_total', label: 'کل معاملات بدون فروش', type: 'count', summable: true },
                    { key: 'deal_without_sales_new', label: 'بدون فروش لید جدید', type: 'count', summable: true },
                    { key: 'deal_without_sales_old', label: 'بدون فروش لید قدیمی', type: 'count', summable: true },
                    { key: 'deal_won_total', label: 'کل معاملات موفق (سیستمی)', type: 'count', summable: true, defaultVisible: false },
                    { key: 'deal_won_new', label: 'موفق لید جدید (سیستمی)', type: 'count', summable: true, defaultVisible: false },
                    { key: 'deal_won_old', label: 'موفق لید قدیمی (سیستمی)', type: 'count', summable: true, defaultVisible: false },
                    { key: 'deal_lost_total', label: 'کل معاملات ناموفق (سیستمی)', type: 'count', summable: true, defaultVisible: false },
                    { key: 'deal_lost_new', label: 'ناموفق لید جدید (سیستمی)', type: 'count', summable: true, defaultVisible: false },
                    { key: 'deal_lost_old', label: 'ناموفق لید قدیمی (سیستمی)', type: 'count', summable: true, defaultVisible: false }
                ]
            },
            lead_status: {
                title: '2) وضعیت لیدها',
                rowTotalUnit: 'count',
                rowTotalColumns: ['total_leads'],
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول وضعیت لیدهای ثبت‌شده هر روز را با زبان فروش نشان می‌دهد: لید دارای فروش، لید بدون فروش، فروش جدید و فروش قبلی.',
                    source: 'اطلاعات لیدها، مسئول لید، تاریخ اولین پرداخت هر لید و وضعیت داشتن یا نداشتن معامله جاری.',
                    formula: 'اگر لید هر پرداختی داشته باشد «دارای فروش» است. اگر اولین پرداخت دقیقاً در همان روز ثبت لید باشد «فروش جدید» است. اگر اولین پرداخت قبل از روز ثبت لید باشد «فروش قبلی» است. لید بدون هیچ پرداختی، «بدون فروش» حساب می‌شود.',
                    goal: 'کمک می‌کند دقیق ببینیم رشد واقعی فروش روزانه از لیدهای تازه بوده یا از لیدهایی که از قبل به فروش رسیده بودند.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'total_leads', label: 'تعداد کل لیدها', type: 'count', summable: true },
                    { key: 'leads_with_sales', label: 'لید دارای فروش', type: 'count', summable: true },
                    { key: 'leads_with_new_sales', label: 'فروش جدید', type: 'count', summable: true },
                    { key: 'leads_with_previous_sales', label: 'لید با فروش قبلی', type: 'count', summable: true },
                    { key: 'leads_without_sales', label: 'لید بدون فروش', type: 'count', summable: true },
                    { key: 'leads_without_sales_with_current_deal', label: 'بدون فروش با معامله جاری', type: 'count', summable: true },
                    { key: 'leads_without_sales_without_current_deal', label: 'بدون فروش بدون معامله جاری', type: 'count', summable: true },
                    { key: 'new_leads_count', label: 'تعداد لید جدید', type: 'count', summable: true }
                ]
            },
            payment_mix: {
                title: '3) پرداخت‌ها',
                rowTotalUnit: 'count',
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول نشان می‌دهد لیدهای جدید و قدیمی در هر روز در چه وضعیتی از نظر پرداخت هستند: با پرداخت، بدون پرداخت یا شکست‌خورده.',
                    source: 'اطلاعات معاملات همان روز، وضعیت پرداخت هر معامله و نوع لید (جدید/قدیمی).',
                    formula: 'برای لیدهایی که معامله دارند، اگر پرداخت ثبت شده باشد «با پرداخت» و اگر ثبت نشده باشد «بدون پرداخت» در نظر گرفته می‌شوند.',
                    goal: 'برای پیدا کردن لیدهایی که هنوز ظرفیت تبدیل به فروش دارند و باید سریع‌تر پیگیری شوند.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'new_leads_with_payment', label: 'لید جدید با پرداخت', type: 'count', summable: true },
                    { key: 'new_leads_without_payment', label: 'لید جدید بدون پرداخت', type: 'count', summable: true },
                    { key: 'new_leads_failed', label: 'لید جدید ناموفق', type: 'count', summable: true },
                    { key: 'old_leads_with_payment', label: 'لید قدیمی با پرداخت', type: 'count', summable: true },
                    { key: 'old_leads_without_payment', label: 'لید قدیمی بدون پرداخت', type: 'count', summable: true },
                    { key: 'old_leads_failed', label: 'لید قدیمی ناموفق', type: 'count', summable: true }
                ]
            },
            payment_status: {
                title: '4) وضعیت پرداخت‌ها',
                rowTotalUnit: null,
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول وضعیت مالی معاملات را نشان می‌دهد: چند معامله هنوز مانده حساب دارند، چند معامله کامل تسویه شده‌اند و مجموع مبالغ هرکدام چقدر است.',
                    source: 'مبلغ کل قرارداد، مجموع پرداخت‌های ثبت‌شده و وضعیت پرداخت هر معامله در سیستم.',
                    formula: 'اگر مجموع پرداخت از مبلغ قرارداد کمتر باشد، معامله بدهکار است. اگر برابر یا بیشتر باشد، معامله تسویه‌شده محسوب می‌شود.',
                    goal: 'برای کنترل دقیق وصولی‌ها، مدیریت مانده‌ها و برنامه‌ریزی پیگیری مالی استفاده می‌شود.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'outstanding_deals_count', label: 'معامله با مانده حساب', type: 'count', summable: true },
                    { key: 'settled_deals_count', label: 'معامله تسویه‌شده', type: 'count', summable: true },
                    { key: 'outstanding_amount_sum', label: 'جمع مانده حساب', type: 'amount', summable: true },
                    { key: 'settled_contract_amount_sum', label: 'جمع تسویه‌شده', type: 'amount', summable: true },
                    { key: 'payments_sum', label: 'جمع کل پرداخت‌ها', type: 'amount', summable: true }
                ]
            },
            conversion_status: {
                title: '5) وضعیت نرخ تبدیل',
                rowTotalUnit: null,
                showOwnerTotals: true,
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول درصد تبدیل معاملات به فروش را برای کل، لید جدید و لید قدیمی نشان می‌دهد.',
                    source: 'تعداد کل معاملات و تعداد معاملاتی که واقعاً پرداخت داشته‌اند.',
                    formula: 'نرخ تبدیل برابر است با تعداد فروش تقسیم بر کل معاملات همان بخش، ضربدر ۱۰۰.',
                    goal: 'برای سنجش کیفیت تبدیل و بررسی اینکه کدام بخش نیاز به بهبود در فرآیند فروش دارد.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'owner_range_conversion_rate', label: 'نرخ تبدیل کل بازه کارشناس', type: 'percent', aggregation: 'rate', numeratorKey: 'sales_total_count', denominatorKey: 'total_deals_count' },
                    { key: 'total_deals_count', label: 'کل معاملات', type: 'count', summable: true },
                    { key: 'total_new_deals_count', label: 'کل معاملات لید جدید', type: 'count', summable: true },
                    { key: 'total_old_deals_count', label: 'کل معاملات لید قدیمی', type: 'count', summable: true },
                    { key: 'conversion_rate_total', label: 'نرخ تبدیل کل', type: 'percent', aggregation: 'rate', numeratorKey: 'sales_total_count', denominatorKey: 'total_deals_count' },
                    { key: 'non_conversion_rate_total', label: 'نرخ عدم تبدیل کل', type: 'percent', aggregation: 'rate_inverse', numeratorKey: 'sales_total_count', denominatorKey: 'total_deals_count' },
                    { key: 'conversion_rate_new', label: 'نرخ تبدیل لید جدید', type: 'percent', aggregation: 'rate', numeratorKey: 'sales_new_count', denominatorKey: 'total_new_deals_count' },
                    { key: 'non_conversion_rate_new', label: 'نرخ عدم تبدیل لید جدید', type: 'percent', aggregation: 'rate_inverse', numeratorKey: 'sales_new_count', denominatorKey: 'total_new_deals_count' },
                    { key: 'conversion_rate_old', label: 'نرخ تبدیل لید قدیمی', type: 'percent', aggregation: 'rate', numeratorKey: 'sales_old_count', denominatorKey: 'total_old_deals_count' },
                    { key: 'non_conversion_rate_old', label: 'نرخ عدم تبدیل لید قدیمی', type: 'percent', aggregation: 'rate_inverse', numeratorKey: 'sales_old_count', denominatorKey: 'total_old_deals_count' },
                    { key: 'sales_total_count', label: 'تعداد فروش کل', type: 'count', summable: true },
                    { key: 'sales_new_count', label: 'تعداد فروش لید جدید', type: 'count', summable: true },
                    { key: 'sales_old_count', label: 'تعداد فروش لید قدیمی', type: 'count', summable: true }
                ]
            },
            conversion_time: {
                title: '6) میانگین زمان تبدیل',
                rowTotalUnit: null,
                showOwnerTotals: true,
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول نشان می‌دهد به طور میانگین چند روز طول می‌کشد یک معامله به اولین پرداخت برسد یا به وضعیت شکست برسد.',
                    source: 'تاریخ ثبت معامله، تاریخ اولین پرداخت و تاریخ ناموفق شدن معامله.',
                    formula: 'زمان تبدیل از اختلاف تاریخ اولین پرداخت و تاریخ ثبت معامله محاسبه می‌شود. زمان شکست هم از اختلاف تاریخ ناموفق شدن و تاریخ ثبت معامله.',
                    goal: 'برای سنجش سرعت تیم فروش و پیدا کردن گلوگاه‌های تأخیر در مسیر تبدیل.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'avg_conversion_days_total', label: 'میانگین تبدیل کل (روز)', type: 'day', aggregation: 'average', sumKey: 'sum_conversion_days_total', countKey: 'count_conversion_days_total' },
                    { key: 'avg_conversion_days_new', label: 'میانگین تبدیل لید جدید (روز)', type: 'day', aggregation: 'average', sumKey: 'sum_conversion_days_new', countKey: 'count_conversion_days_new' },
                    { key: 'avg_conversion_days_old', label: 'میانگین تبدیل لید قدیمی (روز)', type: 'day', aggregation: 'average', sumKey: 'sum_conversion_days_old', countKey: 'count_conversion_days_old' },
                    { key: 'avg_failure_days_total', label: 'میانگین شکست کل (روز)', type: 'day', aggregation: 'average', sumKey: 'sum_failure_days_total', countKey: 'count_failure_days_total' },
                    { key: 'avg_failure_days_new', label: 'میانگین شکست لید جدید (روز)', type: 'day', aggregation: 'average', sumKey: 'sum_failure_days_new', countKey: 'count_failure_days_new' },
                    { key: 'avg_failure_days_old', label: 'میانگین شکست لید قدیمی (روز)', type: 'day', aggregation: 'average', sumKey: 'sum_failure_days_old', countKey: 'count_failure_days_old' },
                    { key: 'payments_after_deal_count', label: 'پرداخت بعد از معامله', type: 'count', summable: true },
                    { key: 'payments_before_deal_count', label: 'پرداخت قبل از معامله', type: 'count', summable: true },
                    { key: 'payments_same_day_count', label: 'پرداخت همان‌روز معامله', type: 'count', summable: true }
                ]
            },
            failure_reasons: {
                title: '7) دلایل شکست',
                rowTotalUnit: 'count',
                rowTotalColumns: ['failed_new_count', 'failed_old_count'],
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول دلیل‌هایی را که باعث ناموفق شدن معامله شده‌اند، به تفکیک کارشناس و نوع لید نمایش می‌دهد.',
                    source: 'دلایل ثبت‌شده برای معاملات ناموفق در سیستم فروش.',
                    formula: 'فقط معاملاتی در این جدول شمارش می‌شوند که واقعاً در وضعیت ناموفق ثبت شده‌اند.',
                    goal: 'برای تشخیص علت‌های پرتکرار از دست‌رفتن فروش و اصلاح فرآیند مذاکره یا پیگیری.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'failure_reason', label: 'عنوان دلیل', type: 'text' },
                    { key: 'failed_without_current_count', label: 'ناموفق بدون معامله جاری', type: 'count', summable: true },
                    { key: 'failed_new_count', label: 'ناموفق لید جدید', type: 'count', summable: true },
                    { key: 'failed_old_count', label: 'ناموفق لید قدیمی', type: 'count', summable: true }
                ]
            },
            product_status: {
                title: '8) وضعیت محصولات',
                rowTotalUnit: 'count',
                rowTotalColumns: ['sales_total_count'],
                defaultSortKey: 'date',
                description: {
                    definition: 'این جدول نشان می‌دهد هر محصول چند فروش داشته و این فروش‌ها از لید جدید بوده یا لید قدیمی.',
                    source: 'محصولات هر معامله به‌همراه وضعیت پرداخت همان معامله.',
                    formula: 'فقط معامله‌هایی به عنوان فروش محصول شمرده می‌شوند که حداقل یک پرداخت داشته باشند.',
                    goal: 'برای تحلیل عملکرد محصولات و تمرکز روی محصولاتی که فروش بهتری دارند.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'product_name', label: 'نام محصول', type: 'text' },
                    { key: 'sales_total_count', label: 'تعداد فروش کل', type: 'count', summable: true },
                    { key: 'sales_new_count', label: 'تعداد فروش لید جدید', type: 'count', summable: true },
                    { key: 'sales_old_count', label: 'تعداد فروش لید قدیمی', type: 'count', summable: true }
                ]
            },
            product_sold_by_owner_day: {
                title: '8-1) محصولات فروخته‌شده',
                rowTotalUnit: 'count',
                rowTotalColumns: ['sold_total_count'],
                defaultSortKey: 'date',
                description: {
                    definition: 'تعداد فروش هر محصول در هر روز و هر کارشناس با تفکیک لید جدید و قدیمی.',
                    source: 'deals + deal_products + products + payments',
                    formula: 'فقط معاملاتی محاسبه می‌شوند که حداقل یک پرداخت ثبت کرده باشند.',
                    goal: 'مشخص‌کردن اینکه هر کارشناس در هر روز از هر محصول چه مقدار فروش واقعی داشته است.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'product_name', label: 'نام محصول', type: 'text' },
                    { key: 'sold_new_count', label: 'فروش لید جدید', type: 'count', summable: true },
                    { key: 'sold_old_count', label: 'فروش لید قدیمی', type: 'count', summable: true },
                    { key: 'sold_total_count', label: 'فروش کل', type: 'count', summable: true }
                ]
            },
            product_requests_by_owner_day: {
                title: '8-2) کل محصولات درخواستی',
                rowTotalUnit: 'count',
                rowTotalColumns: ['requested_total_count'],
                defaultSortKey: 'date',
                description: {
                    definition: 'حجم درخواست محصول در معاملات و نسبت فروش‌رفته/فروش‌نرفته برای هر محصول، روز و کارشناس.',
                    source: 'deals + deal_products + products + payments',
                    formula: 'درخواست = وجود محصول در معامله؛ فروش‌رفته = has_payment=1؛ فروش‌نرفته = has_payment=0.',
                    goal: 'تحلیل شکاف بین تقاضای محصول و فروش واقعی برای تصمیم‌گیری تیم فروش.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'product_name', label: 'نام محصول', type: 'text' },
                    { key: 'requested_new_count', label: 'درخواست لید جدید', type: 'count', summable: true },
                    { key: 'requested_old_count', label: 'درخواست لید قدیمی', type: 'count', summable: true },
                    { key: 'requested_total_count', label: 'درخواست کل', type: 'count', summable: true },
                    { key: 'sold_total_count', label: 'فروش‌رفته', type: 'count', summable: true },
                    { key: 'unsold_total_count', label: 'فروش‌نرفته', type: 'count', summable: true }
                ]
            },
            product_failed_by_owner_day: {
                title: '8-3) فروش ناموفق محصولات',
                rowTotalUnit: 'count',
                rowTotalColumns: ['failed_total_count'],
                defaultSortKey: 'date',
                description: {
                    definition: 'تعداد معاملات هر محصول که هنوز به فروش نرسیده‌اند، با تفکیک لید جدید و قدیمی.',
                    source: 'deals + deal_products + products + payments',
                    formula: 'ناموفق در این جدول یعنی معامله محصولی که پرداختی ثبت نکرده است (has_payment=0).',
                    goal: 'تمرکز روی محصولات و کارشناسانی که بیشترین عدم‌تبدیل را دارند.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'product_name', label: 'نام محصول', type: 'text' },
                    { key: 'failed_new_count', label: 'ناموفق لید جدید', type: 'count', summable: true },
                    { key: 'failed_old_count', label: 'ناموفق لید قدیمی', type: 'count', summable: true },
                    { key: 'failed_total_count', label: 'ناموفق کل', type: 'count', summable: true }
                ]
            },
            activity_summary: {
                title: '9-1) خلاصه فعالیت روزانه',
                rowTotalUnit: null,
                defaultSortKey: 'date',
                description: {
                    definition: 'نمای کلی عملکرد روزانه هر کارشناس را نشان می‌دهد: چه تعداد فعالیت ثبت شده، چند دقیقه زمان صرف شده و چه مقدار از لیدهای جدید/قدیمی پوشش پیگیری داشته‌اند.',
                    source: 'جدول activities به‌همراه persons و users.',
                    formula: 'تجمیع در سطح «تاریخ + کارشناس» انجام می‌شود و هر ردیف یک خلاصه یکتا از همان روز است.',
                    goal: 'برای کنترل حجم کار روزانه، کیفیت پیگیری و تشخیص روزهایی که لیدها بدون فعالیت مانده‌اند.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'total_activities', label: 'کل فعالیت‌ها', type: 'count', summable: true },
                    { key: 'total_duration_minutes', label: 'مدت کل فعالیت‌ها (دقیقه)', type: 'count', summable: true },
                    { key: 'avg_duration_minutes', label: 'میانگین مدت هر فعالیت (دقیقه)', type: 'count' },
                    { key: 'activities_new_lead', label: 'فعالیت روی لید جدید', type: 'count', summable: true },
                    { key: 'activities_old_lead', label: 'فعالیت روی لید قدیمی', type: 'count', summable: true },
                    { key: 'new_leads_without_activity', label: 'لید جدید بدون فعالیت', type: 'count', summable: true },
                    { key: 'old_leads_without_activity', label: 'لید قدیمی بدون فعالیت', type: 'count', summable: true },
                    { key: 'no_response_new', label: 'تماس بی‌پاسخ لید جدید', type: 'count', summable: true },
                    { key: 'no_response_old', label: 'تماس بی‌پاسخ لید قدیمی', type: 'count', summable: true },
                    { key: 'no_response_total', label: 'کل تماس بی‌پاسخ', type: 'count', summable: true }
                ]
            },
            activity_type_details: {
                title: '9-2) جزئیات نوع فعالیت',
                rowTotalUnit: null,
                defaultSortKey: 'date',
                description: {
                    definition: 'جزئیات نوع فعالیت را در هر روز و برای هر کارشناس نشان می‌دهد تا مشخص شود سهم واقعی هر نوع پیگیری چقدر بوده است.',
                    source: 'جدول activities با تجمیع بر اساس عنوان نوع فعالیت (activity_type_title).',
                    formula: 'هر ردیف فقط یک نوع فعالیت را نشان می‌دهد و ستون «درصد سهم از کل فعالیت روز» نسبت همان نوع به کل فعالیت‌های همان روزِ همان کارشناس است.',
                    goal: 'برای تحلیل ترکیب فعالیت‌ها (مثلاً تماس/پیگیری/چت) و بهبود سبک پیگیری تیم فروش.'
                },
                columns: [
                    { key: 'date', label: 'تاریخ', type: 'date' },
                    { key: 'owner_name', label: 'کارشناس', type: 'text' },
                    { key: 'activity_type', label: 'نوع فعالیت', type: 'text' },
                    { key: 'activity_count_total', label: 'تعداد این نوع فعالیت', type: 'count', summable: true },
                    { key: 'activity_type_new_count', label: 'تعداد روی لید جدید', type: 'count', summable: true },
                    { key: 'activity_type_old_count', label: 'تعداد روی لید قدیمی', type: 'count', summable: true },
                    { key: 'activity_type_duration_minutes', label: 'مدت این نوع فعالیت (دقیقه)', type: 'count', summable: true },
                    { key: 'avg_activity_type_duration_minutes', label: 'میانگین مدت این نوع (دقیقه)', type: 'count' },
                    { key: 'activity_share_percent', label: 'درصد سهم از کل فعالیت روز', type: 'percent' }
                ]
            }
        };

        return defs[tableId] || null;
    },

    renderKpiAdvancedTablesSection() {
        const tables = this.state.kpiAdvancedTables || {};
        const tableIds = this.getVisibleKpiAdvancedTableIds();
        if (tableIds.length === 0) {
            return '<div class="text-sm text-gray-500">برای بازه انتخاب‌شده داده تحلیلی پیشرفته موجود نیست.</div>';
        }

        // Navigation is now in the main header
        // Responsive scroll margin: small on mobile (relative header), large on desktop (sticky header)
        const panels = tableIds
            .map(tableId => `<div id="kpi-adv-panel-${tableId}" class="scroll-mt-[30rem] transition-all duration-500 mb-8">${this.renderKpiAdvancedTablePanel(tableId)}</div>`)
            .join('');

        return `
            <div class="mb-4 flex justify-end">
                <button onclick="app.exportAllKpiAdvancedTables()" class="bg-emerald-700 hover:bg-emerald-600 text-white px-4 py-2 rounded text-sm shadow-lg flex items-center gap-2 transition-colors">
                    <i class="fa-solid fa-file-csv"></i>
                    خروجی همه جدول‌ها (CSV)
                </button>
            </div>
            ${panels}
        `;
    },

    getVisibleKpiAdvancedTableIds() {
        const all = Object.keys(this.state.kpiAdvancedTables || {});
        return all.filter(tableId => this.state.kpiAdvancedVisibleTables?.[tableId] !== false);
    },

    toggleKpiAdvancedTableVisibility(tableId, isVisible) {
        if (!this.state.kpiAdvancedVisibleTables) {
            this.state.kpiAdvancedVisibleTables = {};
        }
        this.state.kpiAdvancedVisibleTables[tableId] = !!isVisible;
        if (this.state.kpiLastData) {
            this.renderKPIDashboard(this.state.kpiLastData);
        }
    },
    
    scrollToKpiTop() {
        const content = document.getElementById('kpi-dashboard-content');
        if (content) {
            content.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    },

    scrollToKpiTable(elementId) {
        const element = document.getElementById(elementId);
        if (element) {
            // Use scrollIntoView which handles overflow containers correctly
            element.scrollIntoView({ behavior: 'smooth', block: 'start' });

            // Highlight effect
            element.classList.add('ring-2', 'ring-blue-500', 'ring-offset-2', 'ring-offset-gray-900', 'rounded-lg');
            setTimeout(() => {
                element.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-2', 'ring-offset-gray-900', 'rounded-lg');
            }, 2000);
        }
    },

    renderKpiAdvancedTablePanel(tableId) {
        const definition = this.getKpiAdvancedTableDefinition(tableId);
        const rows = Array.isArray(this.state.kpiAdvancedTables?.[tableId]) ? this.state.kpiAdvancedTables[tableId] : [];
        if (!definition) return '';
        const ui = this.state.kpiAdvancedUi?.[tableId] || { search: '' };
        const visibleColumns = this.getKpiAdvancedVisibleColumns(tableId);

        const columnToggles = definition.columns.map(col => `
            <label class="inline-flex items-center ml-3 mb-2 text-xs text-gray-300">
                <input type="checkbox" ${visibleColumns.some(v => v.key === col.key) ? 'checked' : ''}
                    onchange="app.toggleKpiAdvancedColumn('${tableId}','${col.key}', this.checked)" class="ml-1">
                ${col.label}
            </label>
        `).join('');

        const descriptionBlock = definition.description ? `
            <div class="mb-3 grid grid-cols-1 md:grid-cols-2 gap-2 text-xs">
                <div class="bg-gray-800 border border-gray-700 rounded p-2"><span class="text-gray-400">تعریف:</span> <span class="text-gray-200">${this.escapeHtml(definition.description.definition || '-')}</span></div>
                <div class="bg-gray-800 border border-gray-700 rounded p-2"><span class="text-gray-400">منبع داده:</span> <span class="text-gray-200">${this.escapeHtml(definition.description.source || '-')}</span></div>
                <div class="bg-gray-800 border border-gray-700 rounded p-2"><span class="text-gray-400">فرمول:</span> <span class="text-gray-200">${this.escapeHtml(definition.description.formula || '-')}</span></div>
                <div class="bg-gray-800 border border-gray-700 rounded p-2"><span class="text-gray-400">هدف:</span> <span class="text-gray-200">${this.escapeHtml(definition.description.goal || '-')}</span></div>
            </div>
        ` : '';

        return `
            <div class="border border-gray-700 rounded-lg p-4">
                <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3 mb-3">
                    <div class="text-sm font-bold text-blue-300">${definition.title}</div>
                    <div class="flex gap-2">
                        <button onclick="app.exportKpiAdvancedTableCsv('${tableId}')" class="bg-blue-700 hover:bg-blue-600 text-white px-3 py-2 rounded text-xs">خروجی CSV</button>
                    </div>
                </div>
                ${descriptionBlock}
                <div class="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3">
                    <input type="text" value="${this.escapeHtml(ui.search || '')}" oninput="app.setKpiAdvancedSearch('${tableId}', this.value)"
                        class="bg-gray-800 border border-gray-700 rounded p-2 text-sm text-white" placeholder="فیلتر داخلی جدول...">
                    <div class="bg-gray-800 border border-gray-700 rounded p-2 text-xs">
                        <label class="inline-flex items-center text-xs text-gray-300 mb-2">
                            <input type="checkbox" class="ml-1" ${ui.hideZeroRows ? 'checked' : ''} onchange="app.toggleKpiAdvancedHideZeroRows('${tableId}', this.checked)">
                            مخفی‌کردن ردیف‌های با جمع صفر
                        </label>
                        <div>${columnToggles}</div>
                    </div>
                </div>
                ${rows.length === 0 ? '<div class="text-xs text-gray-500">رکوردی در این جدول وجود ندارد.</div>' : this.renderKpiAdvancedTableGrid(tableId)}
            </div>
        `;
    },

    getKpiAdvancedVisibleColumns(tableId) {
        const definition = this.getKpiAdvancedTableDefinition(tableId);
        const ui = this.state.kpiAdvancedUi?.[tableId] || {};
        if (!definition) return [];
        return definition.columns.filter(col => ui.visibleColumns?.[col.key] !== false);
    },

    getKpiAdvancedProcessedRows(tableId) {
        const definition = this.getKpiAdvancedTableDefinition(tableId);
        const ui = this.state.kpiAdvancedUi?.[tableId] || {};
        const rows = Array.isArray(this.state.kpiAdvancedTables?.[tableId]) ? [...this.state.kpiAdvancedTables[tableId]] : [];
        if (!definition) return [];

        const search = (ui.search || '').trim().toLowerCase();
        let filtered = rows;
        if (search) {
            filtered = rows.filter(row => {
                return definition.columns.some(col => String(row[col.key] ?? '').toLowerCase().includes(search));
            });
        }

        if (ui.hideZeroRows) {
            const sumCols = definition.columns.filter(c => c.summable);
            filtered = filtered.filter(row => {
                const rowSum = sumCols.reduce((sum, c) => sum + Number(row[c.key] || 0), 0);
                return rowSum > 0;
            });
        }

        const sortKey = ui.sortKey || definition.defaultSortKey || 'date';
        const sortDir = ui.sortDir === 'desc' ? -1 : 1;
        const sortCol = definition.columns.find(c => c.key === sortKey);
        filtered.sort((a, b) => {
            const va = a[sortKey];
            const vb = b[sortKey];
            if (sortCol && ['count', 'amount', 'percent', 'day'].includes(sortCol.type)) {
                return ((Number(va || 0) - Number(vb || 0)) * sortDir);
            }
            return String(va ?? '').localeCompare(String(vb ?? ''), 'fa', { numeric: true }) * sortDir;
        });

        return filtered;
    },

    getKpiAdvancedRowTotalColumns(definition, visibleColumns) {
        if (!definition || !definition.rowTotalUnit) return [];
        if (Array.isArray(definition.rowTotalColumns) && definition.rowTotalColumns.length > 0) {
            return definition.columns.filter((col) => definition.rowTotalColumns.includes(col.key));
        }
        return visibleColumns.filter(c => c.summable && c.type === definition.rowTotalUnit);
    },

    renderKpiAdvancedTableGrid(tableId) {
        const definition = this.getKpiAdvancedTableDefinition(tableId);
        if (!definition) return '';
        if (this.isKpiProductPivotTable(tableId)) {
            return this.renderKpiProductPivotGrid(tableId);
        }

        const rows = this.getKpiAdvancedProcessedRows(tableId);
        const dataRows = rows.filter(row => !row.__owner_total__);
        if (!this.state.kpiAdvancedRenderedRows) {
            this.state.kpiAdvancedRenderedRows = {};
        }
        this.state.kpiAdvancedRenderedRows[tableId] = rows;
        const visibleColumns = this.getKpiAdvancedVisibleColumns(tableId);
        const ui = this.state.kpiAdvancedUi?.[tableId] || {};
        
        // --- Smart Footer Calculation ---
        const totals = {};
        const aggregationState = {};

        const summableKeys = [...new Set(visibleColumns.filter(col => col.summable).map(col => col.key))];
        const numeratorKeys = [...new Set(visibleColumns.filter(col => (col.aggregation === 'rate' || col.aggregation === 'rate_inverse') && col.numeratorKey).map(col => col.numeratorKey))];
        const denominatorKeys = [...new Set(visibleColumns.filter(col => (col.aggregation === 'rate' || col.aggregation === 'rate_inverse') && col.denominatorKey).map(col => col.denominatorKey))];
        const sumKeys = [...new Set(visibleColumns.filter(col => col.aggregation === 'average' && col.sumKey).map(col => col.sumKey))];
        const countKeys = [...new Set(visibleColumns.filter(col => col.aggregation === 'average' && col.countKey).map(col => col.countKey))];
        const allAggKeys = [...new Set([...summableKeys, ...numeratorKeys, ...denominatorKeys, ...sumKeys, ...countKeys])];

        dataRows.forEach((row) => {
            allAggKeys.forEach((key) => {
                aggregationState[key] = (aggregationState[key] || 0) + Number(row[key] || 0);
            });
        });

        // Compute Final Totals
        visibleColumns.forEach(col => {
            if (col.summable) {
                totals[col.key] = aggregationState[col.key] || 0;
            } else if (col.aggregation === 'rate') {
                const num = aggregationState[col.numeratorKey] || 0;
                const den = aggregationState[col.denominatorKey] || 0;
                totals[col.key] = den !== 0 ? (num / den) * 100 : 0;
            } else if (col.aggregation === 'rate_inverse') {
                const num = aggregationState[col.numeratorKey] || 0; // "Converted" count usually
                const den = aggregationState[col.denominatorKey] || 0; // "Total" count
                // rate_inverse usually means "Non-converted Rate" -> (Total - Converted) / Total
                totals[col.key] = den !== 0 ? ((den - num) / den) * 100 : 0;
            } else if (col.aggregation === 'average') {
                const sumVal = aggregationState[col.sumKey] || 0;
                const countVal = aggregationState[col.countKey] || 0;
                totals[col.key] = countVal !== 0 ? (sumVal / countVal) : 0;
            }
        });

        const hasRowTotal = !!definition.rowTotalUnit;
        const rowTotalColumns = this.getKpiAdvancedRowTotalColumns(definition, visibleColumns);
        const headerHtml = visibleColumns.map(col => {
            const sortIcon = ui.sortKey === col.key ? (ui.sortDir === 'asc' ? '▲' : '▼') : '';
            return `<th class="border border-gray-700 bg-gray-800 p-2 cursor-pointer select-none text-gray-300 hover:text-white transition-colors" onclick="app.setKpiAdvancedSort('${tableId}','${col.key}')">${col.label} <span class="text-xs opacity-70">${sortIcon}</span></th>`;
        }).join('');

        const rowTotalFor = (row) => {
            if (!hasRowTotal) return 0;
            return rowTotalColumns.reduce((sum, c) => sum + Number(row[c.key] || 0), 0);
        };

        const bodyHtml = rows.map((row, idx) => {
            const isEven = idx % 2 === 0;
            const bgClass = row.__owner_total__ ? 'bg-indigo-900/20' : (isEven ? 'bg-gray-900/50' : 'bg-gray-900/30');
            const cells = visibleColumns.map(col => {
                const formatted = this.formatKpiAdvancedCell(row[col.key], col.type);
                const clickable = !row.__owner_total__ && !['date', 'text'].includes(col.type);
                if (!clickable) {
                    return `<td class="border border-gray-700/50 p-2 text-center text-gray-300">${formatted}</td>`;
                }
                return `<td class="border border-gray-700/50 p-2 text-center">
                    <button type="button" class="text-cyan-300 hover:text-cyan-200 hover:underline transition-colors" onclick="app.openKpiAdvancedDrilldown('${tableId}','${col.key}',${idx})">
                        ${formatted}
                    </button>
                </td>`;
            }).join('');
            const totalCell = hasRowTotal ? `<td class="border border-gray-700/50 p-2 font-bold text-center text-white bg-gray-800/20">${this.formatKpiAdvancedCell(rowTotalFor(row), definition.rowTotalUnit)}</td>` : '';
            return `<tr class="${bgClass} hover:bg-gray-800 transition-colors">${cells}${totalCell}</tr>`;
        }).join('');

        const footerCells = visibleColumns.map((col, index) => {
            if (index === 0) {
                return '<td class="border border-gray-700 p-2 font-bold text-white bg-gray-800">جمع کل</td>';
            }
            if (totals[col.key] !== undefined) {
                // Format logic
                const formatted = this.formatKpiAdvancedCell(totals[col.key], col.type);
                return `<td class="border border-gray-700 p-2 font-bold text-center text-white bg-gray-800" dir="ltr">${formatted}</td>`;
            }
            return '<td class="border border-gray-700 p-2 text-gray-600 text-center bg-gray-800">-</td>';
        }).join('');
        
        const footerTotalCell = hasRowTotal
            ? `<td class="border border-gray-700 p-2 font-bold text-center text-white bg-gray-800">${this.formatKpiAdvancedCell(dataRows.reduce((sum, row) => sum + rowTotalFor(row), 0), definition.rowTotalUnit)}</td>`
            : '';

        return `
            <div class="overflow-x-auto rounded-lg border border-gray-700 shadow-xl custom-scrollbar">
                <table class="w-full text-xs border-collapse min-w-[900px]">
                    <thead>
                        <tr>${headerHtml}${hasRowTotal ? '<th class="border border-gray-700 bg-gray-800 p-2 text-white">جمع ردیف</th>' : ''}</tr>
                    </thead>
                    <tbody>
                        ${bodyHtml || `<tr><td colspan="${visibleColumns.length + (hasRowTotal ? 1 : 0)}" class="p-6 text-center text-gray-500 italic">نتیجه‌ای برای این فیلتر وجود ندارد.</td></tr>`}
                    </tbody>
                    <tfoot class="sticky bottom-0 z-10 shadow-[0_-5px_15px_rgba(0,0,0,0.3)]">
                        <tr>${footerCells}${footerTotalCell}</tr>
                    </tfoot>
                </table>
            </div>
        `;
    },

    formatKpiAdvancedCell(value, type) {
        if (type === 'amount') return `${this.formatNumber(Number(value || 0))} تومان`;
        if (type === 'percent') {
            if (value === null || value === undefined || value === '') return '-';
            return `${Number(value || 0).toFixed(2)}%`;
        }
        if (type === 'day') return `${Number(value || 0).toFixed(2)} روز`;
        if (type === 'count') return this.formatNumber(Number(value || 0));
        if (type === 'date') return this.formatKpiDate(value);
        return this.escapeHtml(String(value ?? ''));
    },

    setKpiAdvancedSearch(tableId, value) {
        if (!this.state.kpiAdvancedUi?.[tableId]) return;
        this.state.kpiAdvancedUi[tableId].search = value || '';
        this.refreshKpiAdvancedPanel(tableId);
    },

    setKpiAdvancedSort(tableId, sortKey) {
        if (!this.state.kpiAdvancedUi?.[tableId]) return;
        const ui = this.state.kpiAdvancedUi[tableId];
        if (ui.sortKey === sortKey) {
            ui.sortDir = ui.sortDir === 'asc' ? 'desc' : 'asc';
        } else {
            ui.sortKey = sortKey;
            ui.sortDir = 'asc';
        }
        this.refreshKpiAdvancedPanel(tableId);
    },

    toggleKpiAdvancedColumn(tableId, colKey, checked) {
        if (!this.state.kpiAdvancedUi?.[tableId]) return;
        this.state.kpiAdvancedUi[tableId].visibleColumns[colKey] = !!checked;
        this.refreshKpiAdvancedPanel(tableId);
    },

    toggleKpiAdvancedHideZeroRows(tableId, checked) {
        if (!this.state.kpiAdvancedUi?.[tableId]) return;
        this.state.kpiAdvancedUi[tableId].hideZeroRows = !!checked;
        this.refreshKpiAdvancedPanel(tableId);
    },

    refreshKpiAdvancedPanel(tableId) {
        const panel = document.getElementById(`kpi-adv-panel-${tableId}`);
        if (!panel) return;
        panel.innerHTML = this.renderKpiAdvancedTablePanel(tableId);
    },

    exportKpiAdvancedTableCsv(tableId) {
        if (this.isKpiProductPivotTable(tableId)) {
            this.exportKpiProductPivotCsv(tableId);
            return;
        }
        const definition = this.getKpiAdvancedTableDefinition(tableId);
        if (!definition) return;
        const visibleColumns = this.getKpiAdvancedVisibleColumns(tableId);
        const rows = this.getKpiAdvancedProcessedRows(tableId);
        const hasRowTotal = !!definition.rowTotalUnit;
        const rowTotalColumns = this.getKpiAdvancedRowTotalColumns(definition, visibleColumns);

        const headers = visibleColumns.map(c => c.label);
        if (hasRowTotal) headers.push('جمع ردیف');

        const lines = [headers.join(',')];
        rows.forEach(row => {
            const cells = visibleColumns.map(col => this.csvEscape(this.formatKpiAdvancedCell(row[col.key], col.type)));
            if (hasRowTotal) {
                const rowTotal = rowTotalColumns.reduce((sum, c) => sum + Number(row[c.key] || 0), 0);
                cells.push(this.csvEscape(this.formatKpiAdvancedCell(rowTotal, definition.rowTotalUnit)));
            }
            lines.push(cells.join(','));
        });

        const csvText = "\uFEFF" + lines.join('\n');
        const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `kpi_${tableId}_${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(link.href);
    },

    exportKpiProductPivotCsv(tableId) {
        const model = this.buildKpiProductPivotModel(tableId);
        const products = model.products || [];
        const rows = model.rows || [];
        const isRequests = tableId === 'product_requests_by_owner_day';

        const headers = ['تاریخ', 'کارشناس'];
        products.forEach((p) => {
            headers.push(`${p} (ج)`);
            headers.push(`${p} (ق)`);
        });
        if (isRequests) {
            headers.push('کل درخواست');
            headers.push('فروش‌رفته');
            headers.push('فروش‌نرفته');
        }
        headers.push('جمع کل ردیف');

        const lines = [headers.map(v => this.csvEscape(v)).join(',')];
        rows.forEach((row) => {
            const cells = [
                this.csvEscape(this.formatKpiDate(row.date || '')),
                this.csvEscape(row.owner_name || '-')
            ];
            products.forEach((p) => {
                const pv = row.productValues?.[p] || { n: 0, o: 0, t: 0 };
                cells.push(this.csvEscape(this.formatNumber(Number(pv.n || 0))));
                cells.push(this.csvEscape(this.formatNumber(Number(pv.o || 0))));
            });
            if (isRequests) {
                cells.push(this.csvEscape(this.formatNumber(Number(row.requested_total_count || 0))));
                cells.push(this.csvEscape(this.formatNumber(Number(row.sold_total_count || 0))));
                cells.push(this.csvEscape(this.formatNumber(Number(row.unsold_total_count || 0))));
            }
            const rowSum = products.reduce((sum, p) => sum + Number(row.productValues?.[p]?.t || 0), 0);
            cells.push(this.csvEscape(this.formatNumber(Number(rowSum || 0))));
            lines.push(cells.join(','));
        });

        const csvText = "\uFEFF" + lines.join('\n');
        const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `kpi_${tableId}_${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(link.href);
    },

    exportAllKpiAdvancedTables() {
        const tables = this.state.kpiAdvancedTables || {};
        const tableIds = Object.keys(tables || {});
        tableIds.forEach((tableId, index) => {
            setTimeout(() => this.exportKpiAdvancedTableCsv(tableId), index * 250);
        });
    },

    async openKpiAdvancedDrilldown(tableId, metricKey, rowIndex) {
        if (!this.canKpiFeatureClient('kpi.drilldown')) {
            Swal.fire('خطا', 'دسترسی Drilldown داشبورد KPI ندارید', 'error');
            return;
        }
        const rows = this.state.kpiAdvancedRenderedRows?.[tableId] || [];
        const row = rows?.[rowIndex];
        if (!row) return;
        this.state.kpiDrilldownContext = {
            mode: 'kpi',
            kind: 'kpi_advanced',
            tableId,
            metricKey,
            rowIndex
        };

        const definition = this.getKpiAdvancedTableDefinition(tableId);
        const col = definition?.columns?.find(c => c.key === metricKey);
        const metricLabel = col?.label || metricKey;
        const tableTitle = definition?.title || tableId;
        const rowDateText = row.date ? this.formatKpiDate(row.date) : 'همه تاریخ‌ها';
        const rowOwnerText = row.owner_name || 'همه کارشناسان';

        this.state.kpiDrilldown = {
            tableId,
            metricKey,
            row,
            data: null,
            loading: true
        };

        const subtitle = document.getElementById('kpi-drilldown-subtitle');
        if (subtitle) {
            subtitle.textContent = `${tableTitle} | ${metricLabel} | تاریخ: ${rowDateText} | کارشناس: ${rowOwnerText}`;
        }
        const container = document.getElementById('kpi-drilldown-content');
        if (container) {
            container.innerHTML = '<div class="text-sm text-gray-400">در حال بارگذاری داده‌های مرتبط...</div>';
        }
        this.openModal('modalKpiDrilldown');

        const filters = this.state.kpiFilters || {};
        const payload = {
            table_id: tableId,
            metric_key: metricKey,
            date_from: filters.date_from || '',
            date_to: filters.date_to || '',
            owner_didar_id: filters.owner_didar_id || '',
            product_id: filters.product_id || '',
            owner_didar_ids: Array.isArray(filters.owner_didar_ids) ? filters.owner_didar_ids.join(',') : '',
            product_ids: Array.isArray(filters.product_ids) ? filters.product_ids.join(',') : '',
            deal_statuses: Array.isArray(filters.deal_statuses) ? filters.deal_statuses.join(',') : '',
            deal_lead_types: Array.isArray(filters.deal_lead_types) ? filters.deal_lead_types.join(',') : '',
            row_date: row.date || '',
            row_owner_didar_id: row.owner_didar_id || '',
            row_owner_name: row.owner_name || '',
            row_product_name: row.product_name || '',
            row_failure_reason: row.failure_reason || '',
            row_activity_type: row.activity_type || ''
        };

        this.appendCriteriaToKpiPayload(payload, 'kpi');
        const res = await this.req('get_kpi_drilldown', payload);
        if (res.status !== 'success') {
            this.state.kpiDrilldown.loading = false;
            if (container) {
                container.innerHTML = `<div class="text-sm text-red-400">${this.escapeHtml(res.message || 'خطا در دریافت جزئیات')}</div>`;
            }
            return;
        }

        this.state.kpiDrilldown.loading = false;
        this.state.kpiDrilldown.data = res;
        if (container) {
            container.innerHTML = this.renderKpiDrilldownContent(res);
        }
    },

    async openKpiAdvancedDrilldownWithProduct(tableId, metricKey, rowIndex, encodedProductName = '') {
        if (!this.canKpiFeatureClient('kpi.drilldown')) {
            Swal.fire('خطا', 'دسترسی Drilldown داشبورد KPI ندارید', 'error');
            return;
        }
        const rows = this.state.kpiAdvancedRenderedRows?.[tableId] || [];
        const row = rows?.[rowIndex];
        if (!row) return;
        const productName = decodeURIComponent(String(encodedProductName || ''));

        if (!this.state.kpiAdvancedRenderedRows) {
            this.state.kpiAdvancedRenderedRows = {};
        }
        this.state.kpiAdvancedRenderedRows[tableId] = rows.map((item, idx) => {
            if (idx !== rowIndex) return item;
            return { ...item, product_name: productName || item.product_name || '' };
        });

        await this.openKpiAdvancedDrilldown(tableId, metricKey, rowIndex);
    },

    async openLeadFromKpiDrilldown(contactId) {
        const safeContactId = String(contactId || '').trim();
        if (!safeContactId) return;
        this.closeAllCustomMultiSelectDropdowns();
        this.closeModal('modalKpiDrilldown');
        this.closeModal('modalKPI');
        setTimeout(async () => {
            await this.openPersonProfile(safeContactId);
        }, 50);
    },

    async openDealFromKpiDrilldown(dealId) {
        const safeDealId = String(dealId || '').trim();
        if (!safeDealId) return;
        this.closeAllCustomMultiSelectDropdowns();
        this.closeModal('modalKpiDrilldown');
        this.closeModal('modalKPI');
        setTimeout(async () => {
            await this.openDealModal(safeDealId);
        }, 50);
    },

    flattenExportObject(input, prefix = '', output = {}) {
        const src = (input && typeof input === 'object') ? input : {};
        Object.keys(src).forEach((key) => {
            const value = src[key];
            const nextKey = prefix ? `${prefix}.${key}` : key;
            if (value === null || typeof value === 'undefined') {
                output[nextKey] = '';
                return;
            }
            if (Array.isArray(value)) {
                output[nextKey] = JSON.stringify(value);
                return;
            }
            if (typeof value === 'object') {
                this.flattenExportObject(value, nextKey, output);
                return;
            }
            output[nextKey] = value;
        });
        return output;
    },

    normalizeDrilldownExportDateFields(row) {
        const out = row && typeof row === 'object' ? { ...row } : {};
        Object.keys(out).forEach((key) => {
            const value = out[key];
            if (this.isExportEmptyValue(value)) return;
            if (typeof value !== 'string') return;
            const trimmed = value.trim();
            if (!trimmed) return;
            const lowerKey = key.toLowerCase();
            if (lowerKey.endsWith('_jalali') || lowerKey.endsWith('_persian')) return;

            const looksLikeDateTime = /^\d{4}-\d{2}-\d{2}(?:[ t]\d{2}:\d{2}(?::\d{2})?)?$/.test(trimmed);
            const looksLikeDateKey = lowerKey.includes('date') || lowerKey.includes('time') || lowerKey.endsWith('_at');
            if (!looksLikeDateTime || !looksLikeDateKey) return;

            const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(trimmed);
            out[key] = isDateOnly ? (toPersianDate(trimmed) || trimmed) : (toPersianDateTime(trimmed) || trimmed);

            if (lowerKey === 'register_time' && !out.register_time_jalali) {
                out.register_time_jalali = toPersianDate(trimmed) || '';
            }
            if (lowerKey === 'payment_date' && !out.payment_date_persian) {
                out.payment_date_persian = toPersianDate(trimmed) || '';
            }
        });
        return out;
    },

    normalizeDrilldownExportRows(rows) {
        const src = Array.isArray(rows) ? rows : [];
        return src.map((item) => {
            const row = item && typeof item === 'object' ? { ...item } : {};
            if (Object.prototype.hasOwnProperty.call(row, 'acquaintance_source')) {
                row.acquaintance_source = this.getLookupLabel('lead_sources', row.acquaintance_source || '');
            }
            if (Object.prototype.hasOwnProperty.call(row, 'acquaintance_detail')) {
                row.acquaintance_detail = this.getLookupLabel('lead_source_details', row.acquaintance_detail || '');
            }
            if (Object.prototype.hasOwnProperty.call(row, 'content_topic')) {
                row.content_topic = this.getLookupLabel('content_topics', row.content_topic || '');
            }
            if (Object.prototype.hasOwnProperty.call(row, 'created_via')) {
                row.created_via = this.translateCreatedVia(row.created_via);
            }
            return this.flattenExportObject(this.normalizeDrilldownExportDateFields(row));
        });
    },

    translateCreatedVia(value) {
        const raw = String(value || '').trim().toLowerCase();
        if (raw === 'manual') return 'دستی';
        if (raw === 'excel') return 'اکسل';
        if (raw === 'legacy') return 'قدیمی';
        return String(value || '');
    },

    translateDrilldownDealStatus(value) {
        const raw = String(value || '').trim().toLowerCase();
        if (raw === 'won') return 'موفق';
        if (raw === 'lost') return 'ناموفق';
        if (raw === 'pending') return 'جاری';
        return String(value || '');
    },

    translateDrilldownPipelineStage(value) {
        const raw = String(value || '').trim();
        const map = {
            'new': 'جدید',
            'contact_no_answer': 'تماس بی پاسخ',
            'contact_followup': 'تماس پیگیری',
            'deal_registered': 'ثبت معامله و لینک پرداخت',
            'payment': 'پرداخت',
            'payment_followup': 'پیگیری مانده حساب',
            'course_delivered': 'برگزاری دوره',
            'support': 'پشتیبانی',
            'with_purchase': 'با خرید',
            'without_purchase': 'بدون خرید',
            'refer_crm': 'ارجاع به CRM',
        };
        return map[raw] || raw;
    },

    translateDrilldownPaymentMethod(value) {
        const raw = String(value || '').trim().toLowerCase();
        const map = {
            'card_to_card': 'کارت به کارت',
            'card_reader': 'کارت‌خوان',
            'sheba': 'شبا/پل',
            'shaba': 'شبا/پل',
            'sheba_pol': 'شبا/پل',
            'shaba_pol': 'شبا/پل',
            'cash': 'نقدی',
            'online': 'آنلاین',
            'installment': 'اقساطی',
            'check': 'چک',
            'other': 'سایر',
            'confirmed': 'تاییدشده',
            'pending': 'در انتظار',
            'failed': 'ناموفق'
        };
        return map[raw] || String(value || '');
    },

    buildLeadLookupForExport(rows) {
        const src = Array.isArray(rows) ? rows : [];
        const map = {};
        src.forEach((item) => {
            const row = item && typeof item === 'object' ? item : {};
            const contactIds = [
                String(row.contact_id || '').trim(),
                String(row.contact_didar_id || '').trim(),
            ].filter(Boolean);
            if (!contactIds.length) return;
            const payload = {
                full_name: String(row.full_name || row.lead_name || row.contact_name || '').trim(),
                mobile_phone: String(row.mobile_phone || row.contact_mobile || '').trim(),
                secondary_mobile_phone: String(row.secondary_mobile_phone || '').trim(),
                customer_code: String(row.customer_code || '').trim(),
            };
            contactIds.forEach((id) => {
                map[id] = payload;
            });
        });
        return map;
    },

    prepareDealRowsForExport(rows, leadLookup = {}) {
        const src = Array.isArray(rows) ? rows : [];
        return src.map((item) => {
            const row = item && typeof item === 'object' ? { ...item } : {};
            const paymentDetails = Array.isArray(row.payments_details) ? row.payments_details : [];
            const contactLookup = leadLookup[String(row.contact_id || row.contact_didar_id || '').trim()] || {};
            const leadName = String(
                row.contact_name
                || row.full_name
                || row.lead_name
                || contactLookup.full_name
                || paymentDetails.find((p) => p?.contact_name || p?.full_name || p?.lead_name)?.contact_name
                || ''
            ).trim();
            const leadMobile = String(
                row.contact_mobile
                || row.mobile_phone
                || contactLookup.mobile_phone
                || contactLookup.secondary_mobile_phone
                || paymentDetails.find((p) => p?.contact_mobile || p?.mobile_phone)?.contact_mobile
                || ''
            ).trim();
            const customerCode = String(
                row.customer_code
                || contactLookup.customer_code
                || paymentDetails.find((p) => p?.customer_code)?.customer_code
                || ''
            ).trim();
            const registerDateRaw = row.register_time || row.deal_date || '';
            row.contact_name = leadName;
            row.contact_mobile = leadMobile;
            row.customer_code = customerCode;
            row.invoice_code = row.invoice_code || paymentDetails.find((p) => p?.invoice_code)?.invoice_code || '';
            row.status = this.translateDrilldownDealStatus(row.status);
            row.deal_status_group = this.translateDrilldownDealStatus(row.deal_status_group);
            row.pipeline_stage = this.translateDrilldownPipelineStage(row.pipeline_stage);
            row.payment_method = this.translateDrilldownPaymentMethod(row.payment_method);
            row.payment_type = this.translateDrilldownPaymentMethod(row.payment_type);
            row.acquaintance_source = this.getLookupLabel('lead_sources', row.acquaintance_source || '');
            row.acquaintance_detail = this.getLookupLabel('lead_source_details', row.acquaintance_detail || '');
            row.content_topic = this.getLookupLabel('content_topics', row.content_topic || '');
            row.created_via = this.translateCreatedVia(row.created_via || '');
            if (registerDateRaw) {
                row.register_time_jalali = toPersianDate(registerDateRaw) || row.register_time_jalali || '';
                row.register_time = toPersianDateTime(registerDateRaw) || row.register_time;
            }
            if (row.won_time) {
                row.won_time = toPersianDateTime(row.won_time) || row.won_time;
            }
            if (row.lost_time) {
                row.lost_time = toPersianDateTime(row.lost_time) || row.lost_time;
            }
            return row;
        });
    },

    preparePaymentRowsForExport(rows) {
        const src = Array.isArray(rows) ? rows : [];
        return src.map((item) => {
            const row = item && typeof item === 'object' ? { ...item } : {};
            const titleParts = this.splitDealTitleForDrilldownPaymentExport(row.deal_title || row.title || '');
            const leadName = String(
                row.contact_name || row.full_name || row.lead_name || row.deal_contact_name || ''
            ).trim();
            const leadMobile = String(
                row.contact_mobile || row.mobile_phone || titleParts.phone_number || ''
            ).trim();
            const customerCode = String(row.customer_code || '').trim();
            row.contact_name = leadName;
            row.contact_mobile = leadMobile;
            row.phone_number = row.phone_number || titleParts.phone_number || leadMobile;
            row.service_product = row.service_product || titleParts.service_product || row.products || '';
            row.customer_code = customerCode;
            row.invoice_code = row.invoice_code || '';
            row.deal_status = this.translateDrilldownDealStatus(row.deal_status);
            row.status = this.translateDrilldownDealStatus(row.status);
            row.pipeline_stage = this.translateDrilldownPipelineStage(row.pipeline_stage);
            row.payment_method = this.translateDrilldownPaymentMethod(row.payment_method || row.method);
            row.payment_type = this.translateDrilldownPaymentMethod(row.payment_type);
            row.method = this.translateDrilldownPaymentMethod(row.method || row.payment_method);
            row.payment_status = this.translateDrilldownPaymentMethod(row.payment_status);

            if (row.payment_date) {
                row.payment_date_persian = row.payment_date_persian || toPersianDate(row.payment_date) || '';
                row.payment_date = toPersianDateTime(row.payment_date) || row.payment_date;
            }
            if (row.check_date) {
                row.check_date = this.normalizeCheckDateForExport(row.check_date);
            }
            if (row.created_at) {
                row.created_at = toPersianDateTime(row.created_at) || row.created_at;
            }
            return row;
        });
    },

    buildDealPaymentDetailsMap(paymentsRaw) {
        const map = {};
        const rows = Array.isArray(paymentsRaw) ? paymentsRaw : [];
        const paymentTypeKeys = ['payment_type', 'payment_method', 'method', 'type', 'payment_kind'];
        const addForKey = (key, payment) => {
            const normalizedKey = String(key || '').trim();
            if (!normalizedKey) return;
            if (!map[normalizedKey]) map[normalizedKey] = [];
            map[normalizedKey].push(payment);
        };
        rows.forEach((payment) => {
            const row = payment && typeof payment === 'object' ? payment : {};
            const dealId = String(row.deal_id || '').trim();
            const didarDealId = String(row.didar_deal_id || '').trim();
            addForKey(`deal_id:${dealId}`, row);
            addForKey(`didar_deal_id:${didarDealId}`, row);
        });
        Object.keys(map).forEach((key) => {
            map[key].sort((a, b) => String(a.payment_date || '').localeCompare(String(b.payment_date || '')));
        });
        const summarizeTypes = (list) => {
            const out = new Set();
            list.forEach((p) => {
                paymentTypeKeys.forEach((typeKey) => {
                    const value = String(p?.[typeKey] || '').trim();
                    if (value) out.add(value);
                });
            });
            return Array.from(out).join(' | ');
        };
        return {
            raw: map,
            summarizeTypes
        };
    },

    enrichDealRowsWithPaymentDetails(dealsRaw, paymentsRaw) {
        const dealRows = Array.isArray(dealsRaw) ? dealsRaw : [];
        if (!dealRows.length) return [];
        const paymentMap = this.buildDealPaymentDetailsMap(paymentsRaw);
        return dealRows.map((deal) => {
            const row = deal && typeof deal === 'object' ? { ...deal } : {};
            const dealId = String(row.deal_id || '').trim();
            const didarDealId = String(row.didar_deal_id || '').trim();
            const byDealId = paymentMap.raw[`deal_id:${dealId}`] || [];
            const byDidarId = paymentMap.raw[`didar_deal_id:${didarDealId}`] || [];
            const merged = [];
            const seen = new Set();
            [...byDealId, ...byDidarId].forEach((p) => {
                const fingerprint = JSON.stringify(p || {});
                if (seen.has(fingerprint)) return;
                seen.add(fingerprint);
                merged.push(p);
            });
            const total = merged.reduce((sum, p) => sum + Number(p?.amount || 0), 0);
            const payableAmount = Number(row.payable_amount || 0);
            row.payments_count = merged.length;
            row.payments_total_amount = total;
            row.outstanding_balance = Math.max(payableAmount - total, 0);
            row.payment_types = paymentMap.summarizeTypes(merged);
            row.payments_details = merged;
            row.payments_details_json = JSON.stringify(merged);
            return row;
        });
    },

    buildExpandedDealPaymentColumnsForExport(dealRows) {
        const rows = Array.isArray(dealRows) ? dealRows : [];
        if (!rows.length) return [];
        const paymentFields = [
            'transaction_id', 'payment_date', 'amount', 'payment_status', 'payment_method', 'payment_type',
            'card_last4', 'destination_bank', 'destination_card_number', 'payment_time', 'account_owner_name',
            'card_reader_device', 'currency', 'currency_other', 'currency_amount',
            'check_bank_name', 'check_date', 'check_number', 'check_owner_name', 'description', 'created_at', 'invoice_code', 'payment_source'
        ];
        const maxPayments = rows.reduce((max, row) => {
            const count = Array.isArray(row?.payments_details) ? row.payments_details.length : 0;
            return Math.max(max, count);
        }, 0);
        return rows.map((row) => {
            const out = { ...(row || {}) };
            const details = Array.isArray(out.payments_details) ? out.payments_details : [];
            for (let index = 0; index < maxPayments; index++) {
                const p = details[index] && typeof details[index] === 'object' ? details[index] : {};
                paymentFields.forEach((field) => {
                    let value = (p[field] ?? '');
                    if (field === 'payment_method' || field === 'payment_type' || field === 'payment_status') {
                        value = this.translateDrilldownPaymentMethod(value);
                    } else if (field === 'payment_date' || field === 'created_at') {
                        value = value ? (toPersianDateTime(value) || value) : value;
                    } else if (field === 'check_date') {
                        value = value ? this.normalizeCheckDateForExport(value) : value;
                    }
                    out[`payment_${index + 1}_${field}`] = value;
                });
            }
            delete out.payments_details;
            delete out.payments_details_json;
            return out;
        });
    },

    getDrilldownFieldLabel(datasetKey, fieldKey) {
        const key = String(fieldKey || '');
        const maps = {
            common: {
                title: 'عنوان',
                table_id: 'شناسه جدول',
                metric_key: 'کلید شاخص',
                drilldown_kind: 'نوع Drill-down',
                generated_at: 'زمان تولید',
                amount: 'مبلغ',
                payment_date: 'تاریخ پرداخت (شمسی)',
                payment_date_persian: 'تاریخ پرداخت (شمسی)',
                payment_status: 'وضعیت پرداخت',
                payment_method: 'روش پرداخت',
                payment_type: 'نوع پرداخت',
                payment_source: 'منبع پرداخت',
                phone_number: 'شماره تماس',
                service_product: 'خدمت/محصول',
                products: 'محصولات معامله',
                card_last4: '۴ رقم آخر کارت',
                destination_bank: 'بانک مقصد',
                destination_card_number: 'شماره کارت مقصد',
                payment_time: 'ساعت پرداخت',
                account_owner_name: 'نام صاحب حساب',
                card_reader_device: 'دستگاه کارتخوان',
                currency: 'نوع ارز',
                currency_other: 'ارز (سایر)',
                currency_amount: 'مبلغ ارزی',
                check_bank_name: 'نام بانک چک',
                check_date: 'تاریخ چک (شمسی)',
                check_number: 'شماره چک',
                check_owner_name: 'نام صاحب چک',
                description: 'توضیحات',
                created_at: 'زمان ثبت (شمسی)',
                invoice_code: 'کد فاکتور/حسابداری',
                owner_name: 'کارشناس',
                owner_didar_id: 'شناسه کارشناس',
                didar_deal_id: 'شناسه دیدار معامله',
                deal_id: 'شناسه معامله',
                deal_title: 'عنوان معامله',
                deal_date: 'تاریخ معامله',
                deal_status_group: 'وضعیت معامله',
                register_time_jalali: 'تاریخ ثبت (شمسی)',
                register_time: 'زمان ثبت (شمسی)',
                won_time: 'زمان موفق (شمسی)',
                lost_time: 'زمان ناموفق (شمسی)',
                payable_amount: 'مبلغ قرارداد',
                outstanding_balance: 'مانده حساب',
                total_paid: 'جمع پرداخت',
                has_payment: 'دارای پرداخت',
                contact_didar_id: 'شناسه لید',
                contact_id: 'شناسه لید',
                full_name: 'نام لید',
                lead_name: 'نام لید',
                contact_name: 'نام لید',
                lead_date: 'تاریخ لید',
                mobile_phone: 'موبایل',
                contact_mobile: 'موبایل لید',
                customer_code: 'کد مشتری',
                secondary_mobile_phone: 'موبایل دوم',
                activity_date: 'تاریخ فعالیت',
                activity_type: 'نوع فعالیت',
                activity_duration_minutes: 'مدت فعالیت (دقیقه)',
                payments_count: 'تعداد پرداخت‌ها',
                payments_total_amount: 'جمع مبلغ پرداخت‌ها',
                payment_types: 'انواع پرداخت',
                total_leads: 'تعداد لید',
                total_deals: 'تعداد معامله',
                acquaintance_source: 'شیوه آشنایی',
                acquaintance_detail: 'جزئیات شیوه آشنایی',
                content_topic: 'موضوع محتوا',
                created_by_name: 'ثبت شده توسط',
                created_via: 'روش ثبت',
                deal_count: 'تعداد معاملات',
                won_deal_count: 'تعداد معاملات موفق',
                lost_deal_count: 'تعداد معاملات ناموفق',
                current_deal_count: 'تعداد معاملات جاری',
                total_activities: 'تعداد فعالیت',
                total_payments: 'تعداد پرداخت'
            },
            leads: {},
            deals: {},
            payments: {},
            activities: {}
        };

        const paymentColMatch = key.match(/^payment_(\d+)_(.+)$/);
        if (paymentColMatch) {
            const idx = Number(paymentColMatch[1] || 0);
            const sub = paymentColMatch[2] || '';
            const subFa = maps.common[sub] || sub;
            return `پرداخت ${idx} - ${subFa}`;
        }

        const byDataset = maps[datasetKey] || {};
        return byDataset[key] || maps.common[key] || key;
    },

    buildDrilldownExportDatasets(data) {
        const meta = data?.meta || {};
        const conversion = data?.conversion_analysis || null;
        const datasets = {};
        const leadsRaw = Array.isArray(data?.leads) ? data.leads : [];
        const dealsRaw = Array.isArray(data?.deals) ? data.deals : [];
        const paymentsRaw = Array.isArray(data?.payments) ? data.payments : [];
        const activitiesRaw = Array.isArray(data?.activities) ? data.activities : [];
        const leadLookup = this.buildLeadLookupForExport(leadsRaw);
        const enrichedDealsRaw = this.enrichDealRowsWithPaymentDetails(dealsRaw, paymentsRaw);
        const preparedDealsRaw = this.prepareDealRowsForExport(enrichedDealsRaw, leadLookup);
        const expandedDealsRaw = this.buildExpandedDealPaymentColumnsForExport(preparedDealsRaw);
        const paymentDealLookup = {};
        preparedDealsRaw.forEach((row) => {
            const dealId = String(row?.deal_id || '').trim();
            const didarDealId = String(row?.didar_deal_id || '').trim();
            if (dealId) paymentDealLookup[`deal_id:${dealId}`] = row;
            if (didarDealId) paymentDealLookup[`didar_deal_id:${didarDealId}`] = row;
        });
        const enrichedPaymentsRaw = this.preparePaymentRowsForExport(paymentsRaw.map((payment) => {
            const row = payment && typeof payment === 'object' ? { ...payment } : {};
            const dealId = String(row.deal_id || '').trim();
            const didarDealId = String(row.didar_deal_id || '').trim();
            const dealRow = paymentDealLookup[`deal_id:${dealId}`] || paymentDealLookup[`didar_deal_id:${didarDealId}`] || null;
            if (dealRow) {
                const payableAmount = Number(dealRow.payable_amount || 0);
                const totalPaid = Number(dealRow.payments_total_amount || dealRow.total_paid || 0);
                row.outstanding_balance = Math.max(payableAmount - totalPaid, 0);
                row.contact_name = row.contact_name || dealRow.contact_name || dealRow.full_name || dealRow.lead_name || '';
                row.contact_mobile = row.contact_mobile || dealRow.contact_mobile || dealRow.mobile_phone || '';
                row.customer_code = row.customer_code || dealRow.customer_code || '';
                row.deal_title = row.deal_title || dealRow.deal_title || dealRow.title || '';
                row.products = row.products || dealRow.products || '';
                const titleParts = this.splitDealTitleForDrilldownPaymentExport(row.deal_title);
                row.phone_number = row.phone_number || titleParts.phone_number || row.contact_mobile || '';
                row.service_product = row.service_product || titleParts.service_product || row.products || '';
            } else if (row.outstanding_balance == null) {
                row.outstanding_balance = '';
            }
            return row;
        }));

        datasets.card_summary = {
            label: 'خلاصه کارت و متا',
            rows: this.normalizeDrilldownExportRows([{
                title: meta.title || '',
                table_id: meta.table_id || this.state.kpiDrilldownContext?.tableId || '',
                metric_key: meta.metric_key || this.state.kpiDrilldownContext?.metricKey || '',
                drilldown_kind: meta.drilldown_kind || '',
                total_leads: Number(meta.total_leads || 0),
                total_deals: Number(meta.total_deals || 0),
                total_activities: Number(meta.total_activities || 0),
                total_payments: Number(meta.total_payments || 0),
                generated_at: new Date().toISOString()
            }])
        };

        datasets.leads = { label: 'لیدها', rows: this.normalizeDrilldownExportRows(leadsRaw) };
        datasets.deals = { label: 'معاملات (با جزئیات پرداخت)', rows: this.normalizeDrilldownExportRows(expandedDealsRaw) };
        datasets.payments = { label: 'پرداخت‌ها', rows: this.normalizeDrilldownExportRows(enrichedPaymentsRaw) };
        datasets.activities = { label: 'فعالیت‌ها', rows: this.normalizeDrilldownExportRows(activitiesRaw) };

        if (conversion && typeof conversion === 'object') {
            const summaryRows = this.normalizeDrilldownExportRows([{
                analysis_type: conversion.type || '',
                formula_title: conversion.formula_title || '',
                formula_text: conversion.formula_text || '',
                ...(conversion.summary || {})
            }]);
            if (summaryRows.length > 0) {
                datasets.conversion_summary = { label: 'خلاصه تحلیل نرخ تبدیل', rows: summaryRows };
            }
            const conversionMap = [
                ['included_deals', 'معاملات محاسبه‌شده نرخ تبدیل'],
                ['excluded_deals', 'معاملات حذف‌شده نرخ تبدیل'],
                ['included_leads', 'لیدهای محاسبه‌شده نرخ تبدیل'],
                ['excluded_leads', 'لیدهای حذف‌شده نرخ تبدیل'],
                ['excluded_reasons_summary', 'دلایل حذف/عدم تبدیل']
            ];
            conversionMap.forEach(([key, label]) => {
                const rows = this.normalizeDrilldownExportRows(conversion[key]);
                if (rows.length > 0) {
                    datasets[key] = { label, rows };
                }
            });
        }

        return datasets;
    },

    getDrilldownExportDatasetEntries() {
        const datasets = this.state.kpiDrilldownExport?.datasets || {};
        return Object.entries(datasets).filter(([, item]) => item && Array.isArray(item.rows));
    },

    getPreferredDrilldownExportFieldOrder(datasetKey) {
        const common = [
            'register_time_jalali', 'register_time', 'payment_date_persian', 'payment_date', 'check_date', 'created_at',
            'contact_name', 'customer_code', 'full_name', 'lead_name', 'contact_mobile', 'mobile_phone', 'secondary_mobile_phone',
            'deal_id', 'didar_deal_id', 'deal_title', 'title', 'invoice_code',
            'owner_name', 'owner_didar_id',
            'status', 'deal_status_group', 'pipeline_stage', 'activity_type',
            'service_cost', 'payable_amount', 'payment_amount', 'outstanding_balance', 'amount', 'total_paid',
            'payments_count', 'payments_total_amount', 'payment_types',
        ];
        const datasetSpecific = {
            deals: ['register_time_jalali', 'register_time', 'contact_name', 'customer_code', 'contact_mobile', 'deal_id', 'didar_deal_id', 'title', 'invoice_code', 'status', 'pipeline_stage', 'owner_name', 'acquaintance_source', 'acquaintance_detail', 'content_topic', 'created_by_name', 'created_via', 'products', 'service_cost', 'payable_amount', 'payment_amount', 'outstanding_balance'],
            payments: ['payment_date_persian', 'payment_date', 'contact_name', 'phone_number', 'service_product', 'customer_code', 'contact_mobile', 'deal_id', 'didar_deal_id', 'deal_title', 'invoice_code', 'owner_name', 'amount', 'outstanding_balance', 'payment_status', 'payment_method', 'payment_type'],
            activities: ['activity_date', 'contact_name', 'customer_code', 'contact_mobile', 'deal_id', 'deal_title', 'owner_name', 'activity_type', 'activity_duration_minutes'],
            leads: ['register_time_jalali', 'register_time', 'contact_name', 'customer_code', 'contact_mobile', 'owner_name', 'acquaintance_source', 'acquaintance_detail', 'content_topic', 'created_by_name', 'created_via', 'deal_count', 'won_deal_count', 'lost_deal_count', 'current_deal_count'],
        };
        return [...(datasetSpecific[datasetKey] || []), ...common];
    },

    sortDrilldownExportFields(datasetKey, fields) {
        const preferred = this.getPreferredDrilldownExportFieldOrder(datasetKey);
        const rank = new Map(preferred.map((key, index) => [key, index]));
        return [...fields].sort((left, right) => {
            const l = rank.has(left) ? rank.get(left) : 10000;
            const r = rank.has(right) ? rank.get(right) : 10000;
            if (l !== r) return l - r;
            const leftPayment = left.match(/^payment_(\d+)_(.+)$/);
            const rightPayment = right.match(/^payment_(\d+)_(.+)$/);
            if (leftPayment && rightPayment) {
                const idxDiff = Number(leftPayment[1]) - Number(rightPayment[1]);
                if (idxDiff !== 0) return idxDiff;
                const subRank = new Map(this.getPreferredDrilldownExportFieldOrder(datasetKey).map((key, index) => [key, index]));
                const sl = subRank.has(leftPayment[2]) ? subRank.get(leftPayment[2]) : 10000;
                const sr = subRank.has(rightPayment[2]) ? subRank.get(rightPayment[2]) : 10000;
                if (sl !== sr) return sl - sr;
            }
            return left.localeCompare(right, 'fa');
        });
    },

    buildDrilldownExportSummaryRow(datasetKey, fields, rows) {
        const src = Array.isArray(rows) ? rows : [];
        if (!src.length) return null;
        const numericFields = new Set(['service_cost', 'payable_amount', 'payment_amount', 'outstanding_balance', 'amount', 'total_paid', 'payments_count', 'payments_total_amount', 'activity_duration_minutes']);
        const countLikeFields = new Set(['deal_id', 'didar_deal_id', 'transaction_id', 'contact_name', 'contact_mobile']);
        const summary = {};
        const totals = {};
        fields.forEach((field, index) => {
            if (index === 0) {
                summary[field] = 'جمع / تحلیل';
                return;
            }
            if (numericFields.has(field) || /^payment_\d+_(amount|currency_amount)$/.test(field)) {
                const total = src.reduce((sum, row) => sum + Number(row?.[field] || 0), 0);
                summary[field] = total ? total : '';
                totals[field] = total;
                return;
            }
            if (countLikeFields.has(field)) {
                const count = src.reduce((sum, row) => sum + (this.isExportEmptyValue(row?.[field]) ? 0 : 1), 0);
                summary[field] = count ? `تعداد: ${count}` : '';
            }
        });

        const notes = [];
        if (datasetKey === 'deals') {
            const count = src.length;
            const payable = Number(totals.payable_amount || 0);
            const paid = Number(totals.payment_amount || 0);
            const outstanding = Number(totals.outstanding_balance || 0);
            notes.push(`تعداد معاملات: ${count}`);
            if (count > 0) notes.push(`میانگین مبلغ قرارداد: ${Math.round((payable / count) * 100) / 100}`);
            if (payable > 0) notes.push(`نسبت وصول: ${Math.round((paid / payable) * 10000) / 100}%`);
            notes.push(`جمع مانده حساب: ${Math.round(outstanding * 100) / 100}`);
        } else if (datasetKey === 'payments') {
            const count = src.length;
            const amount = Number(totals.amount || 0);
            const outstanding = Number(totals.outstanding_balance || 0);
            notes.push(`تعداد پرداخت: ${count}`);
            if (count > 0) notes.push(`میانگین مبلغ پرداخت: ${Math.round((amount / count) * 100) / 100}`);
            notes.push(`جمع مبالغ پرداخت: ${Math.round(amount * 100) / 100}`);
            notes.push(`جمع مانده حساب: ${Math.round(outstanding * 100) / 100}`);
        } else if (datasetKey === 'activities') {
            const count = src.length;
            const duration = Number(totals.activity_duration_minutes || 0);
            const avgDuration = count > 0 ? (duration / count) : 0;
            const callCount = src.reduce((sum, row) => {
                const type = String(row?.activity_type || row?.activity_type_title || '').trim();
                return sum + (type.includes('تماس') ? 1 : 0);
            }, 0);
            const supportCount = src.reduce((sum, row) => {
                const type = String(row?.activity_type || row?.activity_type_title || '').trim();
                return sum + (type.includes('پشتیبانی') ? 1 : 0);
            }, 0);
            const courseCount = src.reduce((sum, row) => {
                const type = String(row?.activity_type || row?.activity_type_title || '').trim();
                return sum + ((type.includes('دوره') || type.includes('برگزاری')) ? 1 : 0);
            }, 0);
            notes.push(`تعداد فعالیت: ${count}`);
            notes.push(`مجموع مدت: ${Math.round(duration * 100) / 100} دقیقه`);
            notes.push(`میانگین مدت: ${Math.round(avgDuration * 100) / 100} دقیقه`);
            notes.push(`تعداد تماس: ${callCount}`);
            notes.push(`تعداد پشتیبانی: ${supportCount}`);
            notes.push(`تعداد برگزاری دوره: ${courseCount}`);
        }
        if (notes.length) {
            let noteIndex = 1;
            for (const note of notes) {
                while (noteIndex < fields.length && !this.isExportEmptyValue(summary[fields[noteIndex]])) {
                    noteIndex += 1;
                }
                if (noteIndex >= fields.length) break;
                summary[fields[noteIndex]] = note;
                noteIndex += 1;
            }
        }
        return summary;
    },

    isDrilldownNumericExportField(field) {
        return [
            'service_cost', 'payable_amount', 'payment_amount', 'outstanding_balance',
            'amount', 'total_paid', 'payments_count', 'payments_total_amount', 'currency_amount'
        ].includes(String(field || '')) || /^payment_\d+_(amount|currency_amount)$/.test(String(field || ''));
    },

    formatDrilldownExportNumber(num) {
        if (num === null || num === undefined || num === '') return '0';
        return new Intl.NumberFormat('en-US').format(num);
    },

    normalizeCheckDateForExport(value) {
        const raw = this.toEnglishDigits(String(value || '').trim());
        if (!raw) return '';
        const normalized = raw.replace(/[.\-]/g, '/');
        const match = normalized.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/);
        if (match) {
            const year = Number(match[1]);
            const month = Number(match[2]);
            const day = Number(match[3]);
            if (year >= 1200 && year <= 1600 && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
                return `${String(year).padStart(4, '0')}/${String(month).padStart(2, '0')}/${String(day).padStart(2, '0')}`;
            }
        }
        return this.toEnglishDigits(toPersianDate(raw) || raw);
    },

    splitDealTitleForDrilldownPaymentExport(title) {
        const normalized = this.toEnglishDigits(String(title || '').trim()).replace(/\s+/g, ' ');
        let baseTitle = normalized;
        let serviceProduct = '';
        if (normalized.includes('|')) {
            const parts = normalized.split('|');
            baseTitle = (parts.shift() || '').trim();
            serviceProduct = parts.join('|').trim();
        }

        let phoneNumber = '';
        const match = baseTitle.match(/(?:\+?98|0)?9\d{9}/);
        if (match) {
            phoneNumber = match[0];
            if (phoneNumber.startsWith('+98') && phoneNumber.length === 13) {
                phoneNumber = `0${phoneNumber.slice(3)}`;
            } else if (phoneNumber.startsWith('98') && phoneNumber.length === 12) {
                phoneNumber = `0${phoneNumber.slice(2)}`;
            } else if (phoneNumber.length === 10 && phoneNumber.startsWith('9')) {
                phoneNumber = `0${phoneNumber}`;
            }
        }

        return { phone_number: phoneNumber, service_product: serviceProduct };
    },

    normalizeDrilldownExportCell(value) {
        if (value === null || typeof value === 'undefined') return '';
        if (typeof value === 'number') return value;
        if (typeof value === 'boolean') return value ? '1' : '0';
        return this.toEnglishDigits(String(value));
    },

    formatDrilldownExportValue(field, value) {
        if (this.isExportEmptyValue(value)) return '';
        if (!this.isDrilldownNumericExportField(field)) return value;
        const num = Number(value || 0);
        if (!Number.isFinite(num)) return value;
        return this.formatDrilldownExportNumber(num);
    },

    getDrilldownExportFieldKeys(datasetKey) {
        const datasets = this.state.kpiDrilldownExport?.datasets || {};
        const rows = Array.isArray(datasets?.[datasetKey]?.rows) ? datasets[datasetKey].rows : [];
        const keys = [];
        const seen = new Set();
        rows.forEach((row) => {
            Object.keys(row || {}).forEach((key) => {
                if (seen.has(key)) return;
                seen.add(key);
                keys.push(key);
            });
        });
        return this.sortDrilldownExportFields(
            datasetKey,
            keys.filter((key) => rows.some((row) => !this.isExportEmptyValue(row?.[key])))
        );
    },

    isExportEmptyValue(value) {
        if (value === null || typeof value === 'undefined') return true;
        if (typeof value === 'number') return false;
        if (typeof value === 'boolean') return false;
        if (Array.isArray(value)) return value.length === 0;
        if (typeof value === 'object') return Object.keys(value).length === 0;
        const str = String(value).trim();
        return str === '';
    },

    renderDrilldownExportControls(data) {
        if (!this.isAdmin()) return '';
        const datasets = this.buildDrilldownExportDatasets(data);
        const entries = Object.entries(datasets);
        if (entries.length === 0) return '';

        this.state.kpiDrilldownExport = this.state.kpiDrilldownExport || { datasets: {}, selectedDataset: '' };
        this.state.kpiDrilldownExport.datasets = datasets;
        if (!this.state.kpiDrilldownExport.selectedDataset || !datasets[this.state.kpiDrilldownExport.selectedDataset]) {
            this.state.kpiDrilldownExport.selectedDataset = entries[0][0];
        }
        const selectedKey = this.state.kpiDrilldownExport.selectedDataset;
        const fieldKeys = this.getDrilldownExportFieldKeys(selectedKey);

        return `
            <div class="bg-gray-900/60 border border-gray-700 rounded p-3 mb-4">
                <div class="text-xs text-gray-300 font-bold mb-2">خروجی CSV (فقط ادمین)</div>
                <div class="grid grid-cols-1 lg:grid-cols-3 gap-3 items-start">
                    <div>
                        <label class="block text-xs text-gray-400 mb-1">نوع دیتای خروجی</label>
                        <select id="drill-export-dataset" class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-white text-xs" onchange="app.refreshDrilldownExportFieldOptions()">
                            ${entries.map(([key, item]) => `<option value="${this.escapeHtml(key)}" ${key === selectedKey ? 'selected' : ''}>${this.escapeHtml(item.label || key)} (${this.formatNumber((item.rows || []).length)})</option>`).join('')}
                        </select>
                    </div>
                    <div class="lg:col-span-2">
                        <div class="flex items-center justify-between mb-1">
                            <label class="block text-xs text-gray-400">فیلدهای خروجی</label>
                            <div class="flex gap-2">
                                <button type="button" class="px-2 py-1 text-[11px] rounded bg-gray-700 hover:bg-gray-600 text-white" onclick="app.setDrilldownExportAllFields(true)">انتخاب همه</button>
                                <button type="button" class="px-2 py-1 text-[11px] rounded bg-gray-700 hover:bg-gray-600 text-white" onclick="app.setDrilldownExportAllFields(false)">حذف همه</button>
                            </div>
                        </div>
                        <div id="drill-export-fields" class="max-h-36 overflow-y-auto border border-gray-700 rounded p-2 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-1 text-xs">
                            ${fieldKeys.map((field) => `
                                <label class="flex items-center gap-2 text-gray-200">
                                    <input type="checkbox" class="drill-export-field" value="${this.escapeHtml(field)}" checked>
                                    <span>${this.escapeHtml(this.getDrilldownFieldLabel(selectedKey, field))}</span>
                                </label>
                            `).join('')}
                        </div>
                    </div>
                </div>
                <div class="mt-3 flex justify-end gap-2">
                    <button type="button" class="px-3 py-1.5 text-xs rounded bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-900/30 border border-blue-400/40" onclick="app.exportCurrentDrilldownCsv()">خروجی CSV</button>
                    <button type="button" class="px-3 py-1.5 text-xs rounded bg-emerald-600 hover:bg-emerald-500 text-white shadow-lg shadow-emerald-900/30 border border-emerald-400/40" onclick="app.exportCurrentDrilldownXlsx()">خروجی Excel</button>
                </div>
            </div>
        `;
    },

    renderPerformanceActivityPerformanceSection(activityPerformance) {
        const pastCards = Array.isArray(activityPerformance?.past_deals) ? activityPerformance.past_deals : [];
        const dailyCards = Array.isArray(activityPerformance?.daily) ? activityPerformance.daily : [];
        if (!pastCards.length && !dailyCards.length) {
            return '';
        }

        const renderCard = (card, borderClass) => {
            const countValue = Number(card?.count || 0);
            const duration = Number(card?.duration_minutes || 0);
            const amount = Number(card?.amount || 0);
            const subCount = Number(card?.sub_count || 0);
            const subAmount = Number(card?.sub_amount || 0);
            const stageCount = Number(card?.stage_count || 0);
            const stageMetricKey = String(card?.stage_metric_key || '').trim();
            const primaryValueType = String(card?.primary_value_type || 'count').trim().toLowerCase() === 'amount'
                ? 'amount'
                : 'count';
            const primaryValueHtml = primaryValueType === 'amount'
                ? `${this.formatNumber(amount)} <span class="text-sm font-medium text-gray-300">تومان</span>`
                : this.formatNumber(countValue);
            const lines = [];
            if (duration > 0) {
                lines.push(`مدت: ${this.formatNumber(duration)} دقیقه`);
            }
            if (primaryValueType !== 'amount' && amount > 0) {
                lines.push(`مبلغ: ${this.formatNumber(amount)} تومان`);
            }
            if (subCount > 0) {
                lines.push(`${this.escapeHtml(card?.sub_count_label || 'زیرمقدار')}: ${this.formatNumber(subCount)}`);
            }
            if (subAmount > 0) {
                lines.push(`${this.escapeHtml(card?.sub_amount_label || 'زیرمبلغ')}: ${this.formatNumber(subAmount)} تومان`);
            }
            if (stageMetricKey) {
                lines.push(`
                    <button type="button" class="text-[11px] text-cyan-300 hover:text-cyan-200" onclick="event.stopPropagation(); app.openPerformanceChartDrilldown('performance_cards','${stageMetricKey}', {})">
                        تعداد مرحله کاریز مشابه: ${this.formatNumber(stageCount)}
                    </button>
                `);
            }
            const metricKey = card?.key || '';
            const isClickable = metricKey !== '';
            const clickHandler = isClickable
                ? `onclick="app.openPerformanceChartDrilldown('performance_cards','${metricKey}', {})"`
                : '';
            return `
                <div class="glass p-3 border-r-4 ${borderClass} ${isClickable ? 'cursor-pointer hover:border-cyan-400 transition-colors' : ''}" ${clickHandler}>
                    <div class="text-[11px] text-gray-400 mb-1">${this.escapeHtml(card?.title || '-')}</div>
                    <div class="text-lg font-bold text-white leading-7 mb-1">${primaryValueHtml}</div>
                    ${lines.map(line => `<div class="text-[11px] text-gray-500">${line}</div>`).join('')}
                    ${isClickable ? '<div class="text-[10px] text-cyan-300 mt-2">برای مشاهده جزئیات کلیک کنید</div>' : ''}
                </div>
            `;
        };

        return `
            <div class="glass p-4 mb-6 border border-gray-700/70">
                <div class="text-sm text-gray-200 font-bold mb-4">عملکرد فعالیت‌ها</div>
                <div class="mb-5">
                    <div class="text-xs text-gray-400 mb-3">فعالیت‌های معاملات گذشته</div>
                    <div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">
                        ${pastCards.map(card => renderCard(card, 'border-indigo-500')).join('')}
                    </div>
                </div>
                <div>
                    <div class="text-xs text-gray-400 mb-3">فعالیت‌های روز</div>
                    <div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-6 gap-3">
                        ${dailyCards.map(card => renderCard(card, 'border-sky-500')).join('')}
                    </div>
                </div>
            </div>
        `;
    },

    refreshDrilldownExportFieldOptions() {
        if (!this.isAdmin()) return;
        const select = document.getElementById('drill-export-dataset');
        const container = document.getElementById('drill-export-fields');
        if (!select || !container) return;
        const selectedKey = String(select.value || '');
        if (!selectedKey) return;
        this.state.kpiDrilldownExport = this.state.kpiDrilldownExport || { datasets: {}, selectedDataset: '' };
        this.state.kpiDrilldownExport.selectedDataset = selectedKey;
        const fields = this.getDrilldownExportFieldKeys(selectedKey);
        if (!fields.length) {
            container.innerHTML = '<div class="text-gray-500">فیلدی برای خروجی وجود ندارد.</div>';
            return;
        }
        container.innerHTML = fields.map((field) => `
            <label class="flex items-center gap-2 text-gray-200">
                <input type="checkbox" class="drill-export-field" value="${this.escapeHtml(field)}" checked>
                <span>${this.escapeHtml(this.getDrilldownFieldLabel(selectedKey, field))}</span>
            </label>
        `).join('');
    },

    setDrilldownExportAllFields(checked) {
        const boxes = document.querySelectorAll('#drill-export-fields .drill-export-field');
        boxes.forEach((box) => { box.checked = !!checked; });
    },

    buildCurrentDrilldownExportTable() {
        if (!this.isAdmin()) {
            Swal.fire('خطا', 'این قابلیت فقط برای ادمین فعال است', 'error');
            return null;
        }
        const datasets = this.state.kpiDrilldownExport?.datasets || {};
        const select = document.getElementById('drill-export-dataset');
        const datasetKey = String(select?.value || this.state.kpiDrilldownExport?.selectedDataset || '');
        const dataset = datasets?.[datasetKey];
        if (!dataset || !Array.isArray(dataset.rows) || dataset.rows.length === 0) {
            Swal.fire('خطا', 'برای این بخش داده‌ای جهت خروجی وجود ندارد', 'error');
            return null;
        }
        const selectedFields = Array.from(document.querySelectorAll('#drill-export-fields .drill-export-field:checked'))
            .map((input) => String(input.value || '').trim())
            .filter(Boolean);
        if (selectedFields.length === 0) {
            Swal.fire('خطا', 'حداقل یک فیلد برای خروجی انتخاب کنید', 'error');
            return null;
        }
        const effectiveFields = selectedFields.filter((field) => dataset.rows.some((row) => !this.isExportEmptyValue(row?.[field])));
        const enforceCustomerCodeDatasets = new Set(['leads', 'deals', 'payments', 'activities']);
        if (enforceCustomerCodeDatasets.has(datasetKey)) {
            const hasAnyCustomerCode = dataset.rows.some((row) => !this.isExportEmptyValue(row?.customer_code));
            if (hasAnyCustomerCode && !effectiveFields.includes('customer_code')) {
                effectiveFields.push('customer_code');
            }
        }
        if (effectiveFields.length === 0) {
            Swal.fire('خطا', 'فیلد انتخاب‌شده با داده غیرخالی وجود ندارد', 'error');
            return null;
        }
        const orderedFields = this.sortDrilldownExportFields(datasetKey, effectiveFields);
        const headerRow = orderedFields.map((field) => this.normalizeDrilldownExportCell(this.getDrilldownFieldLabel(datasetKey, field)));
        const bodyRows = [];
        dataset.rows.forEach((row) => {
            bodyRows.push(orderedFields.map((field) => {
                const value = this.formatDrilldownExportValue(field, row?.[field]);
                if (Array.isArray(value) || (value && typeof value === 'object')) {
                    return this.normalizeDrilldownExportCell(JSON.stringify(value));
                }
                return this.normalizeDrilldownExportCell(value ?? '');
            }));
        });
        const summaryRow = this.buildDrilldownExportSummaryRow(datasetKey, orderedFields, dataset.rows);
        if (summaryRow) {
            bodyRows.push(orderedFields.map((field) => this.normalizeDrilldownExportCell(this.formatDrilldownExportValue(field, summaryRow[field] ?? ''))));
        }
        const ctx = this.state.kpiDrilldownContext || {};
        const ts = new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-');
        const base = `drill_${ctx.mode || 'kpi'}_${ctx.tableId || 'table'}_${ctx.metricKey || 'metric'}_${datasetKey}_${ts}`.replace(/[^\w\-]+/g, '_');
        return { datasetKey, fields: orderedFields, headerRow, bodyRows, base };
    },

    exportCurrentDrilldownCsv() {
        const table = this.buildCurrentDrilldownExportTable();
        if (!table) return;
        const lines = [
            table.headerRow.map((value) => this.csvEscape(value)).join(','),
            ...table.bodyRows.map((row) => row.map((value) => this.csvEscape(value ?? '')).join(','))
        ];
        const csvText = "\uFEFF" + lines.join('\n');
        const blob = new Blob([csvText], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `${table.base}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(link.href);
    },

    exportCurrentDrilldownXlsx() {
        const table = this.buildCurrentDrilldownExportTable();
        if (!table) return;
        if (typeof XLSX === 'undefined') {
            Swal.fire('خطا', 'کتابخانه خروجی Excel بارگذاری نشده است', 'error');
            return;
        }
        const aoa = [table.headerRow, ...table.bodyRows];
        const worksheet = XLSX.utils.aoa_to_sheet(aoa);
        worksheet['!autofilter'] = { ref: XLSX.utils.encode_range({ s: { r: 0, c: 0 }, e: { r: Math.max(0, aoa.length - 1), c: Math.max(0, table.headerRow.length - 1) } }) };
        worksheet['!cols'] = table.headerRow.map((header, index) => {
            const maxLen = aoa.reduce((max, row) => Math.max(max, String(row[index] ?? '').length), String(header || '').length);
            return { wch: Math.min(Math.max(maxLen + 2, 10), 45) };
        });
        const workbook = XLSX.utils.book_new();
        workbook.Workbook = { Views: [{ RTL: true }] };
        XLSX.utils.book_append_sheet(workbook, worksheet, 'Drilldown');
        XLSX.writeFile(workbook, `${table.base}.xlsx`, { bookType: 'xlsx', compression: true });
    },

    renderKpiDrilldownContent(data) {
        const leads = Array.isArray(data?.leads) ? data.leads : [];
        const deals = Array.isArray(data?.deals) ? data.deals : [];
        const activities = Array.isArray(data?.activities) ? data.activities : [];
        const payments = Array.isArray(data?.payments) ? data.payments : [];
        const meta = data?.meta || {};
        const conversionAnalysis = data?.conversion_analysis || null;

        if (meta?.drilldown_kind === 'conversion_analysis' && conversionAnalysis) {
            return this.renderConversionAnalysisDrilldownContent(data);
        }

        const summaryHtml = `
            <div class="grid grid-cols-1 md:grid-cols-4 gap-3">
                <div class="bg-gray-800 border border-gray-700 rounded p-3 text-sm">
                    <div class="text-gray-400">تعداد لید مرتبط</div>
                    <div class="text-cyan-300 font-bold text-lg">${this.formatNumber(Number(meta.total_leads || leads.length || 0))}</div>
                </div>
                <div class="bg-gray-800 border border-gray-700 rounded p-3 text-sm">
                    <div class="text-gray-400">تعداد معامله مرتبط</div>
                    <div class="text-cyan-300 font-bold text-lg">${this.formatNumber(Number(meta.total_deals || deals.length || 0))}</div>
                </div>
                <div class="bg-gray-800 border border-gray-700 rounded p-3 text-sm">
                    <div class="text-gray-400">تعداد فعالیت مرتبط</div>
                    <div class="text-cyan-300 font-bold text-lg">${this.formatNumber(Number(meta.total_activities || activities.length || 0))}</div>
                </div>
                <div class="bg-gray-800 border border-gray-700 rounded p-3 text-sm">
                    <div class="text-gray-400">تعداد پرداخت مرتبط</div>
                    <div class="text-cyan-300 font-bold text-lg">${this.formatNumber(Number(meta.total_payments || payments.length || 0))}</div>
                </div>
            </div>
        `;

        const leadsHtml = leads.length === 0
            ? '<div class="text-xs text-gray-500">لید مرتبطی پیدا نشد.</div>'
            : `
                <div class="overflow-x-auto border border-gray-700 rounded">
                    <table class="w-full text-xs">
                        <thead class="bg-gray-800">
                            <tr>
                                <th class="p-2 border border-gray-700">نام لید</th>
                                <th class="p-2 border border-gray-700">شماره</th>
                                <th class="p-2 border border-gray-700">تاریخ ثبت</th>
                                <th class="p-2 border border-gray-700">مسئول</th>
                                <th class="p-2 border border-gray-700">عملیات</th>
                            </tr>
                        </thead>
                        <tbody>
                            ${leads.map(lead => `
                                <tr class="hover:bg-gray-800/60">
                                    <td class="p-2 border border-gray-700">${this.escapeHtml(lead.full_name || '-')}</td>
                                    <td class="p-2 border border-gray-700">${this.escapeHtml(lead.mobile_phone || lead.secondary_mobile_phone || '-')}</td>
                                    <td class="p-2 border border-gray-700">${this.formatKpiDate(lead.lead_date || '')}</td>
                                    <td class="p-2 border border-gray-700">${this.escapeHtml(lead.owner_name || '-')}</td>
                                    <td class="p-2 border border-gray-700 text-center">
                                        <button class="text-blue-300 hover:text-blue-200 underline" onclick="app.openLeadFromKpiDrilldown('${this.escapeHtml(lead.contact_id || lead.contact_didar_id || '')}')">باز کردن لید</button>
                                    </td>
                                </tr>
                            `).join('')}
                        </tbody>
                    </table>
                </div>
            `;

        const dealsHtml = deals.length === 0
            ? '<div class="text-xs text-gray-500">معامله مرتبطی پیدا نشد.</div>'
            : `
                <div class="overflow-x-auto border border-gray-700 rounded">
                    <table class="w-full text-xs">
                        <thead class="bg-gray-800">
                            <tr>
                                <th class="p-2 border border-gray-700">عنوان معامله</th>
                                <th class="p-2 border border-gray-700">تاریخ معامله</th>
                                <th class="p-2 border border-gray-700">وضعیت</th>
                                <th class="p-2 border border-gray-700">مبلغ قرارداد</th>
                                <th class="p-2 border border-gray-700">جمع پرداخت</th>
                                <th class="p-2 border border-gray-700">عملیات</th>
                            </tr>
                        </thead>
                        <tbody>
                            ${deals.map(deal => `
                                <tr class="hover:bg-gray-800/60">
                                    <td class="p-2 border border-gray-700">${this.escapeHtml(deal.title || ('#' + (deal.deal_id || '')))}</td>
                                    <td class="p-2 border border-gray-700">${this.formatKpiDate(deal.deal_date || '')}</td>
                                    <td class="p-2 border border-gray-700">${this.escapeHtml(deal.deal_status_group || '-')}</td>
                                    <td class="p-2 border border-gray-700">${this.formatNumber(Number(deal.payable_amount || 0))}</td>
                                    <td class="p-2 border border-gray-700">${this.formatNumber(Number(deal.total_paid || 0))}</td>
                                    <td class="p-2 border border-gray-700 text-center">
                                        <button class="text-blue-300 hover:text-blue-200 underline" onclick="app.openDealFromKpiDrilldown('${this.escapeHtml(deal.didar_deal_id || deal.deal_id || '')}')">باز کردن معامله</button>
                                    </td>
                                </tr>
                            `).join('')}
                        </tbody>
                    </table>
                </div>
            `;

        const activitiesHtml = activities.length === 0
            ? ''
            : `
                <div class="space-y-2">
                    <div class="text-sm font-bold text-indigo-300">فعالیت‌های مرتبط</div>
                    <div class="overflow-x-auto border border-gray-700 rounded">
                        <table class="w-full text-xs">
                            <thead class="bg-gray-800">
                                <tr>
                                    <th class="p-2 border border-gray-700">تاریخ</th>
                                    <th class="p-2 border border-gray-700">نوع فعالیت</th>
                                    <th class="p-2 border border-gray-700">مدت (دقیقه)</th>
                                    <th class="p-2 border border-gray-700">نام لید</th>
                                    <th class="p-2 border border-gray-700">شماره</th>
                                    <th class="p-2 border border-gray-700">مسئول</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${activities.map(act => `
                                    <tr class="hover:bg-gray-800/60">
                                        <td class="p-2 border border-gray-700">${this.formatKpiDate(act.activity_date || '')}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(act.activity_type || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(act.activity_duration_minutes || 0))}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(act.lead_name || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(act.mobile_phone || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(act.owner_name || '-')}</td>
                                    </tr>
                                `).join('')}
                            </tbody>
                        </table>
                    </div>
                </div>
            `;

        const paymentsHtml = payments.length === 0
            ? ''
            : `
                <div class="space-y-2">
                    <div class="text-sm font-bold text-indigo-300">پرداخت‌های مرتبط</div>
                    <div class="overflow-x-auto border border-gray-700 rounded">
                        <table class="w-full text-xs">
                            <thead class="bg-gray-800">
                                <tr>
                                    <th class="p-2 border border-gray-700">تاریخ پرداخت</th>
                                    <th class="p-2 border border-gray-700">مبلغ</th>
                                    <th class="p-2 border border-gray-700">عنوان معامله</th>
                                    <th class="p-2 border border-gray-700">مسئول</th>
                                    <th class="p-2 border border-gray-700">عملیات</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${payments.map(pay => `
                                    <tr class="hover:bg-gray-800/60">
                                        <td class="p-2 border border-gray-700">${this.formatKpiDate(pay.payment_date || '')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(pay.amount || 0))}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(pay.deal_title || ('#' + (pay.deal_id || '')))}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(pay.owner_name || '-')}</td>
                                        <td class="p-2 border border-gray-700 text-center">
                                            <button class="text-blue-300 hover:text-blue-200 underline" onclick="app.openDealFromKpiDrilldown('${this.escapeHtml(pay.didar_deal_id || pay.deal_id || '')}')">باز کردن معامله</button>
                                        </td>
                                    </tr>
                                `).join('')}
                            </tbody>
                        </table>
                    </div>
                </div>
            `;

        return `
            ${this.renderDrilldownCriteriaControls()}
            ${this.renderDrilldownExportControls(data)}
            ${summaryHtml}
            <div class="space-y-2">
                <div class="text-sm font-bold text-indigo-300">لیدهای مرتبط</div>
                ${leadsHtml}
            </div>
            <div class="space-y-2">
                <div class="text-sm font-bold text-indigo-300">معاملات مرتبط</div>
                ${dealsHtml}
            </div>
            ${activitiesHtml}
            ${paymentsHtml}
        `;
    },

    renderConversionAnalysisDrilldownContent(data) {
        const meta = data?.meta || {};
        const analysis = data?.conversion_analysis || {};
        const summary = analysis.summary || {};
        const reasons = Array.isArray(analysis.excluded_reasons_summary) ? analysis.excluded_reasons_summary : [];
        const includedDeals = Array.isArray(analysis.included_deals) ? analysis.included_deals : [];
        const excludedDeals = Array.isArray(analysis.excluded_deals) ? analysis.excluded_deals : [];
        const includedLeads = Array.isArray(analysis.included_leads) ? analysis.included_leads : [];
        const excludedLeads = Array.isArray(analysis.excluded_leads) ? analysis.excluded_leads : [];
        const criteria = analysis.criteria || {};

        const metricCards = `
            <div class="grid grid-cols-2 md:grid-cols-3 gap-3">
                ${this.renderConversionMetricCard('کل جامعه اولیه', summary.raw_cohort_count)}
                ${this.renderConversionMetricCard('حذف‌شده از محاسبه', summary.excluded_count)}
                ${this.renderConversionMetricCard('جامعه معتبر', summary.valid_cohort_count)}
                ${this.renderConversionMetricCard('تبدیل‌شده', summary.converted_count)}
                ${this.renderConversionMetricCard('تبدیل‌نشده', summary.not_converted_count)}
                ${this.renderConversionMetricCard('نرخ تبدیل', `${this.formatNumber(summary.conversion_rate || 0)}%`)}
            </div>
        `;

        const formulaHtml = `
            <div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
                <div class="bg-gray-800 border border-gray-700 rounded p-3">
                    <div class="text-sm font-bold text-yellow-300 mb-2">${this.escapeHtml(analysis.formula_title || 'فرمول محاسبه')}</div>
                    <div class="text-sm text-gray-200 leading-7">${this.escapeHtml(analysis.formula_text || '-')}</div>
                </div>
                <div class="bg-gray-800 border border-gray-700 rounded p-3">
                    <div class="text-sm font-bold text-cyan-300 mb-2">معیارها و فیلترهای فعال</div>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-2 text-xs">
                        ${this.renderConversionCriteriaItem('از تاریخ', this.formatKpiDate(criteria.date_from || ''))}
                        ${this.renderConversionCriteriaItem('تا تاریخ', this.formatKpiDate(criteria.date_to || ''))}
                        ${this.renderConversionCriteriaItem('مبنای تاریخ فروش', this.translateKpiCriteriaValue('sales_date_basis', criteria.sales_date_basis))}
                        ${this.renderConversionCriteriaItem('قاعده احتساب فروش', this.translateKpiCriteriaValue('sales_count_rule', criteria.sales_count_rule))}
                        ${this.renderConversionCriteriaItem('مبنای تاریخ مالی', this.translateKpiCriteriaValue('payment_date_basis', criteria.payment_date_basis))}
                        ${this.renderConversionCriteriaItem('قاعده احتساب مالی', this.translateKpiCriteriaValue('payment_count_rule', criteria.payment_count_rule))}
                        ${this.renderConversionCriteriaItem('وضعیت پرداخت', this.translateKpiCriteriaValue('payment_status_scope', criteria.payment_status_scope))}
                        ${this.renderConversionCriteriaItem('Legacy', this.translateKpiCriteriaValue('legacy_deal_scope', criteria.legacy_deal_scope))}
                    </div>
                </div>
            </div>
        `;

        const reasonsHtml = `
            <div class="space-y-2">
                <div class="text-sm font-bold text-orange-300">دلایل خروج از محاسبه / عدم تبدیل</div>
                ${reasons.length === 0 ? '<div class="text-xs text-gray-500">دلیلی ثبت نشده است.</div>' : `
                    <div class="overflow-x-auto border border-gray-700 rounded">
                        <table class="w-full text-xs">
                            <thead class="bg-gray-800">
                                <tr>
                                    <th class="p-2 border border-gray-700">عنوان دلیل</th>
                                    <th class="p-2 border border-gray-700">تعداد</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${reasons.map(r => `
                                    <tr class="hover:bg-gray-800/60">
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(r.label || r.key || 'نامشخص')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(r.count || 0))}</td>
                                    </tr>
                                `).join('')}
                            </tbody>
                        </table>
                    </div>
                `}
            </div>
        `;

        const includedSection = analysis.type === 'lead'
            ? this.renderConversionLeadTable('لیدهای محاسبه‌شده (تبدیل‌شده)', includedLeads, false)
            : this.renderConversionDealTable('معاملات محاسبه‌شده', includedDeals, false);

        const excludedSection = analysis.type === 'lead'
            ? this.renderConversionLeadTable('لیدهای خارج از تبدیل / محاسبه‌نشده', excludedLeads, true)
            : this.renderConversionDealTable('معاملات خارج از محاسبه / تبدیل‌نشده', excludedDeals, true);

        const compatibilityBlocks = `
            ${Array.isArray(data?.deals) && data.deals.length ? `
                <details class="bg-gray-800 border border-gray-700 rounded p-3">
                    <summary class="cursor-pointer text-sm text-indigo-300">لیست معاملات مرتبط برای بررسی سریع</summary>
                    <div class="mt-3">${this.renderConversionCompactDeals(data.deals)}</div>
                </details>
            ` : ''}
            ${Array.isArray(data?.leads) && data.leads.length ? `
                <details class="bg-gray-800 border border-gray-700 rounded p-3">
                    <summary class="cursor-pointer text-sm text-indigo-300">لیست لیدهای مرتبط برای بررسی سریع</summary>
                    <div class="mt-3">${this.renderConversionCompactLeads(data.leads)}</div>
                </details>
            ` : ''}
        `;

        return `
            ${this.renderDrilldownCriteriaControls()}
            ${this.renderDrilldownExportControls(data)}
            <div class="space-y-4">
                <div class="bg-gray-800 border border-gray-700 rounded p-3">
                    <div class="text-sm font-bold text-cyan-300 mb-1">${this.escapeHtml(meta.title || 'جزئیات نرخ تبدیل')}</div>
                    <div class="text-xs text-gray-400">در این بخش دقیقاً مشخص می‌شود چه رکوردهایی در محاسبه وارد شده‌اند، چه رکوردهایی حذف شده‌اند و علت هر مورد چیست.</div>
                </div>
                ${metricCards}
                ${formulaHtml}
                ${reasonsHtml}
                ${includedSection}
                ${excludedSection}
                ${compatibilityBlocks}
            </div>
        `;
    },

    renderConversionMetricCard(label, value) {
        return `
            <div class="bg-gray-800 border border-gray-700 rounded p-3">
                <div class="text-xs text-gray-400 mb-1">${this.escapeHtml(label || '-')}</div>
                <div class="text-lg font-bold text-cyan-300">${typeof value === 'number' ? this.formatNumber(value) : this.escapeHtml(String(value ?? '-'))}</div>
            </div>
        `;
    },

    renderConversionCriteriaItem(label, value) {
        return `
            <div class="bg-gray-900/50 border border-gray-700 rounded p-2">
                <div class="text-gray-400 mb-1">${this.escapeHtml(label || '-')}</div>
                <div class="text-gray-200">${this.escapeHtml(String(value || '-'))}</div>
            </div>
        `;
    },

    translateKpiCriteriaValue(kind, value) {
        const maps = {
            sales_date_basis: {
                ui_payment_date: 'فروش: تاریخ واریزی UI',
                db_payment_date: 'فروش: تاریخ پرداخت DB',
                system_created_at: 'فروش: تاریخ ثبت سیستمی'
            },
            payment_date_basis: {
                ui_payment_date: 'مالی: تاریخ واریزی UI',
                db_payment_date: 'مالی: تاریخ پرداخت DB',
                system_created_at: 'مالی: تاریخ ثبت سیستمی'
            },
            sales_count_rule: {
                first_payment_only: 'فروش: فقط اولین واریزی',
                all_payments: 'فروش: همه پرداخت‌ها'
            },
            payment_count_rule: {
                first_payment_only: 'مالی: فقط اولین واریزی',
                all_payments: 'مالی: همه پرداخت‌ها'
            },
            payment_status_scope: {
                confirmed_only: 'فقط تاییدشده',
                all_statuses: 'همه وضعیت‌ها'
            },
            legacy_deal_scope: {
                exclude: 'معاملات legacy حذف شود',
                all: 'همه معاملات',
                only: 'فقط معاملات legacy'
            }
        };
        return (maps[kind] && maps[kind][value]) ? maps[kind][value] : (value || '-');
    },

    renderConversionDealTable(title, rows, showReason = false) {
        return `
            <div class="space-y-2">
                <div class="text-sm font-bold text-indigo-300">${this.escapeHtml(title || 'معاملات')}</div>
                ${rows.length === 0 ? '<div class="text-xs text-gray-500">رکوردی وجود ندارد.</div>' : `
                    <div class="overflow-x-auto border border-gray-700 rounded">
                        <table class="w-full text-xs">
                            <thead class="bg-gray-800">
                                <tr>
                                    <th class="p-2 border border-gray-700">عنوان معامله</th>
                                    <th class="p-2 border border-gray-700">تاریخ ثبت معامله</th>
                                    <th class="p-2 border border-gray-700">اولین واریزی</th>
                                    <th class="p-2 border border-gray-700">نوع لید</th>
                                    <th class="p-2 border border-gray-700">وضعیت</th>
                                    <th class="p-2 border border-gray-700">مبلغ قرارداد</th>
                                    <th class="p-2 border border-gray-700">جمع پرداخت (طبق معیار)</th>
                                    ${showReason ? '<th class="p-2 border border-gray-700">علت</th>' : ''}
                                    <th class="p-2 border border-gray-700">عملیات</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${rows.map(row => `
                                    <tr class="hover:bg-gray-800/60">
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(row.title || ('#' + (row.deal_id || '')))}</td>
                                        <td class="p-2 border border-gray-700">${this.formatKpiDate(row.deal_date || '')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatKpiDate(row.first_payment_date || '') || '-'}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(row.lead_type || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(row.deal_status_group || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(row.payable_amount || 0))}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(row.total_paid || 0))}</td>
                                        ${showReason ? `<td class="p-2 border border-gray-700">${this.escapeHtml(row.exclude_reason_label || row.conversion_reason_label || '-')}</td>` : ''}
                                        <td class="p-2 border border-gray-700 text-center">
                                            <button class="text-blue-300 hover:text-blue-200 underline" onclick="app.openDealFromKpiDrilldown('${this.escapeHtml(row.didar_deal_id || row.deal_id || '')}')">باز کردن معامله</button>
                                        </td>
                                    </tr>
                                `).join('')}
                            </tbody>
                        </table>
                    </div>
                `}
            </div>
        `;
    },

    renderConversionLeadTable(title, rows, showReason = false) {
        return `
            <div class="space-y-2">
                <div class="text-sm font-bold text-indigo-300">${this.escapeHtml(title || 'لیدها')}</div>
                ${rows.length === 0 ? '<div class="text-xs text-gray-500">رکوردی وجود ندارد.</div>' : `
                    <div class="overflow-x-auto border border-gray-700 rounded">
                        <table class="w-full text-xs">
                            <thead class="bg-gray-800">
                                <tr>
                                    <th class="p-2 border border-gray-700">نام لید</th>
                                    <th class="p-2 border border-gray-700">شماره</th>
                                    <th class="p-2 border border-gray-700">تاریخ ثبت لید</th>
                                    <th class="p-2 border border-gray-700">مسئول</th>
                                    <th class="p-2 border border-gray-700">تعداد معامله</th>
                                    <th class="p-2 border border-gray-700">معاملات دارای پرداخت</th>
                                    ${showReason ? '<th class="p-2 border border-gray-700">علت</th>' : ''}
                                    <th class="p-2 border border-gray-700">عملیات</th>
                                </tr>
                            </thead>
                            <tbody>
                                ${rows.map(row => `
                                    <tr class="hover:bg-gray-800/60">
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(row.full_name || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(row.mobile_phone || row.secondary_mobile_phone || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatKpiDate(row.lead_date || '')}</td>
                                        <td class="p-2 border border-gray-700">${this.escapeHtml(row.owner_name || '-')}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(row.deal_count || 0))}</td>
                                        <td class="p-2 border border-gray-700">${this.formatNumber(Number(row.converted_deal_count || 0))}</td>
                                        ${showReason ? `<td class="p-2 border border-gray-700">${this.escapeHtml(row.exclude_reason_label || row.conversion_reason_label || '-')}</td>` : ''}
                                        <td class="p-2 border border-gray-700 text-center">
                                            <button class="text-blue-300 hover:text-blue-200 underline" onclick="app.openLeadFromKpiDrilldown('${this.escapeHtml(row.contact_id || '')}')">باز کردن لید</button>
                                        </td>
                                    </tr>
                                `).join('')}
                            </tbody>
                        </table>
                    </div>
                `}
            </div>
        `;
    },

    renderConversionCompactDeals(deals) {
        return `
            <div class="overflow-x-auto border border-gray-700 rounded">
                <table class="w-full text-xs">
                    <thead class="bg-gray-800">
                        <tr>
                            <th class="p-2 border border-gray-700">عنوان</th>
                            <th class="p-2 border border-gray-700">تاریخ</th>
                            <th class="p-2 border border-gray-700">وضعیت</th>
                            <th class="p-2 border border-gray-700">قرارداد</th>
                            <th class="p-2 border border-gray-700">پرداخت</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${deals.slice(0, 100).map(deal => `
                            <tr>
                                <td class="p-2 border border-gray-700">${this.escapeHtml(deal.title || '-')}</td>
                                <td class="p-2 border border-gray-700">${this.formatKpiDate(deal.deal_date || '')}</td>
                                <td class="p-2 border border-gray-700">${this.escapeHtml(deal.deal_status_group || '-')}</td>
                                <td class="p-2 border border-gray-700">${this.formatNumber(Number(deal.payable_amount || 0))}</td>
                                <td class="p-2 border border-gray-700">${this.formatNumber(Number(deal.total_paid || 0))}</td>
                            </tr>
                        `).join('')}
                    </tbody>
                </table>
            </div>
        `;
    },

    renderConversionCompactLeads(leads) {
        return `
            <div class="overflow-x-auto border border-gray-700 rounded">
                <table class="w-full text-xs">
                    <thead class="bg-gray-800">
                        <tr>
                            <th class="p-2 border border-gray-700">نام لید</th>
                            <th class="p-2 border border-gray-700">شماره</th>
                            <th class="p-2 border border-gray-700">تاریخ ثبت</th>
                            <th class="p-2 border border-gray-700">مسئول</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${leads.slice(0, 100).map(lead => `
                            <tr>
                                <td class="p-2 border border-gray-700">${this.escapeHtml(lead.full_name || '-')}</td>
                                <td class="p-2 border border-gray-700">${this.escapeHtml(lead.mobile_phone || lead.secondary_mobile_phone || '-')}</td>
                                <td class="p-2 border border-gray-700">${this.formatKpiDate(lead.lead_date || '')}</td>
                                <td class="p-2 border border-gray-700">${this.escapeHtml(lead.owner_name || '-')}</td>
                            </tr>
                        `).join('')}
                    </tbody>
                </table>
            </div>
        `;
    },

    isKpiProductPivotTable(tableId) {
        return [
            'product_sold_by_owner_day',
            'product_requests_by_owner_day',
            'product_failed_by_owner_day'
        ].includes(tableId);
    },

    buildKpiProductPivotModel(tableId) {
        const rows = Array.isArray(this.state.kpiAdvancedTables?.[tableId]) ? [...this.state.kpiAdvancedTables[tableId]] : [];
        const products = [...new Set(rows.map(r => String(r.product_name || 'نامشخص')))].sort((a, b) => a.localeCompare(b, 'fa'));

        const byKey = {};
        rows.forEach((row) => {
            const date = String(row.date || '');
            const ownerDidarId = String(row.owner_didar_id || '');
            const ownerName = String(row.owner_name || 'بدون مسئول');
            const productName = String(row.product_name || 'نامشخص');
            const key = `${date}__${ownerDidarId}__${ownerName}`;
            if (!byKey[key]) {
                byKey[key] = {
                    date,
                    owner_didar_id: ownerDidarId,
                    owner_name: ownerName,
                    productValues: {},
                    requested_total_count: 0,
                    sold_total_count: 0,
                    unsold_total_count: 0
                };
            }
            if (!byKey[key].productValues[productName]) {
                byKey[key].productValues[productName] = { n: 0, o: 0, t: 0 };
            }

            if (tableId === 'product_sold_by_owner_day') {
                byKey[key].productValues[productName].n += Number(row.sold_new_count || 0);
                byKey[key].productValues[productName].o += Number(row.sold_old_count || 0);
                byKey[key].productValues[productName].t += Number(row.sold_total_count || 0);
            } else if (tableId === 'product_requests_by_owner_day') {
                byKey[key].productValues[productName].n += Number(row.requested_new_count || 0);
                byKey[key].productValues[productName].o += Number(row.requested_old_count || 0);
                byKey[key].productValues[productName].t += Number(row.requested_total_count || 0);
                byKey[key].requested_total_count += Number(row.requested_total_count || 0);
                byKey[key].sold_total_count += Number(row.sold_total_count || 0);
                byKey[key].unsold_total_count += Number(row.unsold_total_count || 0);
            } else if (tableId === 'product_failed_by_owner_day') {
                byKey[key].productValues[productName].n += Number(row.failed_new_count || 0);
                byKey[key].productValues[productName].o += Number(row.failed_old_count || 0);
                byKey[key].productValues[productName].t += Number(row.failed_total_count || 0);
            }
        });

        let dataRows = Object.values(byKey);
        dataRows.sort((a, b) => {
            const d = String(a.date || '').localeCompare(String(b.date || ''));
            if (d !== 0) return d;
            return String(a.owner_name || '').localeCompare(String(b.owner_name || ''), 'fa');
        });

        const withOwnerTotals = [];
        const groups = {};
        dataRows.forEach((row) => {
            const ownerKey = `${row.owner_didar_id}__${row.owner_name}`;
            if (!groups[ownerKey]) groups[ownerKey] = [];
            groups[ownerKey].push(row);
        });

        dataRows.forEach((row) => withOwnerTotals.push(row));
        Object.keys(groups).forEach((ownerKey) => {
            const rowsByOwner = groups[ownerKey];
            const total = {
                date: 'جمع کارشناس',
                owner_didar_id: rowsByOwner[0].owner_didar_id,
                owner_name: `${rowsByOwner[0].owner_name} (جمع)`,
                productValues: {},
                requested_total_count: 0,
                sold_total_count: 0,
                unsold_total_count: 0,
                __owner_total__: true
            };
            products.forEach((productName) => {
                total.productValues[productName] = { n: 0, o: 0, t: 0 };
            });
            rowsByOwner.forEach((r) => {
                products.forEach((productName) => {
                    const pv = r.productValues[productName] || { n: 0, o: 0, t: 0 };
                    total.productValues[productName].n += Number(pv.n || 0);
                    total.productValues[productName].o += Number(pv.o || 0);
                    total.productValues[productName].t += Number(pv.t || 0);
                });
                total.requested_total_count += Number(r.requested_total_count || 0);
                total.sold_total_count += Number(r.sold_total_count || 0);
                total.unsold_total_count += Number(r.unsold_total_count || 0);
            });
            withOwnerTotals.push(total);
        });

        return { products, rows: withOwnerTotals };
    },

    renderKpiProductPivotGrid(tableId) {
        const model = this.buildKpiProductPivotModel(tableId);
        const products = model.products || [];
        const rows = model.rows || [];
        const isRequests = tableId === 'product_requests_by_owner_day';
        if (!this.state.kpiAdvancedRenderedRows) {
            this.state.kpiAdvancedRenderedRows = {};
        }
        this.state.kpiAdvancedRenderedRows[tableId] = rows;

        const headerProductCols = products.map((p) => `
            <th colspan="2" class="border border-gray-700 bg-gray-800 p-2 text-gray-300">${this.escapeHtml(p)}</th>
        `).join('');
        const headerSubCols = products.map(() => `
            <th class="border border-gray-700 bg-gray-800 p-2 text-gray-300">ج</th>
            <th class="border border-gray-700 bg-gray-800 p-2 text-gray-300">ق</th>
        `).join('');

        const allTotals = {};
        products.forEach((p) => {
            allTotals[p] = { n: 0, o: 0, t: 0 };
        });
        let reqTotal = 0;
        let soldTotal = 0;
        let unsoldTotal = 0;

        const dataRows = rows.filter(r => !r.__owner_total__);
        dataRows.forEach((row) => {
            products.forEach((p) => {
                const pv = row.productValues?.[p] || { n: 0, o: 0, t: 0 };
                allTotals[p].n += Number(pv.n || 0);
                allTotals[p].o += Number(pv.o || 0);
                allTotals[p].t += Number(pv.t || 0);
            });
            reqTotal += Number(row.requested_total_count || 0);
            soldTotal += Number(row.sold_total_count || 0);
            unsoldTotal += Number(row.unsold_total_count || 0);
        });

        const bodyHtml = rows.map((row, idx) => {
            const isEven = idx % 2 === 0;
            const bgClass = row.__owner_total__ ? 'bg-indigo-900/20' : (isEven ? 'bg-gray-900/50' : 'bg-gray-900/30');
            const renderPivotMetricCell = (value, metricKey, productName = '') => {
                const formatted = this.formatNumber(Number(value || 0));
                if (row.__owner_total__) {
                    return `<span class="text-gray-300">${formatted}</span>`;
                }
                const encodedProduct = encodeURIComponent(String(productName || ''));
                return `<button type="button" class="text-cyan-300 hover:text-cyan-200 hover:underline transition-colors" onclick="app.openKpiAdvancedDrilldownWithProduct('${tableId}','${metricKey}',${idx},'${encodedProduct}')">${formatted}</button>`;
            };
            const productCells = products.map((p) => {
                const pv = row.productValues?.[p] || { n: 0, o: 0, t: 0 };
                const metricNew = tableId === 'product_sold_by_owner_day'
                    ? 'sold_new_count'
                    : (tableId === 'product_requests_by_owner_day' ? 'requested_new_count' : 'failed_new_count');
                const metricOld = tableId === 'product_sold_by_owner_day'
                    ? 'sold_old_count'
                    : (tableId === 'product_requests_by_owner_day' ? 'requested_old_count' : 'failed_old_count');
                return `
                    <td class="border border-gray-700/50 p-2 text-center text-gray-300">${renderPivotMetricCell(pv.n, metricNew, p)}</td>
                    <td class="border border-gray-700/50 p-2 text-center text-gray-300">${renderPivotMetricCell(pv.o, metricOld, p)}</td>
                `;
            }).join('');
            const rowSum = products.reduce((sum, p) => sum + Number(row.productValues?.[p]?.t || 0), 0);
            return `
                <tr class="${bgClass}">
                    <td class="border border-gray-700/50 p-2 text-center text-gray-300">${this.formatKpiDate(row.date || '')}</td>
                    <td class="border border-gray-700/50 p-2 text-center text-gray-300">${this.escapeHtml(row.owner_name || '-')}</td>
                    ${productCells}
                    ${isRequests ? `
                        <td class="border border-gray-700/50 p-2 text-center text-gray-300">${renderPivotMetricCell(row.requested_total_count, 'requested_total_count')}</td>
                        <td class="border border-gray-700/50 p-2 text-center text-gray-300">${renderPivotMetricCell(row.sold_total_count, 'sold_total_count')}</td>
                        <td class="border border-gray-700/50 p-2 text-center text-gray-300">${renderPivotMetricCell(row.unsold_total_count, 'unsold_total_count')}</td>
                    ` : ''}
                    <td class="border border-gray-700/50 p-2 text-center font-bold text-white">${renderPivotMetricCell(rowSum, (tableId === 'product_sold_by_owner_day' ? 'sold_total_count' : (tableId === 'product_requests_by_owner_day' ? 'requested_total_count' : 'failed_total_count')))}</td>
                </tr>
            `;
        }).join('');

        const footerProductCells = products.map((p) => `
            <td class="border border-gray-700 p-2 text-center font-bold text-white bg-gray-800">${this.formatNumber(Number(allTotals[p].n || 0))}</td>
            <td class="border border-gray-700 p-2 text-center font-bold text-white bg-gray-800">${this.formatNumber(Number(allTotals[p].o || 0))}</td>
        `).join('');
        const footerRowSum = products.reduce((sum, p) => sum + Number(allTotals[p].t || 0), 0);

        return `
            <div class="overflow-x-auto rounded-lg border border-gray-700 shadow-xl custom-scrollbar">
                <table class="w-full text-xs border-collapse min-w-[1100px]">
                    <thead>
                        <tr>
                            <th rowspan="2" class="border border-gray-700 bg-gray-800 p-2 text-gray-300">تاریخ</th>
                            <th rowspan="2" class="border border-gray-700 bg-gray-800 p-2 text-gray-300">کارشناس</th>
                            ${headerProductCols}
                            ${isRequests ? `
                                <th rowspan="2" class="border border-gray-700 bg-gray-800 p-2 text-gray-300">کل درخواست</th>
                                <th rowspan="2" class="border border-gray-700 bg-gray-800 p-2 text-gray-300">فروش‌رفته</th>
                                <th rowspan="2" class="border border-gray-700 bg-gray-800 p-2 text-gray-300">فروش‌نرفته</th>
                            ` : ''}
                            <th rowspan="2" class="border border-gray-700 bg-gray-800 p-2 text-white">جمع کل ردیف</th>
                        </tr>
                        <tr>${headerSubCols}</tr>
                    </thead>
                    <tbody>
                        ${bodyHtml || `<tr><td colspan="${2 + (products.length * 2) + (isRequests ? 4 : 1)}" class="p-6 text-center text-gray-500 italic">نتیجه‌ای برای این فیلتر وجود ندارد.</td></tr>`}
                    </tbody>
                    <tfoot>
                        <tr>
                            <td class="border border-gray-700 p-2 font-bold text-white bg-gray-800">جمع کل</td>
                            <td class="border border-gray-700 p-2 font-bold text-white bg-gray-800">-</td>
                            ${footerProductCells}
                            ${isRequests ? `
                                <td class="border border-gray-700 p-2 text-center font-bold text-white bg-gray-800">${this.formatNumber(Number(reqTotal || 0))}</td>
                                <td class="border border-gray-700 p-2 text-center font-bold text-white bg-gray-800">${this.formatNumber(Number(soldTotal || 0))}</td>
                                <td class="border border-gray-700 p-2 text-center font-bold text-white bg-gray-800">${this.formatNumber(Number(unsoldTotal || 0))}</td>
                            ` : ''}
                            <td class="border border-gray-700 p-2 text-center font-bold text-white bg-gray-800">${this.formatNumber(Number(footerRowSum || 0))}</td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        `;
    },

    csvEscape(value) {
        const str = String(value ?? '');
        if (str.includes(',') || str.includes('"') || str.includes('\n')) {
            return `"${str.replace(/"/g, '""')}"`;
        }
        return str;
    },

    sumLostSales(lostSales) {
        if (!lostSales || lostSales.length === 0) return 0;
        return lostSales.reduce((sum, item) => sum + Number(item.total_lost || 0), 0);
    },

    sumLostSalesAmount(lostSales) {
        if (!lostSales || lostSales.length === 0) return 0;
        return lostSales.reduce((sum, item) => sum + (parseFloat(item.total_amount) || 0), 0);
    },

    getKpiOwnerOptions(selectedIds = []) {
        const selectedSet = new Set((selectedIds || []).map(v => String(v)));
        const allSelected = selectedSet.size === 0 || selectedSet.has('__all__');
        let options = `<option value="__all__" ${allSelected ? 'selected' : ''}>همه مسئول‌ها</option>`;
        if (this.state.kpiUsers && this.state.kpiUsers.length > 0) {
            const activeUsers = this.state.kpiUsers.filter(user => Number(user.is_active) === 1);
            activeUsers.forEach(user => {
                const name = user.display_name || user.email || (user.first_name + ' ' + user.last_name);
                const didarId = user.didar_user_id || user.owner_id || '';
                if (!didarId) return;
                const isSelected = selectedSet.has(String(didarId));
                options += `<option value="${didarId}" ${isSelected ? 'selected' : ''}>${name}</option>`;
            });
        }
        return options;
    },

    getKpiProductOptions(selectedIds = []) {
        const selectedSet = new Set((selectedIds || []).map(v => String(v)));
        const allSelected = selectedSet.size === 0 || selectedSet.has('__all__');
        let options = `<option value="__all__" ${allSelected ? 'selected' : ''}>همه محصولات</option>`;
        if (this.state.kpiProducts && this.state.kpiProducts.length > 0) {
            this.state.kpiProducts.forEach(product => {
                if (!product.id) return;
                const isSelected = selectedSet.has(String(product.id));
                options += `<option value="${product.id}" ${isSelected ? 'selected' : ''}>${product.name || 'محصول'}</option>`;
            });
        }
        return options;
    },

    getKpiDealStatusOptions(selectedValues = []) {
        const selectedSet = new Set((selectedValues || []).map(v => String(v)));
        const options = [
            { value: 'pending', label: 'جاری' },
            { value: 'won', label: 'موفق' },
            { value: 'lost', label: 'ناموفق' }
        ];
        return options.map(opt => (
            `<option value="${opt.value}" ${selectedSet.has(opt.value) ? 'selected' : ''}>${opt.label}</option>`
        )).join('');
    },

    getKpiDealLeadTypeOptions(selectedValues = []) {
        const selectedSet = new Set((selectedValues || []).map(v => String(v)));
        const options = [
            { value: 'new', label: 'معامله لید جدید' },
            { value: 'old', label: 'معامله لید قدیمی' }
        ];
        return options.map(opt => (
            `<option value="${opt.value}" ${selectedSet.has(opt.value) ? 'selected' : ''}>${opt.label}</option>`
        )).join('');
    },

    getMultiSelectedValues(elementId) {
        const element = document.getElementById(elementId);
        if (!element) return [];
        const selected = Array.from(element.selectedOptions || [])
            .map(opt => (opt?.value || '').trim())
            .filter(Boolean);
        const hasAll = selected.includes('__all__');
        if (hasAll) {
            return [];
        }
        return selected;
    },

    syncKpiMultiFiltersToDom() {
        const filters = this.state.kpiFilters || {};
        this.setMultiSelectValues('kpi-owner-filter', filters.owner_didar_ids || []);
        this.setMultiSelectValues('kpi-product-filter', filters.product_ids || []);
        this.setMultiSelectValues('kpi-deal-status-filter', filters.deal_statuses || []);
        this.setMultiSelectValues('kpi-deal-lead-type-filter', filters.deal_lead_types || []);
    },

    setMultiSelectValues(elementId, values) {
        const element = document.getElementById(elementId);
        if (!element) return;
        const valueSet = new Set((values || []).map(v => String(v)));
        const hasAll = valueSet.size === 0 || valueSet.has('__all__');
        Array.from(element.options || []).forEach(opt => {
            if (String(opt.value) === '__all__') {
                opt.selected = hasAll;
                return;
            }
            opt.selected = !hasAll && valueSet.has(String(opt.value));
        });
    },

    getKpiPickerConfigs() {
        return {
            owner: {
                key: 'owner',
                selectId: 'kpi-owner-filter',
                pickerId: 'kpi-owner-picker',
                title: 'انتخاب مسئول'
            },
            product: {
                key: 'product',
                selectId: 'kpi-product-filter',
                pickerId: 'kpi-product-picker',
                title: 'انتخاب محصول'
            },
            dealStatus: {
                key: 'dealStatus',
                selectId: 'kpi-deal-status-filter',
                pickerId: 'kpi-deal-status-picker',
                title: 'انتخاب وضعیت معامله'
            },
            dealLeadType: {
                key: 'dealLeadType',
                selectId: 'kpi-deal-lead-type-filter',
                pickerId: 'kpi-deal-lead-type-picker',
                title: 'انتخاب نوع معامله'
            }
        };
    },

    initKpiTagPickers() {
        this.ensureKpiPickerGlobalHandlers();
        const configs = this.getKpiPickerConfigs();
        Object.values(configs).forEach(cfg => this.renderKpiTagPicker(cfg.key));
    },

    ensureKpiPickerGlobalHandlers() {
        if (this._kpiPickerHandlersBound) return;
        this._kpiPickerHandlersBound = true;

        document.addEventListener('pointerdown', (event) => {
            if (this.isKpiPickerInteractionLocked()) {
                return;
            }
            if (!this.isEventInsideKpiPicker(event)) {
                this.closeAllKpiPickers();
            }
        });

        document.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                this.closeAllKpiPickers();
            }
        });
    },

    isEventInsideKpiPicker(event) {
        if (!event) return false;
        if (typeof event.composedPath === 'function') {
            const path = event.composedPath() || [];
            return path.some(node => node?.classList?.contains?.('kpi-tag-picker-root'));
        }
        return !!event.target?.closest?.('.kpi-tag-picker-root');
    },

    setKpiPickerInteractionLock(ms = 180) {
        this._kpiPickerInteractionLockUntil = Date.now() + Number(ms || 0);
    },

    isKpiPickerInteractionLocked() {
        return Number(this._kpiPickerInteractionLockUntil || 0) > Date.now();
    },

    renderKpiTagPicker(key) {
        const config = this.getKpiPickerConfigs()[key];
        if (!config) return;
        const picker = document.getElementById(config.pickerId);
        const select = document.getElementById(config.selectId);
        if (!picker || !select) return;
        if (!this.state.kpiPickerSearch) {
            this.state.kpiPickerSearch = {};
        }

        const allOptions = Array.from(select.options || []);
        const activeSearch = String(this.state.kpiPickerSearch[key] || '').trim().toLowerCase();
        const visibleOptions = activeSearch
            ? allOptions.filter(opt => String(opt.textContent || '').toLowerCase().includes(activeSearch))
            : allOptions;
        const selected = allOptions.filter(opt => opt.selected);
        const allOption = allOptions.find(opt => String(opt.value) === '__all__');
        const selectedReal = selected.filter(opt => String(opt.value) !== '__all__');
        const allSelected = !!allOption?.selected || selectedReal.length === 0;
        const isOpen = this.state.kpiPickerOpenKey === key;

        const selectedChips = allSelected
            ? `<span class="inline-flex items-center gap-1 bg-emerald-900/60 border border-emerald-700 text-emerald-100 px-2 py-1 rounded text-xs">همه</span>`
            : (selectedReal.length > 0 ? selectedReal.map(opt => `
                <span class="inline-flex items-center gap-1 bg-blue-900/60 border border-blue-700 text-blue-100 px-2 py-1 rounded text-xs">
                    <span class="truncate max-w-[120px]">${this.escapeHtml(opt.textContent || '')}</span>
                    <button type="button" class="text-blue-200 hover:text-white" onclick="app.removeKpiPickerValue('${key}','${String(opt.value)}', event)">×</button>
                </span>
            `).join('')
            : '<span class="text-xs text-gray-500">انتخاب نشده</span>');
        const selectedCountText = allSelected ? 'همه' : `${selectedReal.length} مورد`;

        const listItems = visibleOptions.map(opt => `
            <label class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-gray-700 cursor-pointer text-sm">
                <input type="checkbox" ${opt.selected ? 'checked' : ''} onchange="app.toggleKpiPickerValue('${key}','${String(opt.value)}', this.checked, event)">
                <span class="text-gray-200">${this.escapeHtml(opt.textContent || '')}</span>
            </label>
        `).join('');

        picker.innerHTML = `
            <div class="kpi-tag-picker-root relative" onpointerdown="event.stopPropagation()" onclick="event.stopPropagation()">
                <button type="button" onclick="app.toggleKpiPicker('${key}', event)"
                    class="w-full min-h-[44px] bg-gray-800 border border-gray-700 rounded p-2 text-left">
                    <div class="flex items-center justify-between gap-2">
                        <div class="flex flex-wrap gap-1 flex-1">${selectedChips}</div>
                        <span class="text-[11px] text-gray-400 whitespace-nowrap">${selectedCountText}</span>
                        <span class="text-gray-400 text-xs">${isOpen ? '▲' : '▼'}</span>
                    </div>
                </button>
                <div id="kpi-picker-panel-${key}" class="${isOpen ? '' : 'hidden'} absolute z-20 mt-1 w-full bg-gray-900 border border-gray-700 rounded shadow-2xl">
                    <div class="p-2 border-b border-gray-700">
                        <div class="text-xs text-gray-400 mb-1">${config.title}</div>
                        <div class="flex items-center gap-2 mb-2">
                            <button type="button" class="text-[11px] px-2 py-1 rounded bg-emerald-700/70 hover:bg-emerald-600 text-white" onclick="app.selectAllKpiPickerValues('${key}', event)">همه</button>
                            <button type="button" class="text-[11px] px-2 py-1 rounded bg-gray-700 hover:bg-gray-600 text-white" onclick="app.clearKpiPickerValues('${key}', event)">پاک‌کردن</button>
                        </div>
                        <input id="kpi-picker-search-${key}" type="text" oninput="app.filterKpiPickerOptions('${key}', this.value)"
                            class="w-full bg-gray-800 border border-gray-700 rounded p-2 text-xs text-white" placeholder="جستجو..." value="${this.escapeHtml(this.state.kpiPickerSearch[key] || '')}">
                    </div>
                    <div id="kpi-picker-list-${key}" class="max-h-56 overflow-auto p-1">${listItems || '<div class="text-xs text-gray-500 p-2">نتیجه‌ای پیدا نشد.</div>'}</div>
                </div>
            </div>
        `;
    },

    toggleKpiPicker(key, event) {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }
        this.setKpiPickerInteractionLock();
        this.state.kpiPickerOpenKey = (this.state.kpiPickerOpenKey === key) ? null : key;
        const configs = this.getKpiPickerConfigs();
        Object.values(configs).forEach(cfg => this.renderKpiTagPicker(cfg.key));
        if (this.state.kpiPickerOpenKey === key) {
            setTimeout(() => {
                const searchInput = document.getElementById(`kpi-picker-search-${key}`);
                if (searchInput) searchInput.focus();
            }, 0);
        }
    },

    closeAllKpiPickers() {
        if (!this.state.kpiPickerOpenKey) return;
        this.state.kpiPickerOpenKey = null;
        const configs = this.getKpiPickerConfigs();
        Object.values(configs).forEach(cfg => this.renderKpiTagPicker(cfg.key));
    },

    filterKpiPickerOptions(key, searchValue) {
        this.setKpiPickerInteractionLock();
        this.state.kpiPickerOpenKey = key;
        if (!this.state.kpiPickerSearch) {
            this.state.kpiPickerSearch = {};
        }
        this.state.kpiPickerSearch[key] = String(searchValue || '');
        this.renderKpiTagPicker(key);
        setTimeout(() => {
            const searchInput = document.getElementById(`kpi-picker-search-${key}`);
            if (searchInput) {
                searchInput.focus();
                const len = searchInput.value.length;
                searchInput.setSelectionRange(len, len);
            }
        }, 0);
    },

    toggleKpiPickerValue(key, value, isChecked, event = null) {
        if (event) {
            event.stopPropagation();
        }
        this.setKpiPickerInteractionLock();
        this.state.kpiPickerOpenKey = key;
        const config = this.getKpiPickerConfigs()[key];
        if (!config) return;
        const select = document.getElementById(config.selectId);
        if (!select) return;
        const options = Array.from(select.options || []);
        const normalizedValue = String(value);
        const match = options.find(opt => String(opt.value) === normalizedValue);
        if (!match) return;

        if (normalizedValue === '__all__') {
            options.forEach(opt => {
                opt.selected = String(opt.value) === '__all__' ? !!isChecked : false;
            });
            this.renderKpiTagPicker(key);
            return;
        }

        match.selected = !!isChecked;
        const allOption = options.find(opt => String(opt.value) === '__all__');
        if (allOption) {
            allOption.selected = false;
        }

        const selectedRealCount = options.filter(opt => String(opt.value) !== '__all__' && opt.selected).length;
        if (selectedRealCount === 0 && allOption) {
            allOption.selected = true;
        }
        this.renderKpiTagPicker(key);
        setTimeout(() => {
            const searchInput = document.getElementById(`kpi-picker-search-${key}`);
            if (searchInput) searchInput.focus();
        }, 0);
    },

    selectAllKpiPickerValues(key, event = null) {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }
        this.setKpiPickerInteractionLock();
        this.state.kpiPickerOpenKey = key;
        const config = this.getKpiPickerConfigs()[key];
        if (!config) return;
        const select = document.getElementById(config.selectId);
        if (!select) return;
        const options = Array.from(select.options || []);
        options.forEach(opt => {
            opt.selected = String(opt.value) === '__all__';
        });
        this.renderKpiTagPicker(key);
    },

    clearKpiPickerValues(key, event = null) {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }
        this.setKpiPickerInteractionLock();
        this.state.kpiPickerOpenKey = key;
        const config = this.getKpiPickerConfigs()[key];
        if (!config) return;
        const select = document.getElementById(config.selectId);
        if (!select) return;
        const options = Array.from(select.options || []);
        const hasAll = options.some(opt => String(opt.value) === '__all__');
        options.forEach(opt => {
            opt.selected = hasAll ? String(opt.value) === '__all__' : false;
        });
        this.renderKpiTagPicker(key);
    },

    removeKpiPickerValue(key, value, event) {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }
        this.setKpiPickerInteractionLock();
        this.state.kpiPickerOpenKey = key;
        this.toggleKpiPickerValue(key, value, false);
    },

    renderKpiActiveFilterChips(filters = {}) {
        const chips = [];
        const ownerCount = Array.isArray(filters.owner_didar_ids) ? filters.owner_didar_ids.length : 0;
        const productCount = Array.isArray(filters.product_ids) ? filters.product_ids.length : 0;
        const statusCount = Array.isArray(filters.deal_statuses) ? filters.deal_statuses.length : 0;
        const typeCount = Array.isArray(filters.deal_lead_types) ? filters.deal_lead_types.length : 0;

        if (filters.date_from || filters.date_to) {
            chips.push(`بازه: ${this.formatKpiDate(filters.date_from || '')} تا ${this.formatKpiDate(filters.date_to || '')}`);
        }
        if (ownerCount > 0) chips.push(`مسئول: ${this.formatNumber(ownerCount)} مورد`);
        if (productCount > 0) chips.push(`محصول: ${this.formatNumber(productCount)} مورد`);
        if (statusCount > 0) chips.push(`وضعیت معامله: ${this.formatNumber(statusCount)} مورد`);
        if (typeCount > 0) chips.push(`نوع معامله: ${this.formatNumber(typeCount)} مورد`);

        if (chips.length === 0) {
            return '<div class="text-xs text-gray-500">فیلتر فعالی انتخاب نشده است.</div>';
        }

        return `
            <div class="flex flex-wrap gap-2">
                ${chips.map(c => `<span class="text-xs px-3 py-1 rounded-full bg-blue-900/60 border border-blue-700 text-blue-100">${this.escapeHtml(c)}</span>`).join('')}
            </div>
        `;
    },

    clearKpiFilters() {
        const range = this.getJalaliMonthRange();
        this.state.kpiFilters = {
            date_from: range.date_from,
            date_to: range.date_to,
            owner_didar_id: '',
            product_id: '',
            owner_didar_ids: [],
            product_ids: [],
            deal_statuses: [],
            deal_lead_types: [],
            criteria: this.getDefaultKpiCriteria()
        };
        this.loadKPIDashboard({ useState: true });
    },

    setKpiQuickRange(type) {
        const now = new Date();
        const toIso = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
        let fromDate = new Date(now);
        let toDate = new Date(now);

        if (type === 'week') {
            fromDate.setDate(now.getDate() - 6);
        } else if (type === 'month') {
            fromDate = new Date(now.getFullYear(), now.getMonth(), 1);
            toDate = new Date(now.getFullYear(), now.getMonth() + 1, 0);
        }

        this.state.kpiFilters = {
            ...(this.state.kpiFilters || {}),
            date_from: toIso(fromDate),
            date_to: toIso(toDate)
        };
        this.loadKPIDashboard({ useState: true });
    },

    formatKpiDate(dateValue) {
        const raw = String(dateValue || '').trim();
        if (!raw) return '-';
        const dateOnly = raw.length >= 10 ? raw.slice(0, 10) : raw;
        return toPersianDate(dateOnly) || dateOnly;
    },

    getFirstDayOfMonth() {
        const now = new Date();
        return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`;
    },

    getLastDayOfMonth() {
        const now = new Date();
        return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate()}`;
    },

    getJalaliMonthRange() {
        const fallback = {
            date_from: this.getFirstDayOfMonth(),
            date_to: this.getLastDayOfMonth()
        };
        if (typeof persianDate === 'undefined') {
            return fallback;
        }
        try {
            const start = new persianDate().startOf('month').toDate();
            const end = new persianDate().endOf('month').toDate();
            const format = (dateObj) => {
                const y = dateObj.getFullYear();
                const m = String(dateObj.getMonth() + 1).padStart(2, '0');
                const d = String(dateObj.getDate()).padStart(2, '0');
                return `${y}-${m}-${d}`;
            };
            return {
                date_from: format(start),
                date_to: format(end)
            };
        } catch (error) {
            console.warn('getJalaliMonthRange failed', error);
            return fallback;
        }
    },

    formatNumber(num) {
        if (num === null || num === undefined || num === '') return '0';
        return new Intl.NumberFormat('fa-IR').format(num);
    },
    
    // ============ SUPPORT MODULE ============
    
    /**
     * Open support modal
     */
    openSupportModal() {
        const contactId = window.currentContactId;
        if (!contactId) {
            Swal.fire('خطا', 'لطفاً ابتدا یک لید را انتخاب کنید', 'error');
            return;
        }
        
        // Reset form
        document.getElementById('support-contact-id').value = contactId;
        document.getElementById('support-id').value = '';
        document.getElementById('support-channel').value = '';
        document.getElementById('support-direction').value = '';
        document.getElementById('support-result-note').value = '';
        
        this.openModal('modalSupport');
    },
    
    /**
     * Save support log
     */
    async saveSupport(e) {
        e.preventDefault();
        
        const form = e.target;
        const data = new FormData(form);
        
        Swal.fire({
            title: 'در حال ثبت...',
            allowOutsideClick: false,
            didOpen: () => Swal.showLoading()
        });
        
        try {
            const res = await this.req('create_support', Object.fromEntries(data));
            Swal.close();
            
            if (res.status === 'success') {
                Swal.fire({
                    toast: true,
                    position: 'bottom-end',
                    icon: 'success',
                    title: 'پشتیبانی با موفقیت ثبت شد',
                    showConfirmButton: false,
                    timer: 2000
                });
                
                this.closeModal('modalSupport');
                
                // Reload support logs if in support tab
                const contactId = data.get('contact_id');
                if (contactId) {
                    await this.loadSupportLogs(contactId);
                }
            } else {
                Swal.fire('خطا', res.message || 'خطا در ثبت پشتیبانی', 'error');
            }
        } catch (error) {
            console.error('Error saving support:', error);
            Swal.close();
            Swal.fire('خطا', 'خطا در ثبت پشتیبانی: ' + error.message, 'error');
        }
    },
    
    /**
     * Load support logs for a contact
     */
    async loadSupportLogs(contactId) {
        try {
            const res = await this.req('get_support_logs', { contact_id: contactId });
            
            if (res.status === 'success') {
                this.renderSupportLogs(res.support_logs || []);
            } else {
                console.error('Failed to load support logs:', res.message);
                document.getElementById('prof-support').innerHTML = 
                    '<div class="text-center text-red-500 py-4">خطا در بارگذاری پشتیبانی‌ها</div>';
            }
        } catch (error) {
            console.error('Error loading support logs:', error);
            document.getElementById('prof-support').innerHTML = 
                '<div class="text-center text-red-500 py-4">خطا در بارگذاری پشتیبانی‌ها</div>';
        }
    },
    
    /**
     * Render support logs as cards
     */
    renderSupportLogs(logs) {
        const container = document.getElementById('prof-support');
        
        if (!logs || logs.length === 0) {
            container.innerHTML = '<div class="text-center text-gray-500 py-4">هیچ پشتیبانی ثبت نشده است</div>';
            return;
        }
        
        const isAdmin = window.APP_USER?.role === 'admin';
        
        const channelLabels = {
            'call': 'تماس',
            'chat': 'چت',
            'visit': 'حضوری',
            'email': 'ایمیل'
        };
        
        const directionLabels = {
            'incoming': 'ورودی',
            'outgoing': 'خروجی'
        };
        
        const channelIcons = {
            'call': 'fa-phone',
            'chat': 'fa-comment',
            'visit': 'fa-user',
            'email': 'fa-envelope'
        };
        
        const html = logs.map(log => {
            const channelLabel = channelLabels[log.channel] || log.channel;
            const directionLabel = directionLabels[log.direction] || log.direction;
            const channelIcon = channelIcons[log.channel] || 'fa-circle';
            const createdAt = log.created_at ? toPersianDateTime(log.created_at) : '-';
            const creatorName = log.creator_name || 'نامشخص';
            
            return `
                <div class="glass p-4 rounded-lg border border-gray-700/50">
                    <div class="flex justify-between items-start mb-3">
                        <div class="flex items-center gap-3">
                            <div class="w-10 h-10 rounded-full bg-blue-600/20 flex items-center justify-center">
                                <i class="fas ${channelIcon} text-blue-400"></i>
                            </div>
                            <div>
                                <div class="font-bold text-white">${channelLabel} - ${directionLabel}</div>
                                <div class="text-xs text-gray-400 flex items-center gap-2 mt-1">
                                    <i class="fa-solid fa-user text-[10px]"></i>
                                    <span>${creatorName}</span>
                                    <span class="mx-1">•</span>
                                    <i class="fa-solid fa-clock text-[10px]"></i>
                                    <span>${createdAt}</span>
                                </div>
                            </div>
                        </div>
                        ${isAdmin ? `
                            <button onclick="app.editSupportLog(${log.id})" 
                                    class="text-gray-400 hover:text-yellow-500 transition px-2">
                                <i class="fa-solid fa-edit"></i>
                            </button>
                        ` : ''}
                    </div>
                    <div class="text-sm text-gray-300 whitespace-pre-line bg-gray-900/50 p-3 rounded">
                        ${this.escapeHtml(log.result_note || '')}
                    </div>
                </div>
            `;
        }).join('');
        
        container.innerHTML = html;
    },
    
    /**
     * Edit support log (admin only)
     */
    async editSupportLog(id) {
        // TODO: Implement edit functionality if needed
        Swal.fire('اطلاعات', 'قابلیت ویرایش به زودی اضافه می‌شود', 'info');
    },
    
    /**
     * Escape HTML to prevent XSS
     */
    escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    // ============ END SUPPORT MODULE ============
};

// Initialize on page load
// Note: User initialization is handled in the layout file

// ============ MOBILE UX IMPROVEMENTS ============

/**
 * Initialize mobile viewport height fix
 * Accounts for mobile browser chrome (address bar, nav bar)
 */
function initMobileViewportHeight() {
    const setVh = () => {
        document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`);
    };
    
    // Set on load
    setVh();
    
    // Set on resize (debounced)
    let resizeTimer;
    window.addEventListener('resize', () => {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(setVh, 200);
    });
}

/**
 * Initialize swipe gestures for mobile
 * Swipe right to open sidebar, swipe left to close
 */
function initSwipeGestures() {
    if (typeof window === 'undefined') return;
    return; // Disabled: swipe gestures conflict with mobile scroll/touch UX
    
    let touchStartX = 0;
    let touchEndX = 0;
    let touchStartTime = 0;
    const swipeThreshold = 50; // Minimum distance for swipe
    const timeThreshold = 300; // Maximum time for swipe (ms)
    
    // Only enable on mobile
    const isMobile = () => window.innerWidth < 768;
    
    document.addEventListener('touchstart', (e) => {
        if (!isMobile()) return;
        touchStartX = e.changedTouches[0].screenX;
        touchStartTime = Date.now();
    }, { passive: true });
    
    document.addEventListener('touchend', (e) => {
        if (!isMobile()) return;
        touchEndX = e.changedTouches[0].screenX;
        const touchEndTime = Date.now();
        const swipeTime = touchEndTime - touchStartTime;
        
        if (swipeTime > timeThreshold) return; // Too slow, not a swipe
        
        const swipeDistance = touchStartX - touchEndX;
        
        // Swipe left (close sidebar)
        if (swipeDistance < -swipeThreshold && swipeDistance < 0) {
            const sidebar = document.getElementById('main-sidebar');
            if (sidebar && !sidebar.classList.contains('translate-x-full')) {
                if (typeof app !== 'undefined' && typeof app.closeSidebar === 'function') {
                    app.closeSidebar();
                }
            }
        }
        
        // Swipe right (open sidebar)
        if (swipeDistance > swipeThreshold && swipeDistance > 0) {
            const sidebar = document.getElementById('main-sidebar');
            if (sidebar && sidebar.classList.contains('translate-x-full')) {
                // Only open if not already open and not in a modal
                const anyModalOpen = document.querySelector('[id^="modal"]:not(.hidden)');
                if (!anyModalOpen) {
                    if (typeof app !== 'undefined' && typeof app.toggleSidebar === 'function') {
                        app.toggleSidebar();
                    }
                }
            }
        }
    }, { passive: true });
}

// Initialize mobile UX enhancements on DOM ready
if (typeof document !== 'undefined') {
    document.addEventListener('DOMContentLoaded', () => {
        // Initialize mobile viewport height
        initMobileViewportHeight();
        
        // Initialize swipe gestures
        initSwipeGestures();
        
        // Initialize mobile filter enhancements
        initMobileFilters();
    });
}

// ============ MOBILE FILTER FUNCTIONS ============

/**
 * Initialize mobile filter enhancements
 */
function initMobileFilters() {
    // Initialize filter state tracking
    window.filterState = {
        leads: { active: false, count: 0 },
        deals: { active: false, count: 0 }
    };
    
    // Add change listeners to filter inputs
    document.addEventListener('change', (e) => {
        if (e.target.classList.contains('filter-input')) {
            updateFilterCount(e.target);
        }
    });
}

/**
 * Toggle collapsible filters
 */
app.toggleFilters = function(view) {
    const filtersContainer = document.getElementById(`${view}-filters`);
    if (!filtersContainer) return;
    
    const isOpen = filtersContainer.classList.contains('open');
    
    if (isOpen) {
        filtersContainer.classList.remove('open');
        window.filterState[view].active = false;
    } else {
        filtersContainer.classList.add('open');
        window.filterState[view].active = true;
    }
    app.syncFilterToggleUi?.(view);
};

app.syncFilterToggleUi = function(view) {
    const filtersContainer = document.getElementById(`${view}-filters`);
    if (!filtersContainer) return;
    const desktopToggle = document.getElementById(`${view}-desktop-filter-toggle`);
    const desktopText = document.getElementById(`${view}-desktop-filter-toggle-text`);
    if (desktopToggle) {
        const isOpen = filtersContainer.classList.contains('open');
        desktopToggle.classList.toggle('open', isOpen);
        if (desktopText) {
            desktopText.textContent = isOpen ? 'پنهان‌کردن فیلترها' : 'نمایش فیلترها';
        }
    }
};

/**
 * Update filter count badge
 */
function updateFilterCount(input) {
    const view = input.id.split('-')[0]; // leads or deals
    let count = 0;
    
    // Count all non-empty filter inputs
    const filterInputs = document.querySelectorAll(`#${view}-filters .filter-input`);
    filterInputs.forEach(input => {
        if (input.value && input.value !== 'all' && input.value !== '') {
            count++;
        }
        // Toggle active class
        if (input.value && input.value !== 'all' && input.value !== '') {
            input.classList.add('active');
        } else {
            input.classList.remove('active');
        }
    });
    
    window.filterState[view].count = count;
    
    // Update badge
    const badge = document.getElementById(`${view}-filter-count`);
    const bottomBadge = document.getElementById(`${view}-bottom-filter-count`);
    
    if (badge) {
        badge.textContent = count;
        badge.classList.toggle('hidden', count === 0);
    }
    
    if (bottomBadge) {
        bottomBadge.textContent = count;
        bottomBadge.classList.toggle('hidden', count === 0);
    }
    const desktopBadge = document.getElementById(`${view}-desktop-filter-count`);
    if (desktopBadge) {
        desktopBadge.textContent = count;
        desktopBadge.classList.toggle('hidden', count === 0);
    }
}

/**
 * Clear all filters for a view
 */
app.clearAllFilters = function(view) {
    const filtersContainer = document.getElementById(`${view}-filters`);
    if (!filtersContainer) return;
    
    // Reset all filter inputs
    const filterInputs = filtersContainer.querySelectorAll('.filter-input');
    filterInputs.forEach(input => {
        if (input.tagName === 'SELECT') {
            input.selectedIndex = 0;
        } else {
            input.value = '';
        }
        input.classList.remove('active');
    });
    
    // Reset count
    window.filterState[view].count = 0;
    const badge = document.getElementById(`${view}-filter-count`);
    const bottomBadge = document.getElementById(`${view}-bottom-filter-count`);
    
    if (badge) {
        badge.textContent = '0';
        badge.classList.add('hidden');
    }
    
    if (bottomBadge) {
        bottomBadge.classList.add('hidden');
    }
    const desktopBadge = document.getElementById(`${view}-desktop-filter-count`);
    if (desktopBadge) {
        desktopBadge.textContent = '0';
        desktopBadge.classList.add('hidden');
    }
    
    // Reload data
    if (view === 'leads') {
        app.loadLeads(1);
    } else if (view === 'deals') {
        app.loadDeals();
    }
};

/**
 * Apply filters (mobile)
 */
app.applyFilters = function(view) {
    // Close collapsible filters
    const filtersContainer = document.getElementById(`${view}-filters`);
    if (filtersContainer) {
        filtersContainer.classList.remove('open');
    }
    
    // Reload data
    if (view === 'leads') {
        app.loadLeads(1);
    } else if (view === 'deals') {
        app.loadDeals();
    }
};

/**
 * Handle filter change
 */
app.onFilterChange = function(view) {
    updateFilterCount(document.querySelector(`#${view}-filters .filter-input`));
};

/**
 * Open bottom sheet filters
 */
app.openBottomSheetFilters = function(view) {
    // Create bottom sheet if not exists
    if (!document.getElementById('bottom-sheet-filters')) {
        createBottomSheetFilters(view);
    }
    
    const overlay = document.getElementById('bottom-sheet-overlay');
    const sheet = document.getElementById('bottom-sheet-filters');
    
    if (overlay && sheet) {
        overlay.classList.add('active');
        sheet.classList.add('active');
    }
};

/**
 * Close bottom sheet filters
 */
app.closeBottomSheetFilters = function() {
    const overlay = document.getElementById('bottom-sheet-overlay');
    const sheet = document.getElementById('bottom-sheet-filters');
    
    if (overlay && sheet) {
        overlay.classList.remove('active');
        sheet.classList.remove('active');
        
        setTimeout(() => {
            if (overlay) overlay.remove();
            if (sheet) sheet.remove();
        }, 300);
    }
};

/**
 * Create bottom sheet filters component
 */
function createBottomSheetFilters(view) {
    const overlay = document.createElement('div');
    overlay.id = 'bottom-sheet-overlay';
    overlay.className = 'bottom-sheet-overlay';
    overlay.onclick = () => app.closeBottomSheetFilters();
    
    const sheet = document.createElement('div');
    sheet.id = 'bottom-sheet-filters';
    sheet.className = 'bottom-sheet';
    
    sheet.innerHTML = `
        <div class="bottom-sheet-header">
            <div class="bottom-sheet-title">فیلترهای ${view === 'leads' ? 'لیدها' : 'معاملات'}</div>
            <button onclick="app.closeBottomSheetFilters()" class="bottom-sheet-close">
                <i class="fa-solid fa-times"></i>
            </button>
        </div>
        <div class="bottom-sheet-content">
            <p class="text-center text-gray-400 py-8">فیلترهای پیشرفته به زودی...</p>
        </div>
        <div class="bottom-sheet-footer">
            <button onclick="app.closeBottomSheetFilters()" class="bg-gray-700 hover:bg-gray-600 text-white rounded-lg">انصراف</button>
            <button onclick="app.applyBottomSheetFilters()" class="btn-primary">اعمال فیلتر</button>
        </div>
    `;
    
    document.body.appendChild(overlay);
    document.body.appendChild(sheet);
}

/**
 * Apply bottom sheet filters
 */
app.applyBottomSheetFilters = function() {
    app.closeBottomSheetFilters();
    // TODO: Implement filter logic
};

/**
 * Render simplified mobile lead card
 */
app.renderMobileLeadCard = function(lead) {
    const statusKey = lead.sale_status === 1 ? 'success' : (lead.sale_status === 0 ? 'failed' : 'unknown');
    const statusMeta = this.state.saleStatusMap[statusKey] || this.state.saleStatusMap.unknown;
    const phone = lead.mobile_phone || '-';
    const leadId = lead.didar_contact_id || lead.id;
    
    return `
        <div class="glass lead-card-mobile" onclick="app.openPersonProfile('${leadId}')">
            <div class="card-header">
                <div>
                    <div class="card-name">${lead.first_name || ''} ${lead.last_name || ''}</div>
                    <div class="card-phone">${phone}</div>
                </div>
                <span class="card-badge ${statusMeta.bg} text-white">${statusMeta.icon}</span>
            </div>
            <div class="card-meta">
                <span class="text-xs text-gray-400">${lead.owner_name || '---'}</span>
            </div>
            <div class="card-actions">
                <button onclick="event.stopPropagation(); app.quickContact('${phone}', '${leadId}')" class="bg-gray-700 hover:bg-gray-600 text-white rounded-lg flex items-center justify-center gap-2 haptic-button">
                    <i class="fa-solid fa-phone"></i>
                    تماس
                </button>
                <button onclick="event.stopPropagation(); app.openPersonProfile('${leadId}')" class="btn-primary flex items-center justify-center gap-2 haptic-button">
                    <i class="fa-solid fa-eye"></i>
                    مشاهده
                </button>
            </div>
        </div>
    `;
};

/**
 * Render lead cards (mobile-optimized)
 */
app.renderLeads = function() {
    const container = document.getElementById('leads-list');
    if (!container) return;
    
    const isMobile = window.innerWidth < 768;
    
    if (this.state.leads.length === 0) {
        const searchValue = document.getElementById('leads-search')?.value || '';
        
        if (searchValue.trim()) {
            container.innerHTML = `
                <div class="text-center py-12">
                    <div class="mb-4">
                        <i class="fa-solid fa-search text-4xl text-gray-600"></i>
                    </div>
                    <p class="text-gray-400 font-semibold text-lg">لیدی با این مشخصات یافت نشد</p>
                    <p class="text-gray-500 text-sm mt-3">جستجو: <span class="text-yellow-500 font-mono">${this.escapeHtml(searchValue)}</span></p>
                    <button onclick="document.getElementById('leads-search').value = ''; app.loadLeads();" class="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded transition">
                        پاک کردن جستجو
                    </button>
                </div>
            `;
        } else {
            container.innerHTML = '<div class="text-center text-gray-500 py-8">لیدی یافت نشد</div>';
        }
        return;
    }
    
    // Use mobile-optimized rendering on mobile
    if (isMobile) {
        container.innerHTML = this.state.leads.map(lead => this.renderMobileLeadCard(lead)).join('');
    } else {
        // Desktop rendering (existing code)
        container.innerHTML = this.state.leads.map(lead => {
            const statusKey = lead.sale_status === 1 ? 'success' : (lead.sale_status === 0 ? 'failed' : 'unknown');
            const statusMeta = this.state.saleStatusMap[statusKey] || this.state.saleStatusMap.unknown;
            const regDate = lead.register_time_jalali || (lead.register_time ? toPersianDate(lead.register_time) : '');
            const products = this.escapeHtml(this.getLeadProductsDisplayValue(lead)) || '-';
            
            return `
                <div class="glass p-4 hover:bg-gray-800/50 transition cursor-pointer" onclick="app.openPersonProfile('${lead.didar_contact_id || lead.id || ''}')">
                    <div class="flex justify-between items-start mb-2">
                        <div>
                            <div class="font-bold text-white text-sm">${lead.first_name || ''} ${lead.last_name || ''}</div>
                            <div class="text-xs text-gray-400 mt-1">${lead.mobile_phone || '-'} ${lead.email ? '| ' + lead.email : ''}</div>
                            <div class="text-[11px] text-gray-500 mt-1">مسئول: ${lead.owner_name || '---'}</div>
                        </div>
                        <div class="flex flex-col items-end gap-1">
                            <span class="text-xs px-2 py-1 rounded ${statusMeta.bg} text-white">${statusMeta.icon} ${statusMeta.title}</span>
                        </div>
                    </div>
                    <div class="text-[11px] text-gray-400 mt-1">محصولات: ${products}</div>
                    <div class="flex justify-between items-center mt-2 text-xs text-gray-500">
                        <span>تاریخ ثبت: ${regDate || '-'}</span>
                        <span>${statusMeta.title}</span>
                    </div>
                </div>
            `;
        }).join('');
    }
    
    // Update sticky pagination on mobile
    if (isMobile) {
        this.renderStickyPagination('leads');
    }
};

/**
 * Render sticky pagination
 */
app.renderStickyPagination = function(view) {
    const paginationContainer = document.getElementById(`${view}-sticky-pagination`);
    if (!paginationContainer) return;
    
    const pagination = this.state[`${view}Pagination`];
    if (!pagination || pagination.totalPages <= 1) {
        paginationContainer.classList.add('hidden');
        return;
    }
    
    paginationContainer.classList.remove('hidden');
    
    const maxVisiblePages = 5;
    let startPage = Math.max(1, pagination.currentPage - Math.floor(maxVisiblePages / 2));
    let endPage = Math.min(pagination.totalPages, startPage + maxVisiblePages - 1);
    
    if (endPage - startPage + 1 < maxVisiblePages) {
        startPage = Math.max(1, endPage - maxVisiblePages + 1);
    }
    
    let html = '<div class="flex justify-center items-center gap-2 overflow-x-auto no-scrollbar">';
    
    // Previous button
    if (pagination.currentPage > 1) {
        html += `<button onclick="app.load${view.charAt(0).toUpperCase() + view.slice(1)}(${pagination.currentPage - 1})" class="bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition flex items-center gap-1">
            <i class="fa-solid fa-chevron-right"></i> قبلی
        </button>`;
    } else {
        html += `<button disabled class="bg-gray-800 text-gray-600 rounded-lg cursor-not-allowed px-4 py-2">قبلی</button>`;
    }
    
    // Page numbers
    if (startPage > 1) {
        html += `<button onclick="app.load${view.charAt(0).toUpperCase() + view.slice(1)}(1)" class="bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition px-4 py-2">1</button>`;
        if (startPage > 2) html += '<span class="text-gray-500 px-2">...</span>';
    }
    
    for (let i = startPage; i <= endPage; i++) {
        const activeClass = i === pagination.currentPage ? 'bg-yellow-600 text-white' : 'bg-gray-700 hover:bg-gray-600 text-white';
        html += `<button onclick="app.load${view.charAt(0).toUpperCase() + view.slice(1)}(${i})" class="${activeClass} rounded-lg transition px-4 py-2">${i}</button>`;
    }
    
    if (endPage < pagination.totalPages) {
        if (endPage < pagination.totalPages - 1) html += '<span class="text-gray-500 px-2">...</span>';
        html += `<button onclick="app.load${view.charAt(0).toUpperCase() + view.slice(1)}(${pagination.totalPages})" class="bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition px-4 py-2">${pagination.totalPages}</button>`;
    }
    
    // Next button
    if (pagination.currentPage < pagination.totalPages) {
        html += `<button onclick="app.load${view.charAt(0).toUpperCase() + view.slice(1)}(${pagination.currentPage + 1})" class="bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition flex items-center gap-1">
            بعدی <i class="fa-solid fa-chevron-left"></i>
        </button>`;
    } else {
        html += `<button disabled class="bg-gray-800 text-gray-600 rounded-lg cursor-not-allowed px-4 py-2">بعدی</button>`;
    }
    
    html += `</div>`;
    paginationContainer.innerHTML = html;
};

// ============ PHASE 3 & 4: ADVANCED MOBILE FEATURES ============

/**
 * Initialize Phase 3 & 4 features
 */
function initAdvancedMobileFeatures() {
    // Initialize filter presets
    initFilterPresets();
    
    // Initialize filter history
    initFilterHistory();
    
    // Initialize voice search
    initVoiceSearch();
    
    // Initialize haptic feedback
    initHapticFeedback();
    
    // Initialize pull-to-refresh
    initPullToRefresh();
    
    console.log('[MOBILE] Phase 3 & 4 features initialized');
}

/**
 * Filter Presets - Quick common filters
 */
function initFilterPresets() {
    window.filterPresets = {
        leads: [
            { id: 'today', label: 'امروز', icon: 'fa-calendar-day', filters: { register_from: 'today', register_to: 'today' } },
            { id: 'week', label: 'این هفته', icon: 'fa-calendar-week', filters: { register_from: 'week_start', register_to: 'today' } },
            { id: 'month', label: 'این ماه', icon: 'fa-calendar', filters: { register_from: 'month_start', register_to: 'today' } },
            { id: 'my_leads', label: 'لیدهای من', icon: 'fa-user', filters: { owner: 'me' } },
            { id: 'hot', label: 'لیدهای داغ', icon: 'fa-fire', filters: { customer_level: 'طلایی' } },
            { id: 'new', label: 'جدید', icon: 'fa-star', filters: { stage: 'new' } }
        ],
        deals: [
            { id: 'today', label: 'امروز', icon: 'fa-calendar-day', filters: { register_from: 'today', register_to: 'today' } },
            { id: 'won', label: 'موفق', icon: 'fa-check-circle', filters: { status: 'won' } },
            { id: 'pending', label: 'جاری', icon: 'fa-clock', filters: { status: 'pending' } },
            { id: 'high_value', label: 'باارزش', icon: 'fa-sack-dollar', filters: { min_price: '10000000' } }
        ]
    };
}

/**
 * Render filter presets
 */
app.renderFilterPresets = function(view, containerId) {
    const container = document.getElementById(containerId);
    if (!container) return;
    
    const presets = window.filterPresets[view] || [];
    
    container.innerHTML = `
        <div class="filter-preset-bar">
            ${presets.map(preset => `
                <div class="filter-preset-chip" onclick="app.applyFilterPreset('${view}', '${preset.id}')">
                    <i class="fa-solid ${preset.icon}"></i>
                    ${preset.label}
                </div>
            `).join('')}
        </div>
    `;
};

/**
 * Apply filter preset
 */
app.applyFilterPreset = function(view, presetId) {
    const presets = window.filterPresets[view] || [];
    const preset = presets.find(p => p.id === presetId);
    if (!preset) return;
    
    // Apply filters based on preset
    const filters = preset.filters;
    
    if (view === 'leads') {
        if (filters.register_from) {
            const fromDate = this.getDateRangeValue(filters.register_from);
            document.getElementById('leads-register-from').value = fromDate;
        }
        if (filters.register_to) {
            const toDate = this.getDateRangeValue(filters.register_to);
            document.getElementById('leads-register-to').value = toDate;
        }
        if (filters.owner === 'me') {
            document.getElementById('leads-owner-filter').value = window.APP_USER?.didar_id || '';
        }
        if (filters.customer_level) {
            document.getElementById('leads-customer-level-filter').value = filters.customer_level;
        }
        if (filters.stage) {
            document.getElementById('leads-stage-filter').value = filters.stage;
        }
        
        this.loadLeads(1);
    }
    
    // Save to history
    this.saveFilterHistory(view, preset.label);
};

/**
 * Get date range value from preset
 */
app.getDateRangeValue = function(range) {
    const now = new Date();
    let date;
    
    switch (range) {
        case 'today':
            date = now;
            break;
        case 'week_start':
            date = new Date(now);
            date.setDate(date.getDate() - 7);
            break;
        case 'month_start':
            date = new Date(now.getFullYear(), now.getMonth(), 1);
            break;
        default:
            date = now;
    }
    
    // Convert to Persian date
    if (typeof persianDate !== 'undefined') {
        const pDate = new persianDate(date);
        return pDate.format('YYYY/MM/DD');
    }
    
    return date.toISOString().split('T')[0];
};

/**
 * Filter History - Save recent combinations
 */
function initFilterHistory() {
    window.filterHistory = {
        leads: JSON.parse(localStorage.getItem('filterHistory_leads') || '[]'),
        deals: JSON.parse(localStorage.getItem('filterHistory_deals') || '[]')
    };
}

/**
 * Save filter to history
 */
app.saveFilterHistory = function(view, label) {
    const history = window.filterHistory[view] || [];
    const timestamp = new Date().toLocaleString('fa-IR');
    
    // Add to beginning
    history.unshift({
        label: label,
        timestamp: timestamp,
        date: Date.now()
    });
    
    // Keep only last 10
    history.splice(10);
    
    // Save to localStorage
    window.filterHistory[view] = history;
    localStorage.setItem(`filterHistory_${view}`, JSON.stringify(history));
    
    // Update UI
    this.renderFilterHistory(view);
};

/**
 * Render filter history
 */
app.renderFilterHistory = function(view) {
    const container = document.getElementById(`${view}-filter-history-dropdown`);
    if (!container) return;
    
    const history = window.filterHistory[view] || [];
    
    if (history.length === 0) {
        container.innerHTML = '<div class="p-4 text-center text-gray-400 text-sm">تاریخچه‌ای وجود ندارد</div>';
        return;
    }
    
    container.innerHTML = history.map((item, index) => `
        <div class="filter-history-item" onclick="app.loadFilterHistory('${view}', '${item.label}')">
            <div class="filter-history-label">${item.label}</div>
            <div class="filter-history-meta">
                <span>${item.timestamp}</span>
                <button onclick="event.stopPropagation(); app.deleteFilterHistory('${view}', ${index})" class="filter-history-delete">
                    <i class="fa-solid fa-trash"></i>
                </button>
            </div>
        </div>
    `).join('');
};

/**
 * Load filter from history
 */
app.loadFilterHistory = function(view, label) {
    // TODO: Implement loading actual filter values from history
    console.log(`Loading filter: ${label} for ${view}`);
    
    // Close dropdown
    const dropdown = document.getElementById(`${view}-filter-history-dropdown`);
    if (dropdown) {
        dropdown.classList.remove('open');
    }
};

/**
 * Delete filter from history
 */
app.deleteFilterHistory = function(view, index) {
    const history = window.filterHistory[view] || [];
    history.splice(index, 1);
    window.filterHistory[view] = history;
    localStorage.setItem(`filterHistory_${view}`, JSON.stringify(history));
    this.renderFilterHistory(view);
};

/**
 * Toggle filter history dropdown
 */
app.toggleFilterHistory = function(view) {
    const dropdown = document.getElementById(`${view}-filter-history-dropdown`);
    if (!dropdown) return;
    
    dropdown.classList.toggle('open');
    this.renderFilterHistory(view);
};

/**
 * Voice Search - Microphone input
 */
function initVoiceSearch() {
    // Check if Web Speech API is supported
    window.voiceSearchSupported = 'webkitSpeechRecognition' in window || 'SpeechRecognition' in window;
    
    if (window.voiceSearchSupported) {
        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
        window.voiceRecognition = new SpeechRecognition();
        window.voiceRecognition.continuous = false;
        window.voiceRecognition.interimResults = false;
        window.voiceRecognition.lang = 'fa-IR';
        
        window.voiceRecognition.onresult = (event) => {
            const transcript = event.results[0][0].transcript;
            const searchInput = document.getElementById('leads-search');
            if (searchInput) {
                searchInput.value = transcript;
                app.loadLeads(1);
            }
            
            // Stop listening
            app.stopVoiceSearch();
        };
        
        window.voiceRecognition.onerror = (event) => {
            console.error('Voice search error:', event.error);
            app.stopVoiceSearch();
            
            if (event.error === 'not-allowed') {
                Swal.fire('خطا', 'دسترسی به میکروفون داده نشد', 'error');
            }
        };
        
        window.voiceRecognition.onend = () => {
            app.stopVoiceSearch();
        };
    }
}

/**
 * Start voice search
 */
app.startVoiceSearch = function() {
    if (!window.voiceSearchSupported) {
        Swal.fire('خطا', 'مرورگر شما از جستجوی صوتی پشتیبانی نمی‌کند', 'error');
        return;
    }
    
    const btn = document.querySelector('.voice-search-btn');
    if (btn) {
        btn.classList.add('listening');
    }
    
    try {
        window.voiceRecognition.start();
        
        // Haptic feedback
        if ('vibrate' in navigator) {
            navigator.vibrate(50);
        }
    } catch (e) {
        console.error('Voice search start error:', e);
        this.stopVoiceSearch();
    }
};

/**
 * Stop voice search
 */
app.stopVoiceSearch = function() {
    const btn = document.querySelector('.voice-search-btn');
    if (btn) {
        btn.classList.remove('listening');
    }
    
    try {
        if (window.voiceRecognition) {
            window.voiceRecognition.stop();
        }
    } catch (e) {
        // Ignore errors
    }
};

/**
 * Haptic Feedback
 */
function initHapticFeedback() {
    window.hapticEnabled = 'vibrate' in navigator;
}

/**
 * Trigger haptic feedback
 */
app.haptic = function(pattern = 10) {
    if (window.hapticEnabled) {
        navigator.vibrate(pattern);
    }
};

/**
 * Add haptic to all buttons
 */
app.addHapticToButtons = function() {
    document.querySelectorAll('button').forEach(btn => {
        btn.addEventListener('click', () => {
            app.haptic(10);
        });
    });
};

/**
 * Pull-to-Refresh
 */
function initPullToRefresh() {
    return; // Disabled: pull-to-refresh touchmove handler breaks native scrolling
    let startY = 0;
    let currentY = 0;
    let isPulling = false;
    const threshold = 100; // px to trigger refresh
    
    document.addEventListener('touchstart', (e) => {
        if (e.touches.length === 1 && window.scrollY === 0) {
            startY = e.touches[0].clientY;
            isPulling = true;
        }
    }, { passive: true });
    
    document.addEventListener('touchmove', (e) => {
        if (!isPulling) return;
        
        currentY = e.touches[0].clientY;
        const diff = currentY - startY;
        
        if (diff > 0 && diff < threshold * 2) {
            e.preventDefault();
            
            const indicator = document.querySelector('.pull-to-refresh-indicator');
            if (indicator) {
                indicator.style.top = `${-60 + diff / 2}px`;
                
                if (diff > threshold) {
                    indicator.classList.add('ready');
                } else {
                    indicator.classList.remove('ready');
                }
            }
        }
    }, { passive: false });
    
    document.addEventListener('touchend', () => {
        if (!isPulling) return;
        
        isPulling = false;
        
        const indicator = document.querySelector('.pull-to-refresh-indicator');
        if (indicator && indicator.classList.contains('ready')) {
            // Trigger refresh
            app.refresh();
        }
        
        if (indicator) {
            indicator.style.top = '-60px';
            indicator.classList.remove('ready');
        }
    });
}

/**
 * Quick Filter Toolbar
 */
app.renderQuickFilterToolbar = function(view) {
    const quickFilters = {
        leads: [
            { id: 'all', label: 'همه', active: true },
            { id: 'new', label: 'جدید', active: false },
            { id: 'contacted', label: 'تماس شده', active: false },
            { id: 'qualified', label: 'صالحیت‌دار', active: false },
            { id: 'converted', label: 'تبدیل شده', active: false }
        ],
        deals: [
            { id: 'all', label: 'همه', active: true },
            { id: 'pending', label: 'جاری', active: false },
            { id: 'won', label: 'موفق', active: false },
            { id: 'lost', label: 'ناموفق', active: false }
        ]
    };
    
    const container = document.getElementById('quick-filter-toolbar');
    if (!container) return;
    
    const filters = quickFilters[view] || [];
    
    container.innerHTML = `
        <div class="quick-filter-scroll">
            ${filters.map(filter => `
                <div class="quick-filter-item ${filter.active ? 'active' : ''}" 
                     onclick="app.applyQuickFilter('${view}', '${filter.id}', this)">
                    ${filter.label}
                </div>
            `).join('')}
        </div>
    `;
};

/**
 * Apply quick filter
 */
app.applyQuickFilter = function(view, filterId, element) {
    // Update active state
    element.parentElement.querySelectorAll('.quick-filter-item').forEach(item => {
        item.classList.remove('active');
    });
    element.classList.add('active');
    
    // Apply filter
    if (view === 'leads') {
        if (filterId === 'new') {
            document.getElementById('leads-stage-filter').value = 'new';
        } else if (filterId === 'all') {
            document.getElementById('leads-stage-filter').value = 'all';
        }
        this.loadLeads(1);
    } else if (view === 'deals') {
        if (filterId === 'pending') {
            document.getElementById('deals-status-filter').value = 'pending';
        } else if (filterId === 'won') {
            document.getElementById('deals-status-filter').value = 'won';
        } else if (filterId === 'lost') {
            document.getElementById('deals-status-filter').value = 'lost';
        } else if (filterId === 'all') {
            document.getElementById('deals-status-filter').value = 'all';
        }
        this.loadDeals();
    }
    
    // Haptic feedback
    this.haptic(10);
};

/**
 * Enhanced Loading State
 */
app.showLoading = function(message = 'در حال بارگذاری...') {
    let overlay = document.querySelector('.loading-overlay');
    
    if (!overlay) {
        overlay = document.createElement('div');
        overlay.className = 'loading-overlay';
        overlay.innerHTML = `
            <div class="loading-spinner"></div>
            <div class="loading-text">${message}</div>
        `;
        document.body.appendChild(overlay);
    }
    
    overlay.querySelector('.loading-text').textContent = message;
    overlay.classList.add('active');
};

/**
 * Hide Loading State
 */
app.hideLoading = function() {
    const overlay = document.querySelector('.loading-overlay');
    if (overlay) {
        overlay.classList.remove('active');
    }
};

app.operationManagerState = {
    loaded: false,
    rows: [],
    pagination: { current_page: 1, per_page: 20, total_count: 0, total_pages: 0 },
    selectedIds: new Set(),
    selectAllMatching: false,
    rules: [],
    runs: [],
    pools: [],
    conditionBuilderRows: []
};

app.loadOperationManager = async function() {
    if (!document.getElementById('view-operation-manager')) return;
    this.operationManagerState = this.operationManagerState || {};
    this.operationManagerState.selectedIds = this.operationManagerState.selectedIds || new Set();
    await this.loadOperationManagerOwners();
    this.initOperationManagerDatePickers();
    this.updateOperationManagerActionVisibility();
    this.applyOperationManagerPermissions();
    if (!this.operationManagerState.loaded) {
        this.fillDefaultOperationRule();
        this.operationManagerState.loaded = true;
    }
    await Promise.all([
        this.previewOperationManager(1),
        this.loadOperationManagerRules(),
        this.loadOperationManagerPools(),
        this.loadOperationManagerRuns()
    ]);
};

app.applyOperationManagerPermissions = function() {
    const canExecute = this.canCurrentUserCapabilityClient('operation_manager.execute');
    const canManageRules = this.canCurrentUserCapabilityClient('operation_manager.rules.manage');
    const executeBtn = document.getElementById('om-execute-btn');
    if (executeBtn) {
        executeBtn.classList.toggle('hidden', !canExecute);
        executeBtn.disabled = !canExecute;
    }
    const saveBtn = document.getElementById('om-rule-save-btn');
    if (saveBtn) {
        saveBtn.classList.toggle('hidden', !canManageRules);
        saveBtn.disabled = !canManageRules;
    }
};

app.loadOperationManagerOwners = async function() {
    if (Array.isArray(this.state.didarUsers) && this.state.didarUsers.length > 0) {
        this.populateOperationManagerOwnerSelects();
        return;
    }
    try {
        const res = await this.req('get_didar_users');
        if (res.status === 'success') {
            this.state.didarUsers = res.users || [];
        }
    } catch (e) {
        console.warn('Operation Manager owner list failed', e);
    }
    this.populateOperationManagerOwnerSelects();
};

app.populateOperationManagerOwnerSelects = function() {
    const users = Array.isArray(this.state.didarUsers) ? this.state.didarUsers : [];
    const options = users.map(user => {
        const id = user.owner_id || user.didar_user_id || user.didari_user_id || '';
        const name = user.display_name || user.name || user.email || `${user.first_name || ''} ${user.last_name || ''}`.trim() || id;
        if (!id) return '';
        return `<option value="${this.escapeHtml(id)}">${this.escapeHtml(name)}</option>`;
    }).join('');
    const singleSelects = {
        'om-prev-owner-filter': 'همه مسئول‌های قبلی',
        'om-operation-owner': 'انتخاب مسئول',
        'om-rule-operation-owner': 'انتخاب مسئول'
    };
    Object.entries(singleSelects).forEach(([id, label]) => {
        const el = document.getElementById(id);
        if (!el) return;
        const current = el.value || '';
        el.innerHTML = `<option value="">${label}</option>${options}`;
        if (current) el.value = current;
    });
    // Populate multi-owner dropdown
    const list = document.getElementById('om-owner-filter-list');
    if (list) {
        list.innerHTML = users.map(user => {
            const id = user.owner_id || user.didar_user_id || user.didari_user_id || '';
            const name = user.display_name || user.name || user.email || `${user.first_name || ''} ${user.last_name || ''}`.trim() || id;
            if (!id) return '';
            return `<label class="flex items-center gap-2 px-3 py-2 hover:bg-gray-800 cursor-pointer text-sm text-gray-200">
                <input type="checkbox" class="accent-yellow-500 om-owner-check" value="${this.escapeHtml(id)}" onchange="app.omOwnerFilterChange()">
                ${this.escapeHtml(name)}
            </label>`;
        }).join('');
        this.omOwnerFilterUpdateLabel();
    }
};

app.toggleOmOwnerDropdown = function() {
    const dd = document.getElementById('om-owner-filter-dropdown');
    if (!dd) return;
    dd.classList.toggle('hidden');
    // Close when clicking outside
    if (!dd.classList.contains('hidden')) {
        const close = (e) => {
            const wrap = document.getElementById('om-owner-filter-wrap');
            if (wrap && !wrap.contains(e.target)) {
                dd.classList.add('hidden');
                document.removeEventListener('click', close);
            }
        };
        setTimeout(() => document.addEventListener('click', close), 0);
    }
};

app.omOwnerFilterToggleAll = function(checked) {
    document.querySelectorAll('.om-owner-check').forEach(cb => { cb.checked = false; });
    const allCb = document.getElementById('om-owner-all');
    if (allCb) allCb.checked = true;
    this.omOwnerFilterUpdateLabel();
};

app.omOwnerFilterChange = function() {
    const checked = Array.from(document.querySelectorAll('.om-owner-check:checked'));
    const allCb = document.getElementById('om-owner-all');
    if (allCb) allCb.checked = checked.length === 0;
    this.omOwnerFilterUpdateLabel();
};

app.omOwnerFilterUpdateLabel = function() {
    const label = document.getElementById('om-owner-filter-label');
    if (!label) return;
    const checked = Array.from(document.querySelectorAll('.om-owner-check:checked'));
    if (checked.length === 0) {
        label.textContent = 'همه مسئول‌ها';
        label.classList.remove('text-yellow-300');
        label.classList.add('text-gray-300');
        return;
    }
    label.classList.add('text-yellow-300');
    label.classList.remove('text-gray-300');
    if (checked.length === 1) {
        label.textContent = checked[0].closest('label')?.textContent?.trim() || checked[0].value;
    } else {
        label.textContent = `${checked.length} مسئول انتخاب شده`;
    }
};

app.getOmOwnerFilterValues = function() {
    const checked = Array.from(document.querySelectorAll('.om-owner-check:checked'));
    return checked.map(cb => cb.value);
};

app.initOperationManagerDatePickers = function() {
    this.initJalaliDatePickers([
        document.getElementById('om-lead-date-from'),
        document.getElementById('om-lead-date-to'),
        document.getElementById('om-last-activity-from'),
        document.getElementById('om-last-activity-to'),
        document.getElementById('om-last-deal-from'),
        document.getElementById('om-last-deal-to'),
        document.getElementById('om-last-payment-from'),
        document.getElementById('om-last-payment-to')
    ].filter(Boolean));
};

app.operationManagerDateValue = function(id, endOfDay = false) {
    const raw = (document.getElementById(id)?.value || '').trim();
    if (!raw) return '';
    const normalized = this.toEnglishDigits(raw).replace(/\//g, '-');
    if (/^20\d{2}-\d{1,2}-\d{1,2}$/.test(normalized)) return normalized;
    const converted = persianToGregorian(this.toEnglishDigits(raw), endOfDay ? '23:59' : '00:00');
    return converted ? String(converted).split(' ')[0] : normalized;
};

app.collectOperationManagerFilters = function() {
    const filters = {};
    const add = (key, value) => {
        const v = typeof value === 'string' ? value.trim() : value;
        if (v !== '' && v !== null && v !== undefined) filters[key] = v;
    };
    add('search', document.getElementById('om-search')?.value || '');
    const ownerFilterValues = this.getOmOwnerFilterValues();
    const poolValues = ownerFilterValues.filter(v => String(v).startsWith('__pool:'));
    const ownerIds = ownerFilterValues.filter(v => !String(v).startsWith('__pool:'));
    if (poolValues.length > 0) {
        add('pool_key', String(poolValues[0]).slice(7));
        filters.no_owner = 1;
    }
    if (ownerIds.length > 0) {
        filters.owner_didar_ids = ownerIds;
    }
    add('previous_owner_id', document.getElementById('om-prev-owner-filter')?.value || '');
    add('sale_status', document.getElementById('om-sale-status-filter')?.value || '');
    add('deal_status', document.getElementById('om-deal-status-filter')?.value || '');
    if (!filters.pool_key) {
        add('pool_key', document.getElementById('om-pool-filter')?.value || '');
    }
    add('has_payment', document.getElementById('om-has-payment-filter')?.value || '');
    add('deal_older_than_days', document.getElementById('om-deal-older-than-days')?.value || '');
    if (document.getElementById('om-no-owner-filter')?.checked) filters.no_owner = 1;
    add('lead_date_from', this.operationManagerDateValue('om-lead-date-from'));
    add('lead_date_to', this.operationManagerDateValue('om-lead-date-to', true));
    add('last_activity_from', this.operationManagerDateValue('om-last-activity-from'));
    add('last_activity_to', this.operationManagerDateValue('om-last-activity-to', true));
    add('last_deal_from', this.operationManagerDateValue('om-last-deal-from'));
    add('last_deal_to', this.operationManagerDateValue('om-last-deal-to', true));
    add('last_payment_from', this.operationManagerDateValue('om-last-payment-from'));
    add('last_payment_to', this.operationManagerDateValue('om-last-payment-to', true));
    return filters;
};

app.previewOperationManager = async function(page = 1) {
    const results = document.getElementById('om-results');
    const summary = document.getElementById('om-preview-summary');
    if (!results) return;
    results.innerHTML = '<div class="p-6 text-center text-gray-400">در حال دریافت نتایج...</div>';
    try {
        const res = await this.req('operation_manager_preview', {
            filters: JSON.stringify(this.collectOperationManagerFilters()),
            page,
            per_page: this.operationManagerState?.pagination?.per_page || 20
        }, { requestKey: 'operation_manager_preview' });
        if (res.status !== 'success') {
            results.innerHTML = `<div class="p-6 text-center text-red-400">${this.escapeHtml(res.message || 'خطا در دریافت نتایج')}</div>`;
            return;
        }
        this.operationManagerState.rows = res.rows || [];
        this.operationManagerState.pagination = res.pagination || { current_page: page, per_page: 20, total_count: 0, total_pages: 0 };
        if (summary) {
            summary.innerHTML = `تعداد مطابق فیلتر: <b class="text-yellow-400">${this.formatNumber(this.operationManagerState.pagination.total_count || 0)}</b> | انتخاب‌شده: <b class="text-cyan-300">${this.operationManagerState.selectAllMatching ? 'همه' : this.formatNumber(this.operationManagerState.selectedIds.size || 0)}</b>`;
        }
        this.renderOperationManagerRows();
        this.renderOperationManagerPagination();
    } catch (e) {
        results.innerHTML = `<div class="p-6 text-center text-red-400">${this.escapeHtml(e.message || 'خطای ارتباط')}</div>`;
    }
};

app.renderOperationManagerRows = function() {
    const container = document.getElementById('om-results');
    if (!container) return;
    const rows = this.operationManagerState.rows || [];
    if (!rows.length) {
        container.innerHTML = '<div class="p-8 text-center text-gray-500">موردی مطابق فیلترها پیدا نشد</div>';
        return;
    }
    const money = value => Number(value || 0) > 0 ? `${this.formatNumber(Number(value || 0))} تومان` : '-';
    container.innerHTML = `
        <table class="w-full text-sm">
            <thead class="bg-gray-900/80 text-gray-400 border-b border-gray-700">
                <tr>
                    <th class="p-3 text-right w-10"></th>
                    <th class="p-3 text-right">لید</th>
                    <th class="p-3 text-right">مسئول</th>
                    <th class="p-3 text-right">آخرین معامله</th>
                    <th class="p-3 text-right">آخرین فعالیت</th>
                    <th class="p-3 text-right">آخرین پرداخت</th>
                    <th class="p-3 text-right">مخزن</th>
                </tr>
            </thead>
            <tbody class="divide-y divide-gray-800">
                ${rows.map(row => {
                    const id = String(row.person_id || '');
                    const selected = this.operationManagerState.selectedIds.has(id);
                    const name = `${row.first_name || ''} ${row.last_name || ''}`.trim() || row.display_name || '-';
                    return `
                        <tr class="hover:bg-gray-800/50">
                            <td class="p-3 align-top">
                                <input type="checkbox" class="accent-yellow-500" ${selected ? 'checked' : ''} onchange="app.toggleOperationManagerRow('${this.escapeHtml(id)}', this.checked)">
                            </td>
                            <td class="p-3 align-top">
                                <button onclick="app.openPersonProfile('${this.escapeHtml(row.didar_contact_id || id)}')" class="text-right">
                                    <div class="font-bold text-white hover:text-yellow-400">${this.escapeHtml(name)}</div>
                                    <div class="text-xs text-gray-500 dir-ltr text-left">${this.escapeHtml(row.mobile_phone || '-')}</div>
                                    <div class="text-[11px] text-gray-500">کد مشتری: ${this.escapeHtml(row.customer_code || '-')}</div>
                                </button>
                            </td>
                            <td class="p-3 align-top">
                                <div class="text-gray-200">${this.escapeHtml(row.owner_name || 'بدون مسئول')}</div>
                                <div class="text-[11px] text-gray-500">قبلی: ${this.escapeHtml(row.previous_owner_name || '-')}</div>
                            </td>
                            <td class="p-3 align-top">
                                <div class="text-gray-200">${this.escapeHtml(row.last_deal_status || '-')}</div>
                                <div class="text-[11px] text-gray-500">${this.formatKpiDate(row.last_deal_at || '') || '-'}</div>
                                <div class="text-[11px] text-gray-500">${this.escapeHtml(row.last_deal_pipeline_stage || '')}</div>
                            </td>
                            <td class="p-3 align-top">
                                <div class="text-gray-200">${this.formatKpiDate(row.last_activity_at || '') || '-'}</div>
                                <div class="text-[11px] text-gray-500">${this.escapeHtml(row.last_activity_type || '-')}</div>
                            </td>
                            <td class="p-3 align-top">
                                <div class="${row.has_any_payment ? 'text-emerald-300' : 'text-gray-500'}">${this.formatKpiDate(row.last_payment_at || '') || '-'}</div>
                                <div class="text-[11px] text-gray-500">${money(row.last_payment_amount)}</div>
                            </td>
                            <td class="p-3 align-top">
                                <span class="px-2 py-1 rounded bg-gray-800 border border-gray-700 text-xs">${this.escapeHtml(row.pool_title || row.pool_key || '-')}</span>
                            </td>
                        </tr>
                    `;
                }).join('')}
            </tbody>
        </table>
    `;
};

app.renderOperationManagerPagination = function() {
    const container = document.getElementById('om-pagination');
    if (!container) return;
    const p = this.operationManagerState.pagination || {};
    const totalPages = Number(p.total_pages || 0);
    if (totalPages <= 1) {
        container.innerHTML = '';
        return;
    }
    const current = Number(p.current_page || 1);
    const start = Math.max(1, current - 2);
    const end = Math.min(totalPages, current + 2);
    let html = '<div class="flex justify-center items-center gap-2 flex-wrap">';
    html += `<button ${current <= 1 ? 'disabled' : ''} onclick="app.previewOperationManager(${current - 1})" class="px-3 py-1 bg-gray-800 hover:bg-gray-700 disabled:opacity-40 rounded text-sm">قبلی</button>`;
    for (let i = start; i <= end; i++) {
        html += `<button onclick="app.previewOperationManager(${i})" class="px-3 py-1 ${i === current ? 'bg-yellow-600 text-white' : 'bg-gray-800 hover:bg-gray-700 text-gray-200'} rounded text-sm">${i}</button>`;
    }
    html += `<button ${current >= totalPages ? 'disabled' : ''} onclick="app.previewOperationManager(${current + 1})" class="px-3 py-1 bg-gray-800 hover:bg-gray-700 disabled:opacity-40 rounded text-sm">بعدی</button>`;
    html += `<span class="text-xs text-gray-500 mr-2">${this.formatNumber(p.total_count || 0)} مورد</span></div>`;
    container.innerHTML = html;
};

app.toggleOperationManagerRow = function(id, checked) {
    this.operationManagerState.selectedIds = this.operationManagerState.selectedIds || new Set();
    if (checked) this.operationManagerState.selectedIds.add(String(id));
    else this.operationManagerState.selectedIds.delete(String(id));
    this.operationManagerState.selectAllMatching = false;
    const all = document.getElementById('om-select-all-matching');
    if (all) all.checked = false;
    this.updateOperationManagerSummarySelection();
};

app.toggleOperationManagerAllMatching = function(checked) {
    this.operationManagerState.selectAllMatching = !!checked;
    if (checked) this.operationManagerState.selectedIds.clear();
    this.renderOperationManagerRows();
    this.updateOperationManagerSummarySelection();
};

app.clearOperationManagerSelection = function() {
    this.operationManagerState.selectedIds.clear();
    this.operationManagerState.selectAllMatching = false;
    const all = document.getElementById('om-select-all-matching');
    if (all) all.checked = false;
    this.renderOperationManagerRows();
    this.updateOperationManagerSummarySelection();
};

app.updateOperationManagerSummarySelection = function() {
    const summary = document.getElementById('om-preview-summary');
    const p = this.operationManagerState.pagination || {};
    if (summary) {
        summary.innerHTML = `تعداد مطابق فیلتر: <b class="text-yellow-400">${this.formatNumber(p.total_count || 0)}</b> | انتخاب‌شده: <b class="text-cyan-300">${this.operationManagerState.selectAllMatching ? 'همه' : this.formatNumber(this.operationManagerState.selectedIds.size || 0)}</b>`;
    }
};

app.updateOperationManagerActionVisibility = function() {
    const type = document.getElementById('om-operation-type')?.value || 'change_owner';
    const ownerWrap = document.getElementById('om-operation-owner-wrap');
    if (ownerWrap) ownerWrap.classList.toggle('hidden', type !== 'change_owner');
};

app.buildOperationManagerOperation = function() {
    const type = document.getElementById('om-operation-type')?.value || 'change_owner';
    const reason = (document.getElementById('om-operation-reason')?.value || '').trim();
    if (type === 'change_owner') {
        const owner = document.getElementById('om-operation-owner')?.value || '';
        if (!owner) throw new Error('مسئول جدید را انتخاب کنید');
        return { actions: [{ type: 'change_owner', owner_didar_id: owner, reason }] };
    }
    if (type === 'move_to_crm_pool') {
        return { actions: [{ type: 'move_lead_to_pool', pool_key: 'crm_leads', reason: reason || 'Manual Operation Manager move' }] };
    }
    return {
        actions: [
            { type: 'mark_last_deal_lost', failure_reason: 'sales_agent_underperformance', reason: reason || 'عدم توانمندی کارشناس فروش' },
            { type: 'move_lead_to_pool', pool_key: 'crm_leads', reason: reason || 'Moved by Operation Manager' }
        ]
    };
};

app.executeOperationManager = async function() {
    try {
        const selectedIds = Array.from(this.operationManagerState.selectedIds || []);
        if (!this.operationManagerState.selectAllMatching && selectedIds.length === 0) {
            Swal.fire('انتخاب نشده', 'حداقل یک لید را انتخاب کنید یا گزینه انتخاب همه موارد مطابق فیلتر را بزنید.', 'warning');
            return;
        }
        const operation = this.buildOperationManagerOperation();
        const countText = this.operationManagerState.selectAllMatching ? 'همه موارد مطابق فیلتر' : `${this.formatNumber(selectedIds.length)} لید`;
        const confirmed = await Swal.fire({
            title: 'اجرای عملیات',
            text: `عملیات روی ${countText} اجرا شود؟`,
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: 'اجرا',
            cancelButtonText: 'انصراف',
            reverseButtons: true
        });
        if (!confirmed.isConfirmed) return;
        const filters = this.collectOperationManagerFilters();
        const res = await this.req('operation_manager_execute', {
            filters: JSON.stringify(filters),
            operation: JSON.stringify(operation),
            selected_ids: JSON.stringify(selectedIds),
            select_all: this.operationManagerState.selectAllMatching ? 1 : 0
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'عملیات انجام نشد', 'error');
            return;
        }
        Swal.fire('انجام شد', `موفق: ${this.formatNumber(res.summary?.success || 0)} | خطا: ${this.formatNumber(res.summary?.failed || 0)}`, 'success');
        this.clearOperationManagerSelection();
        await Promise.all([this.previewOperationManager(1), this.loadOperationManagerRuns(), this.loadOperationManagerPools()]);
    } catch (e) {
        Swal.fire('خطا', e.message || 'خطا در اجرای عملیات', 'error');
    }
};

app.clearOperationManagerFilters = function() {
    ['om-search','om-deal-older-than-days','om-lead-date-from','om-lead-date-to','om-last-activity-from','om-last-activity-to','om-last-deal-from','om-last-deal-to','om-last-payment-from','om-last-payment-to'].forEach(id => {
        const el = document.getElementById(id);
        if (el) el.value = '';
    });
    ['om-prev-owner-filter','om-sale-status-filter','om-deal-status-filter','om-pool-filter','om-has-payment-filter'].forEach(id => {
        const el = document.getElementById(id);
        if (el) el.value = '';
    });
    document.querySelectorAll('.om-owner-check').forEach(cb => { cb.checked = false; });
    const allCb = document.getElementById('om-owner-all');
    if (allCb) allCb.checked = true;
    this.omOwnerFilterUpdateLabel();
    const noOwner = document.getElementById('om-no-owner-filter');
    if (noOwner) noOwner.checked = false;
    this.clearOperationManagerSelection();
    this.previewOperationManager(1);
};

app.exportOperationManagerResults = async function() {
    const total = this.operationManagerState?.pagination?.total_count || 0;
    if (total === 0) {
        Swal.fire('توجه', 'ابتدا پیش‌نمایش را اجرا کنید تا نتایجی برای خروجی وجود داشته باشد.', 'info');
        return;
    }

    const confirmed = await Swal.fire({
        title: 'خروجی CSV',
        text: `خروجی ${this.formatNumber(total)} رکورد مطابق فیلترهای فعلی دریافت شود؟`,
        icon: 'question',
        showCancelButton: true,
        confirmButtonText: 'دانلود',
        cancelButtonText: 'انصراف'
    });
    if (!confirmed.isConfirmed) return;

    Swal.fire({ title: 'در حال آماده‌سازی...', allowOutsideClick: false, didOpen: () => Swal.showLoading() });

    try {
        const filters = this.collectOperationManagerFilters();
        const formData = new FormData();
        formData.append('filters', JSON.stringify(filters));

        const response = await fetch('?api=operation_manager_export', {
            method: 'POST',
            body: formData,
            credentials: 'same-origin'
        });

        if (!response.ok) {
            throw new Error('خطا در دریافت فایل');
        }

        const blob = await response.blob();
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'operation_manager_' + new Date().toISOString().slice(0, 10) + '.csv';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
        Swal.close();
    } catch (e) {
        console.error('Export failed:', e);
        Swal.close();
        Swal.fire('خطا', e.message || 'خطا در خروجی گرفتن', 'error');
    }
};

app.fillDefaultOperationRule = function() {
    const name = document.getElementById('om-rule-name');
    const filter = document.getElementById('om-rule-json-filter');
    const condition = document.getElementById('om-rule-json-condition');
    const operation = document.getElementById('om-rule-json-operation');
    if (name && !name.value) name.value = 'انتقال لیدهای بدون پرداخت پس از یک ماه';
    if (filter && !filter.value) filter.value = JSON.stringify({ deal_older_than_days: 30, has_payment: 0, deal_status: 'Pending' }, null, 2);
    if (condition && !condition.value) condition.value = JSON.stringify({ all: [
        { field: 'last_deal_at', operator: 'older_than', value: 30, unit: 'days' },
        { field: 'has_any_payment', operator: 'equals', value: false },
        { field: 'last_deal_status', operator: 'not_equals', value: 'Won' }
    ] }, null, 2);
    if (operation && !operation.value) operation.value = JSON.stringify({ actions: [
        { type: 'mark_last_deal_lost', failure_reason: 'sales_agent_underperformance', reason: 'عدم توانمندی کارشناس فروش' },
        { type: 'move_lead_to_pool', pool_key: 'crm_leads', reason: 'No conversion after 30 days without payment' }
    ] }, null, 2);
    this.syncOperationRuleJsonToBuilder();
};

app.resetOperationManagerRuleForm = function() {
    ['om-rule-id','om-rule-name','om-rule-json-filter','om-rule-json-condition','om-rule-json-operation'].forEach(id => {
        const el = document.getElementById(id);
        if (el) el.value = '';
    });
    const active = document.getElementById('om-rule-active');
    if (active) active.checked = true;
    const schedule = document.getElementById('om-rule-schedule-type');
    if (schedule) schedule.value = 'daily';
    const operationType = document.getElementById('om-rule-operation-type');
    if (operationType) operationType.value = 'mark_lost_and_pool';
    const operationOwner = document.getElementById('om-rule-operation-owner');
    if (operationOwner) operationOwner.value = '';
    const reason = document.getElementById('om-rule-operation-reason');
    if (reason) reason.value = 'عدم توانمندی کارشناس فروش';
    this.operationManagerState.conditionBuilderRows = [];
    this.fillDefaultOperationRule();
};

app.parseOperationRuleJson = function(id) {
    const raw = (document.getElementById(id)?.value || '').trim();
    if (!raw) return {};
    return JSON.parse(raw);
};

app.decodeOperationRuleJson = function(rule, key) {
    const direct = rule?.[key];
    if (direct && typeof direct === 'object') return direct;
    const raw = rule?.[`${key}_json`];
    if (!raw) return {};
    try {
        const parsed = JSON.parse(raw);
        return parsed && typeof parsed === 'object' ? parsed : {};
    } catch (_) {
        return {};
    }
};

app.operationRuleConditionFields = function() {
    return {
        last_deal_at: {
            label: 'آخرین معامله',
            operators: ['older_than'],
            valueType: 'number',
            unit: 'days',
            defaultValue: 30
        },
        last_activity_at: {
            label: 'آخرین فعالیت',
            operators: ['older_than'],
            valueType: 'number',
            unit: 'days',
            defaultValue: 30
        },
        lead_registered_at: {
            label: 'تاریخ ثبت لید',
            operators: ['older_than'],
            valueType: 'number',
            unit: 'days',
            defaultValue: 30
        },
        last_payment_at: {
            label: 'آخرین پرداخت',
            operators: ['older_than'],
            valueType: 'number',
            unit: 'days',
            defaultValue: 30
        },
        has_any_payment: {
            label: 'پرداخت',
            operators: ['equals'],
            valueType: 'boolean',
            defaultValue: false
        },
        last_deal_status: {
            label: 'وضعیت آخرین معامله',
            operators: ['equals', 'not_equals'],
            valueType: 'deal_status',
            defaultValue: 'Won'
        },
        sale_status: {
            label: 'وضعیت فروش لید',
            operators: ['equals'],
            valueType: 'sale_status',
            defaultValue: 0
        },
        owner_empty: {
            label: 'مسئول ندارد',
            operators: ['equals'],
            valueType: 'boolean',
            defaultValue: true
        },
        pool_key: {
            label: 'مخزن لید',
            operators: ['equals'],
            valueType: 'pool_key',
            defaultValue: 'crm_leads'
        }
    };
};

app.operationRuleOperatorLabel = function(operator) {
    return {
        older_than: 'قدیمی‌تر از',
        equals: 'برابر با',
        not_equals: 'برابر نباشد با'
    }[operator] || operator;
};

app.normalizeOperationRuleConditionRow = function(row = {}) {
    const fields = this.operationRuleConditionFields();
    const field = fields[row.field] ? row.field : 'last_deal_at';
    const meta = fields[field];
    const operator = meta.operators.includes(row.operator) ? row.operator : meta.operators[0];
    const value = row.value !== undefined && row.value !== null && row.value !== '' ? row.value : meta.defaultValue;
    return { field, operator, value, unit: meta.unit || row.unit || '' };
};

app.addOperationRuleCondition = function(row = {}) {
    this.operationManagerState.conditionBuilderRows = this.operationManagerState.conditionBuilderRows || [];
    this.operationManagerState.conditionBuilderRows.push(this.normalizeOperationRuleConditionRow(row));
    this.renderOperationRuleConditionBuilder();
    this.syncOperationRuleBuilderToJson();
};

app.removeOperationRuleCondition = function(index) {
    this.operationManagerState.conditionBuilderRows = this.operationManagerState.conditionBuilderRows || [];
    this.operationManagerState.conditionBuilderRows.splice(index, 1);
    this.renderOperationRuleConditionBuilder();
    this.syncOperationRuleBuilderToJson();
};

app.updateOperationRuleCondition = function(index, key, value) {
    this.operationManagerState.conditionBuilderRows = this.operationManagerState.conditionBuilderRows || [];
    const row = this.operationManagerState.conditionBuilderRows[index] || {};
    row[key] = value;
    this.operationManagerState.conditionBuilderRows[index] = this.normalizeOperationRuleConditionRow(row);
    this.renderOperationRuleConditionBuilder();
    this.syncOperationRuleBuilderToJson();
};

app.renderOperationRuleConditionBuilder = function() {
    const container = document.getElementById('om-rule-condition-builder');
    if (!container) return;
    const fields = this.operationRuleConditionFields();
    const rows = this.operationManagerState.conditionBuilderRows || [];
    if (!rows.length) {
        container.innerHTML = '<div class="text-xs text-gray-500 border border-dashed border-gray-700 rounded p-3 text-center">شرطی تعریف نشده است. از «شرط جدید» یا «قالب یک ماهه» استفاده کنید.</div>';
        return;
    }
    container.innerHTML = rows.map((row, index) => {
        const meta = fields[row.field] || fields.last_deal_at;
        const fieldOptions = Object.entries(fields).map(([key, item]) => `<option value="${key}" ${key === row.field ? 'selected' : ''}>${this.escapeHtml(item.label)}</option>`).join('');
        const operatorOptions = meta.operators.map(op => `<option value="${op}" ${op === row.operator ? 'selected' : ''}>${this.escapeHtml(this.operationRuleOperatorLabel(op))}</option>`).join('');
        return `
            <div class="grid grid-cols-12 gap-2 items-center bg-gray-950/50 border border-gray-800 rounded p-2">
                <select class="input bg-gray-900 col-span-12 md:col-span-4 text-xs" onchange="app.updateOperationRuleCondition(${index}, 'field', this.value)">
                    ${fieldOptions}
                </select>
                <select class="input bg-gray-900 col-span-6 md:col-span-3 text-xs" onchange="app.updateOperationRuleCondition(${index}, 'operator', this.value)">
                    ${operatorOptions}
                </select>
                <div class="col-span-5 md:col-span-4">
                    ${this.renderOperationRuleConditionValue(index, row, meta)}
                </div>
                <button onclick="app.removeOperationRuleCondition(${index})" class="col-span-1 text-red-300 hover:text-red-200 text-sm" title="حذف شرط">
                    <i class="fa-solid fa-trash"></i>
                </button>
            </div>
        `;
    }).join('');
};

app.renderOperationRuleConditionValue = function(index, row, meta) {
    const value = row.value;
    if (meta.valueType === 'boolean') {
        return `
            <select class="input bg-gray-900 w-full text-xs" onchange="app.updateOperationRuleCondition(${index}, 'value', this.value === 'true')">
                <option value="true" ${value === true || value === 1 || value === 'true' ? 'selected' : ''}>بله</option>
                <option value="false" ${value === false || value === 0 || value === 'false' ? 'selected' : ''}>خیر</option>
            </select>
        `;
    }
    if (meta.valueType === 'deal_status') {
        return `
            <select class="input bg-gray-900 w-full text-xs" onchange="app.updateOperationRuleCondition(${index}, 'value', this.value)">
                <option value="Pending" ${value === 'Pending' ? 'selected' : ''}>در جریان</option>
                <option value="Won" ${value === 'Won' ? 'selected' : ''}>موفق</option>
                <option value="Lost" ${value === 'Lost' ? 'selected' : ''}>ناموفق</option>
            </select>
        `;
    }
    if (meta.valueType === 'sale_status') {
        return `
            <select class="input bg-gray-900 w-full text-xs" onchange="app.updateOperationRuleCondition(${index}, 'value', Number(this.value))">
                <option value="0" ${Number(value) === 0 ? 'selected' : ''}>ناموفق</option>
                <option value="1" ${Number(value) === 1 ? 'selected' : ''}>موفق</option>
            </select>
        `;
    }
    if (meta.valueType === 'pool_key') {
        return `
            <select class="input bg-gray-900 w-full text-xs" onchange="app.updateOperationRuleCondition(${index}, 'value', this.value)">
                <option value="crm_leads" ${value === 'crm_leads' ? 'selected' : ''}>لید CRM</option>
            </select>
        `;
    }
    return `
        <div class="flex items-center gap-2">
            <input type="number" min="1" class="input bg-gray-900 w-full text-xs" value="${this.escapeHtml(String(value || 30))}" onchange="app.updateOperationRuleCondition(${index}, 'value', Number(this.value || 0))">
            <span class="text-[11px] text-gray-500 whitespace-nowrap">روز</span>
        </div>
    `;
};

app.syncOperationRuleBuilderToJson = function() {
    const conditionTextarea = document.getElementById('om-rule-json-condition');
    const operationTextarea = document.getElementById('om-rule-json-operation');
    const rows = (this.operationManagerState.conditionBuilderRows || []).map(row => this.normalizeOperationRuleConditionRow(row));
    if (conditionTextarea) {
        conditionTextarea.value = JSON.stringify({ all: rows }, null, 2);
    }
    if (operationTextarea) {
        operationTextarea.value = JSON.stringify(this.buildOperationRuleOperationFromBuilder(), null, 2);
    }
    const operationType = document.getElementById('om-rule-operation-type')?.value || 'mark_lost_and_pool';
    const ownerWrap = document.getElementById('om-rule-operation-owner-wrap');
    if (ownerWrap) ownerWrap.classList.toggle('hidden', operationType !== 'change_owner');
};

app.syncOperationRuleJsonToBuilder = function() {
    try {
        const condition = this.parseOperationRuleJson('om-rule-json-condition');
        const rows = Array.isArray(condition.all) ? condition.all : [];
        this.operationManagerState.conditionBuilderRows = rows.map(row => this.normalizeOperationRuleConditionRow(row));
        this.renderOperationRuleConditionBuilder();
    } catch (_) {}
    try {
        const operation = this.parseOperationRuleJson('om-rule-json-operation');
        this.applyOperationRuleOperationToBuilder(operation);
    } catch (_) {}
};

app.buildOperationRuleOperationFromBuilder = function() {
    const type = document.getElementById('om-rule-operation-type')?.value || 'mark_lost_and_pool';
    const owner = document.getElementById('om-rule-operation-owner')?.value || '';
    const reason = (document.getElementById('om-rule-operation-reason')?.value || '').trim() || 'Operation Manager rule';
    if (type === 'change_owner') {
        return { actions: [{ type: 'change_owner', owner_didar_id: owner, reason }] };
    }
    if (type === 'move_to_crm_pool') {
        return { actions: [{ type: 'move_lead_to_pool', pool_key: 'crm_leads', reason }] };
    }
    return { actions: [
        { type: 'mark_last_deal_lost', failure_reason: 'sales_agent_underperformance', reason },
        { type: 'move_lead_to_pool', pool_key: 'crm_leads', reason }
    ] };
};

app.applyOperationRuleOperationToBuilder = function(operation) {
    const actions = Array.isArray(operation?.actions) ? operation.actions : [];
    const hasLost = actions.some(a => a.type === 'mark_last_deal_lost');
    const poolAction = actions.find(a => a.type === 'move_lead_to_pool');
    const ownerAction = actions.find(a => a.type === 'change_owner');
    const typeEl = document.getElementById('om-rule-operation-type');
    if (typeEl) {
        typeEl.value = ownerAction ? 'change_owner' : (hasLost && poolAction ? 'mark_lost_and_pool' : 'move_to_crm_pool');
    }
    const ownerEl = document.getElementById('om-rule-operation-owner');
    if (ownerEl && ownerAction) ownerEl.value = ownerAction.owner_didar_id || '';
    const reasonEl = document.getElementById('om-rule-operation-reason');
    const reason = ownerAction?.reason || poolAction?.reason || actions[0]?.reason || '';
    if (reasonEl && reason) reasonEl.value = reason;
    const ownerWrap = document.getElementById('om-rule-operation-owner-wrap');
    if (ownerWrap && typeEl) ownerWrap.classList.toggle('hidden', typeEl.value !== 'change_owner');
};

app.saveOperationManagerRule = async function() {
    try {
        this.syncOperationRuleBuilderToJson();
        const name = (document.getElementById('om-rule-name')?.value || '').trim();
        if (!name) throw new Error('نام شرط را وارد کنید');
        const res = await this.req('operation_rules_save', {
            id: document.getElementById('om-rule-id')?.value || '',
            name,
            is_active: document.getElementById('om-rule-active')?.checked ? 1 : 0,
            schedule_type: document.getElementById('om-rule-schedule-type')?.value || 'daily',
            filter: JSON.stringify(this.parseOperationRuleJson('om-rule-json-filter')),
            condition: JSON.stringify(this.parseOperationRuleJson('om-rule-json-condition')),
            operation: JSON.stringify(this.parseOperationRuleJson('om-rule-json-operation'))
        });
        if (res.status !== 'success') {
            Swal.fire('خطا', res.message || 'شرط ذخیره نشد', 'error');
            return;
        }
        Swal.fire('ذخیره شد', 'شرط عملیات ذخیره شد.', 'success');
        await this.loadOperationManagerRules();
        this.resetOperationManagerRuleForm();
    } catch (e) {
        Swal.fire('خطا', e.message || 'JSON نامعتبر است', 'error');
    }
};

app.loadOperationManagerRules = async function() {
    const container = document.getElementById('om-rules-list');
    if (!container) return;
    container.innerHTML = '<div class="text-xs text-gray-500">در حال دریافت شرط‌ها...</div>';
    const res = await this.req('operation_rules_list');
    if (res.status !== 'success') {
        container.innerHTML = `<div class="text-xs text-red-400">${this.escapeHtml(res.message || 'خطا')}</div>`;
        return;
    }
    this.operationManagerState.rules = res.rules || [];
    if (!this.operationManagerState.rules.length) {
        container.innerHTML = '<div class="text-xs text-gray-500">هنوز شرطی ذخیره نشده است.</div>';
        return;
    }
    const canManageRules = this.canCurrentUserCapabilityClient('operation_manager.rules.manage');
    const canRunRules = this.canCurrentUserCapabilityClient('operation_manager.automation.run');
    container.innerHTML = this.operationManagerState.rules.map(rule => `
        <div class="bg-gray-900/70 border border-gray-700 rounded p-3">
            <div class="flex items-start justify-between gap-2">
                <div>
                    <div class="font-bold text-sm text-white">${this.escapeHtml(rule.name || '-')}</div>
                    <div class="text-[11px] text-gray-500">اجرا: ${this.escapeHtml(rule.schedule_type || '-')} | وضعیت: ${Number(rule.is_active) === 1 ? 'فعال' : 'غیرفعال'}</div>
                </div>
                <div class="flex gap-2 text-xs">
                    ${canManageRules ? `<button onclick="app.editOperationManagerRule(${Number(rule.id)})" class="text-cyan-300 hover:text-cyan-200">ویرایش</button>` : ''}
                    ${canRunRules ? `<button onclick="app.runOperationManagerRule(${Number(rule.id)})" class="text-yellow-300 hover:text-yellow-200">اجرا</button>` : ''}
                    ${canManageRules ? `<button onclick="app.deleteOperationManagerRule(${Number(rule.id)})" class="text-red-300 hover:text-red-200">حذف</button>` : ''}
                </div>
            </div>
        </div>
    `).join('');
};

app.editOperationManagerRule = function(id) {
    const rule = (this.operationManagerState.rules || []).find(r => Number(r.id) === Number(id));
    if (!rule) return;
    document.getElementById('om-rule-id').value = rule.id || '';
    document.getElementById('om-rule-name').value = rule.name || '';
    document.getElementById('om-rule-active').checked = Number(rule.is_active) === 1;
    document.getElementById('om-rule-schedule-type').value = rule.schedule_type || 'daily';
    document.getElementById('om-rule-json-filter').value = JSON.stringify(this.decodeOperationRuleJson(rule, 'filter'), null, 2);
    document.getElementById('om-rule-json-condition').value = JSON.stringify(this.decodeOperationRuleJson(rule, 'condition'), null, 2);
    document.getElementById('om-rule-json-operation').value = JSON.stringify(this.decodeOperationRuleJson(rule, 'operation'), null, 2);
    this.syncOperationRuleJsonToBuilder();
};

app.runOperationManagerRule = async function(id) {
    const confirmed = await Swal.fire({ title: 'اجرای شرط', text: 'این شرط الان اجرا شود؟', icon: 'warning', showCancelButton: true, confirmButtonText: 'اجرا', cancelButtonText: 'انصراف', reverseButtons: true });
    if (!confirmed.isConfirmed) return;
    const res = await this.req('operation_rules_run', { id });
    if (res.status === 'success') {
        Swal.fire('انجام شد', `موفق: ${this.formatNumber(res.summary?.success || 0)} | خطا: ${this.formatNumber(res.summary?.failed || 0)}`, 'success');
        await Promise.all([this.previewOperationManager(1), this.loadOperationManagerRuns(), this.loadOperationManagerPools()]);
    } else {
        Swal.fire('خطا', res.message || 'اجرا نشد', 'error');
    }
};

app.deleteOperationManagerRule = async function(id) {
    const confirmed = await Swal.fire({ title: 'حذف شرط', text: 'این شرط حذف شود؟', icon: 'warning', showCancelButton: true, confirmButtonText: 'حذف', cancelButtonText: 'انصراف', reverseButtons: true });
    if (!confirmed.isConfirmed) return;
    const res = await this.req('operation_rules_delete', { id });
    if (res.status === 'success') {
        await this.loadOperationManagerRules();
    } else {
        Swal.fire('خطا', res.message || 'حذف نشد', 'error');
    }
};

app.loadOperationManagerRuns = async function() {
    const container = document.getElementById('om-runs-list');
    if (!container) return;
    const res = await this.req('operation_runs_list', { limit: 5 });
    if (res.status !== 'success') {
        container.innerHTML = `<div class="text-xs text-red-400">${this.escapeHtml(res.message || 'خطا')}</div>`;
        return;
    }
    const runs = res.runs || [];
    if (!runs.length) {
        container.innerHTML = '<div class="text-xs text-gray-500">اجرایی ثبت نشده است.</div>';
        return;
    }
    container.innerHTML = runs.map(run => `
        <div class="bg-gray-900/70 border border-gray-700 rounded p-3">
            <div class="flex justify-between gap-2">
                <div class="font-bold text-white text-xs">${this.escapeHtml(run.rule_name || run.source || '-')}</div>
                <div class="text-[11px] text-gray-500">${this.formatKpiDate(run.started_at || '') || '-'}</div>
            </div>
            <div class="text-[11px] text-gray-400 mt-1">اثرگذار: ${this.formatNumber(run.affected_count || 0)} | خطا: ${this.formatNumber(run.failed_count || 0)} | بررسی: ${this.formatNumber(run.matched_count || 0)}</div>
        </div>
    `).join('');
};

app.loadOperationManagerPools = async function() {
    const container = document.getElementById('om-pools-list');
    if (!container) return;
    const res = await this.req('crm_lead_pool_list');
    if (res.status !== 'success') {
        container.innerHTML = `<div class="text-xs text-red-400">${this.escapeHtml(res.message || 'خطا')}</div>`;
        return;
    }
    const pools = res.pools || [];
    if (!pools.length) {
        container.innerHTML = '<div class="text-xs text-gray-500">مخزنی ثبت نشده است.</div>';
        return;
    }
    container.innerHTML = pools.map(pool => `
        <div class="bg-gray-900/70 border border-gray-700 rounded p-3 flex items-center justify-between">
            <div>
                <div class="font-bold text-white">${this.escapeHtml(pool.title || pool.name || pool.pool_key || '-')}</div>
                <div class="text-[11px] text-gray-500">${this.escapeHtml(pool.description || pool.pool_key || '')}</div>
            </div>
            <div class="text-lg font-bold text-emerald-300">${this.formatNumber(pool.lead_count || 0)}</div>
        </div>
    `).join('');
};

/**
 * Quick Contact - Make phone calls with number selection
 */
app.quickContact = function(phone, leadId = null) {
    // Haptic feedback
    this.haptic(10);
    
    // If leadId provided, get lead data to check for multiple phones
    if (leadId) {
        const lead = this.state.leads.find(l => l.didar_contact_id === leadId || l.id === leadId);
        if (lead) {
            this.showPhoneSelector(lead);
            return;
        }
    }
    
    // Single phone, make call directly
    if (phone) {
        this.makePhoneCall(phone);
    }
};

/**
 * Show phone number selector for leads with multiple numbers
 */
app.showPhoneSelector = function(lead) {
    const phones = [];
    
    // Collect all available phone numbers
    if (lead.mobile_phone) phones.push({ number: lead.mobile_phone, label: 'شماره اصلی', icon: 'fa-mobile-alt' });
    if (lead.secondary_mobile_phone) phones.push({ number: lead.secondary_mobile_phone, label: 'شماره دوم', icon: 'fa-mobile-alt' });
    if (lead.mobile_phone_3) phones.push({ number: lead.mobile_phone_3, label: 'شماره سوم', icon: 'fa-mobile-alt' });
    if (lead.mobile_phone_4) phones.push({ number: lead.mobile_phone_4, label: 'شماره چهارم', icon: 'fa-mobile-alt' });
    if (lead.work_phone) phones.push({ number: lead.work_phone, label: 'شماره کار', icon: 'fa-building' });
    
    // If only one phone, call directly
    if (phones.length === 1) {
        this.makePhoneCall(phones[0].number);
        return;
    }
    
    // Show phone selection modal
    const name = `${lead.first_name || ''} ${lead.last_name || ''}`;
    const self = this; // Store reference to app
    
    Swal.fire({
        title: 'انتخاب شماره تماس',
        html: `
            <div class="text-right mb-4">
                <p class="text-sm text-gray-400 mb-2">تماس با:</p>
                <p class="text-lg font-bold text-white">${name}</p>
            </div>
            <div class="space-y-2">
                ${phones.map((phone, index) => `
                    <button data-phone="${phone.number}"
                            class="phone-select-btn w-full bg-gray-800 hover:bg-gray-700 border border-gray-700 
                                   text-white py-4 px-4 rounded-lg transition flex items-center gap-3
                                   touch-input haptic-button">
                        <i class="fa-solid ${phone.icon} text-yellow-500 text-xl"></i>
                        <div class="text-right flex-1">
                            <div class="font-bold text-sm">${phone.label}</div>
                            <div class="text-xs text-gray-400 dir-ltr text-left">${phone.number}</div>
                        </div>
                        <i class="fa-solid fa-phone text-green-500"></i>
                    </button>
                `).join('')}
            </div>
            <div class="mt-4 pt-4 border-t border-gray-700">
                <button onclick="Swal.close()" 
                        class="w-full bg-gray-700 hover:bg-gray-600 text-white py-3 rounded-lg transition">
                    انصراف
                </button>
            </div>
        `,
        showConfirmButton: false,
        customClass: {
            popup: 'glass',
            htmlContainer: 'text-right'
        },
        didOpen: () => {
            // Add click handlers to phone buttons
            document.querySelectorAll('.phone-select-btn').forEach(btn => {
                btn.addEventListener('click', function() {
                    const phoneNumber = this.getAttribute('data-phone');
                    // Close modal first, then make call
                    Swal.close();
                    setTimeout(() => {
                        self.makePhoneCall(phoneNumber);
                    }, 300);
                });
                
                // Add haptic feedback
                btn.addEventListener('click', () => {
                    self.haptic(10);
                });
            });
        }
    });
};

/**
 * Make actual phone call
 */
app.makePhoneCall = function(phoneNumber) {
    if (!phoneNumber) {
        Swal.fire('خطا', 'شماره تماسی وجود ندارد', 'error');
        return;
    }
    
    // Clean phone number (remove spaces, dashes, etc.)
    const cleanNumber = phoneNumber.replace(/[^\d+]/g, '');
    
    // Show calling confirmation
    Swal.fire({
        title: 'تماس تلفنی',
        html: `
            <div class="text-center py-4">
                <i class="fa-solid fa-phone text-green-500 text-4xl mb-4 animate-pulse"></i>
                <p class="text-lg font-bold text-white dir-ltr">${cleanNumber}</p>
                <p class="text-sm text-gray-400 mt-2">در حال برقراری تماس...</p>
            </div>
        `,
        showCancelButton: true,
        confirmButtonText: 'تماس',
        cancelButtonText: 'انصراف',
        confirmButtonColor: '#22c55e',
        cancelButtonColor: '#6b7280',
        reverseButtons: true,
        customClass: {
            popup: 'glass'
        }
    }).then((result) => {
        if (result.isConfirmed) {
            // Create tel: link and trigger it
            const telLink = document.createElement('a');
            telLink.href = `tel:${cleanNumber}`;
            document.body.appendChild(telLink);
            telLink.click();
            document.body.removeChild(telLink);
            
            // Log the call
            console.log(`[CALL] Initiating call to: ${cleanNumber}`);
            
            // Haptic feedback
            this.haptic([10, 50, 10]);
        }
    });
};

// Initialize advanced features on DOM ready
if (typeof document !== 'undefined') {
    document.addEventListener('DOMContentLoaded', () => {
        setTimeout(() => {
            initAdvancedMobileFeatures();
            app.addHapticToButtons();
        }, 1000);
    });
}
