// script.js - Neshan Mapbox GL Version

const BRANDS = {
    dailymarket: { name: "دیلی‌مارکت",  icon: "assets/img/dailymarket.png", color: "#2E7D32",  defaultChecked: true  },
    okcs:        { name: "افق کوروش",   icon: "assets/img/okcs.png",        color: "#C62828",  defaultChecked: true  },
    canbo:       { name: "جانبو",       icon: "assets/img/canbo.png",       color: "#000000",  defaultChecked: false },
    seven:       { name: "هفت",         icon: "assets/img/seven.png",       color: "#0D47A1",  defaultChecked: false },
    minoo:       { name: "مینو مارت",   icon: "assets/img/minoo.png",       color: "#FF9800",  defaultChecked: false },
    amiran:      { name: "امیران",      icon: "assets/img/amiran.png",      color: "#B7D82B",  defaultChecked: false },
    beheshti:    { name: "بهشتی",       icon: "assets/img/beheshti.png",    color: "#FF5900",  defaultChecked: false },
    gandom:      { name: "گندم",        icon: "assets/img/gandoom.png",     color: "#70247B",  defaultChecked: false },
};

const $ = (selector) => document.querySelector(selector);
const $$ = (selector) => [...document.querySelectorAll(selector)];
const parseNum = (str) => parseFloat((str || "0").toString().replace(/,/g, ''));
const formatNum = (n) => n ? Number(parseNum(n)).toLocaleString() : "0";

const App = {
    map: null,
    _mapLoaded: false,
    _activeMarkers: [],
    _activeTooltips: [],
    _activePopup: null,
    _clusterIndexes: {},      
    _updateTimer: null,
    _needsRenderAfterLoad: false,
        opportunityManager: null, 


    data: {
        daily: {},
        haft: {},
        allStores: [],
        allItems: [],
        filtered: []
    },

        state: {
        isClustering: true,
        showTooltips: false,    
        radius: 250,
        shouldFitBounds: false,
        filters: {
            brand: 'all',
            state: '',
            county: '',
            supervisor: '',
            traffic_zone: '',
            oddeven_zone: '',
            franchise: '',
            sales_type: 'all',
            // --- کدهای اضافه شده برای فیلتر فروش ---
            sales_min: 0,
            sales_max: 600 
            // --------------------------------------
        }
    },


    ui: {
        loading: null,
        progressBar: null,
        progressText: null,
        loadingText: null
    },

    access: {
        role: 'admin',
        area_supervisor_id: null,
        allowedStates: [],
        user_name: ''
    },

    // ===================== INIT =====================

    async init() {
        this.ui.loading = $('#loading-overlay');
        this.ui.progressBar = $('#progress-bar');
        this.ui.progressText = $('#progress-percent');
        this.ui.loadingText = $('#loading-text');

        this.loadAccessConfig();
        this.initMap();
        this.setupEventListeners();
        try {
            await this.loadAllData();
        } catch (e) {
            console.error(e);
            $('#loading-text').textContent = "خطا در اتصال به سرور";
        }
    },

loadAccessConfig() {
    const ua = window.USER_ACCESS || {};
    this.access.role = ua.role || 'admin';
    this.access.area_supervisor_id = ua.area_supervisor_id || null;
    this.access.area_supervisor_ids = ua.area_supervisor_ids || null;
    this.access.user_name = ua.user_name || '';
    console.log('[Access] Role:', this.access.role, '| Area Supervisor ID:', this.access.area_supervisor_id, '| Area Supervisor IDs:', this.access.area_supervisor_ids);
},


    isAdmin()          { return this.access.role === 'admin'; },
    isAreaSupervisor() { return this.access.role === 'area_supervisor' && this.access.area_supervisor_id; },
    isRegionalManager() { return this.access.role === 'regional_manager' && this.access.area_supervisor_ids; },


    // ===================== MAP =====================

    initMap() {
        const config = window.MAP_CONFIG || {};

        this.map = new nmp_mapboxgl.Map({
            mapType: nmp_mapboxgl.Map.mapTypes.neshanVector,
            container: "map",
            zoom: 5,
            pitch: 0,
            center: [51.389, 35.6892],
            minZoom: 2,
            maxZoom: 21,
            trackResize: true,
            mapKey: config.apiKey,
            poi: false,      
            traffic: false, 
            mapTypeControllerOptions: {
                show: true,
                position: 'bottom-right'
            }
        });

        this.map.dragRotate.disable();
        this.map.touchZoomRotate.disableRotation();
        this.map.addControl(new nmp_mapboxgl.NavigationControl(), 'top-left');

        this.map.on('load', () => {
            console.log('✅ نقشه با تمام ویژگی‌ها لود شد');
            this._mapLoaded = true;

            this.opportunityManager = new OpportunityLayer(this.map);

            this._initCircleLayer();

            if (this._needsRenderAfterLoad) {
                console.log('[Map] Executing deferred render after map load');
                this._needsRenderAfterLoad = false;
                
                this.ui.loading.style.display = 'none';
                this.render();
                
                if (this.data.filtered.length > 0) {
                    this._fitBoundsToItems(this.data.filtered);
                }
            }
        });

        this.map.on('moveend', () => {
            this._debouncedUpdate();
            this.updateVisibleStats();
        });
    },

    _initCircleLayer() {
        this.map.addSource('radius-circles', {
            type: 'geojson',
            data: { type: 'FeatureCollection', features: [] }
        });

        this.map.addLayer({
            id: 'radius-circles-layer',
            type: 'circle',
            source: 'radius-circles',
            paint: {
                'circle-radius': this._calcCircleRadiusStops(this.state.radius),
                'circle-color': ['get', 'color'],
                'circle-opacity': 0.1,
                'circle-stroke-color': ['get', 'color'],
                'circle-stroke-opacity': 0.4,
                'circle-stroke-width': 1
            }
        });
    },

    _calcCircleRadiusStops(radiusMeters) {
        const lat = 35.6892;
        const cosLat = Math.cos(lat * Math.PI / 180);
        const stops = [];
        for (let z = 0; z <= 22; z++) {
            const metersPerPixel = (156543.03392 * cosLat) / Math.pow(2, z);
            const px = Math.max(0.5, radiusMeters / metersPerPixel);
            stops.push(z, px);
        }
        return ['interpolate', ['linear'], ['zoom'], ...stops];
    },

    // ===================== DATA LOADING =====================

    // ✅ جایگزین تابع loadAllData فعلی شود
    async loadAllData() {
        const config = window.MAP_CONFIG;

        this.updateProgress(5, "در حال دریافت داده‌های دیلی...");
        const dailyText = await this.fetchWithProgress(config.dailyCsv, (pct) => this.updateProgress(5 + pct * 10));
        this.parseDailyData(dailyText);

        this.updateProgress(15, "در حال دریافت داده‌های هفت...");
        const haftText = await this.fetchWithProgress(config.haftCsv, (pct) => this.updateProgress(15 + pct * 10));
        this.parseHaftData(haftText);

        this.updateProgress(25, "در حال دریافت مختصات فروشگاه‌ها...");
        const totalText = await this.fetchWithProgress(config.totalCsv, (pct) => this.updateProgress(25 + pct * 55));

        this.updateProgress(85, "در حال پردازش نقاط...");
        await new Promise(r => setTimeout(r, 100));

        this.processStores(totalText);
        this.applyAccessFilter();
        this.buildUIControls();

        this.updateProgress(100, "تکمیل شد.");

        if (this._mapLoaded) {
            setTimeout(() => {
                this.ui.loading.style.display = 'none';
                this.render();
                this.fitToAll();
            }, 300);
        } else {
            console.log('[Data] Data ready, waiting for map...');
            this._needsRenderAfterLoad = true;
        }
    },

    async fetchWithProgress(url, onProgress) {
        const response = await fetch(url);
        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

        const contentLength = response.headers.get('content-length');
        if (!contentLength) {
            const text = await response.text();
            onProgress(1);
            return text;
        }

        const total = parseInt(contentLength, 10);
        let loaded = 0;
        const reader = response.body.getReader();
        const chunks = [];

        while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            chunks.push(value);
            loaded += value.length;
            onProgress(loaded / total);
        }
        return await new Blob(chunks).text();
    },

    updateProgress(percent, text) {
        const p = Math.min(100, Math.max(0, percent));
        this.ui.progressBar.style.width = `${p}%`;
        this.ui.progressText.textContent = `${Math.round(p)}%`;
        if (text) this.ui.loadingText.textContent = text;
    },

    parseDailyData(csvText) {
        Papa.parse(csvText, {
            header: true, skipEmptyLines: true,
            complete: (results) => {
                (results.data || []).forEach(row => {
                    if (row.store_code) this.data.daily[row.store_code.trim()] = row;
                });
                console.log('[Daily] Loaded:', Object.keys(this.data.daily).length);
            }
        });
    },

    parseHaftData(csvText) {
        Papa.parse(csvText, {
            header: true, skipEmptyLines: true,
            complete: (results) => {
                (results.data || []).forEach(row => {
                    if (row.store_code) this.data.haft[row.store_code.trim()] = row;
                });
                console.log('[Haft] Loaded:', Object.keys(this.data.haft).length);
            }
        });
    },

    processStores(csvText) {
        Papa.parse(csvText, {
            header: true, skipEmptyLines: true,
            complete: ({ data }) => {
                this.data.allStores = data || [];
                this.data.allItems = [];

                this.data.allStores.forEach(row => {
                    const lat = parseFloat(row.lat);
                    const lng = parseFloat(row.lng);
                    if (isNaN(lat) || isNaN(lng)) return;

                    const brandKey = row.brand || 'unknown';
                    const storeCode = row.store_code ? row.store_code.trim() : 'unknown';
                    const dailyInfo = (brandKey === 'dailymarket') ? this.data.daily[storeCode] : null;
                    const haftInfo  = (brandKey === 'seven')       ? this.data.haft[storeCode]  : null;

                    this.data.allItems.push({
                        lat, lng,
                        storeData: {
                            ...row,
                            brand: brandKey,
                            store_code: storeCode,
                            dailyInfo,
                            haftInfo,
                            supervisor_name: dailyInfo ? dailyInfo.supervisor_name : "",
                            area_supervisor_name: dailyInfo ? dailyInfo.area_supervisor_name : "",
                            area_supervisor_id: dailyInfo ? dailyInfo.area_supervisor_id : "",
                            lat, lng
                        }
                    });
                });

                this.data.filtered = [...this.data.allItems];
                console.log('[Stores] Total items:', this.data.allItems.length);
            }
        });
    },

    // ===================== ACCESS FILTER =====================

    applyAccessFilter() {
    if (this.isAdmin()) {
        console.log('[Access] Admin mode - no filtering');
        return;
    }

    // Regional Manager
    if (this.isRegionalManager()) {
        const supIds = this.access.area_supervisor_ids;
        console.log('[Access] Filtering for regional_manager with area_supervisor_ids:', supIds);

        const myDaily = this.data.allItems.filter(item =>
            item.storeData.brand === 'dailymarket' && supIds.includes(item.storeData.area_supervisor_id)
        );

        this.access.allowedStates = [...new Set(myDaily.map(i => i.storeData.state))].filter(Boolean);
        console.log('[Access] Allowed states:', this.access.allowedStates);

        this.data.allItems = this.data.allItems.filter(item => {
            const d = item.storeData;
            if (d.brand === 'dailymarket') return supIds.includes(d.area_supervisor_id);
            return this.access.allowedStates.includes(d.state);
        });

        this.data.allStores = this.data.allStores.filter(row => {
            if (row.brand === 'dailymarket') {
                const daily = this.data.daily[row.store_code?.trim()];
                return daily && supIds.includes(daily.area_supervisor_id);
            }
            return this.access.allowedStates.includes(row.state);
        });

        this.data.filtered = [...this.data.allItems];
        console.log(`[Access] After filter: ${this.data.allItems.length} items`);return;
    }

    // Area Supervisor
    if (!this.isAreaSupervisor()) return;

    const supId = this.access.area_supervisor_id;
    console.log('[Access] Filtering for area_supervisor_id:', supId);

    const myDaily = this.data.allItems.filter(item =>
        item.storeData.brand === 'dailymarket' && item.storeData.area_supervisor_id === supId
    );

    this.access.allowedStates = [...new Set(myDaily.map(i => i.storeData.state))].filter(Boolean);
    console.log('[Access] Allowed states:', this.access.allowedStates);

    this.data.allItems = this.data.allItems.filter(item => {
        const d = item.storeData;
        if (d.brand === 'dailymarket') return d.area_supervisor_id === supId;
        return this.access.allowedStates.includes(d.state);
    });

    this.data.allStores = this.data.allStores.filter(row => {
        if (row.brand === 'dailymarket') {
            const daily = this.data.daily[row.store_code?.trim()];
            return daily && daily.area_supervisor_id === supId;
        }
        return this.access.allowedStates.includes(row.state);
    });

    this.data.filtered = [...this.data.allItems];

    if (myDaily.length > 0) {
        const firstName = myDaily[0].storeData.area_supervisor_name;
        if (firstName) {
            this.access.user_name = firstName;const el = $('#user-display-name');
            if (el) el.textContent = firstName;
        }
    }

    console.log(`[Access] After filter: ${this.data.allItems.length} items`);
},


    // ===================== UI CONTROLS =====================

        buildUIControls() {
        const brandBox = $('#brand-list');
        brandBox.innerHTML = this.createBrandCheckboxHtml('all', 'همه برندها', true) +
            Object.keys(BRANDS).map(b => this.createBrandCheckboxHtml(b, BRANDS[b].name)).join('');

        let states;
        if (this.isAreaSupervisor()) {
            states = [...this.access.allowedStates].sort();
        } else {
            states = [...new Set(this.data.allStores.map(r => r.state))].filter(Boolean).sort();
        }
        this.setupDropdown('state', states);
        this.updateSupervisorOptions();

        $('#radius-slider').addEventListener('input', (e) => {
            const val = parseInt(e.target.value);
            this.state.radius = val;
            $('#radius-value').textContent = val >= 1000 ? (val / 1000) + ' کیلومتر' : val + ' متر';
            this._updateCircleRadius();
        });

        // --- کدهای اضافه شده برای اسلایدر میانگین فروش ---
        const minSalesSlider = $('#sales-min-slider');
        const maxSalesSlider = $('#sales-max-slider');
        const minSalesVal = $('#sales-min-val');
        const maxSalesVal = $('#sales-max-val');

        if (minSalesSlider && maxSalesSlider) {
            const updateSalesSliders = () => {
                let minVal = parseInt(minSalesSlider.value);
                let maxVal = parseInt(maxSalesSlider.value);

                // جلوگیری از عبور حداقل از حداکثر
                if (minVal > maxVal) {
                    let tmp = minVal;
                    minVal = maxVal;
                    maxVal = tmp;
                    minSalesSlider.value = minVal;
                    maxSalesSlider.value = maxVal;
                }

                minSalesVal.textContent = minVal === 0 ? "۰" : minVal + " میلیون";
                maxSalesVal.textContent = maxVal + " میلیون";

                this.state.filters.sales_min = minVal;
                this.state.filters.sales_max = maxVal;

                this.state.shouldFitBounds = false;
                this.applyFilters();
            };

            minSalesSlider.addEventListener('input', updateSalesSliders);
            maxSalesSlider.addEventListener('input', updateSalesSliders);
        }
        // ---------------------------------------------------

        this._initTooltipToggle();

        this.state.shouldFitBounds = true;
        this.applyFilters();
    },


    _initTooltipToggle() {
        const btn = $('#toggle-tooltip-btn');
        if (!btn) return;

        this.state.showTooltips = false;
        btn.textContent = "نمایش آمار فروش";
        btn.className = 'control-btn warning';

        btn.addEventListener('click', () => {
            this.state.showTooltips = !this.state.showTooltips;

            if (this.state.showTooltips) {
                btn.textContent = "مخفی کردن آمار فروش";
                btn.className = 'control-btn success';
            } else {
                btn.textContent = "نمایش آمار فروش";
                btn.className = 'control-btn warning';
            }

            this._refreshTooltips();
        });
    },

    _refreshTooltips() {
        this._activeTooltips.forEach(m => m.remove());
        this._activeTooltips = [];

        if (!this.state.showTooltips) return;

        if (!this._mapLoaded) return;

        const bounds = this.map.getBounds();
        const zoom = Math.floor(this.map.getZoom());

        if (this.state.isClustering) {
            Object.keys(this._clusterIndexes).forEach(brandKey => {
                const idx = this._clusterIndexes[brandKey];
                if (!idx) return;

                const bbox = [
                    bounds.getWest(), bounds.getSouth(),
                    bounds.getEast(), bounds.getNorth()
                ];
                const clusters = idx.getClusters(bbox, zoom);

                clusters.forEach(feature => {
                    if (!feature.properties.cluster) {
                        const itemIndex = feature.properties.index;
                        const item = this.data.filtered[itemIndex];
                        if (item) {
                            const info = item.storeData.dailyInfo || item.storeData.haftInfo;
                            if (info) this._addTooltip(item, info);
                        }
                    }
                });
            });
        } else {
            this.data.filtered.forEach(item => {
                if (bounds.contains([item.lng, item.lat])) {
                    const info = item.storeData.dailyInfo || item.storeData.haftInfo;
                    if (info) this._addTooltip(item, info);
                }
            });
        }
    },

    updateSupervisorOptions() {
        const { state, county } = this.state.filters;

        const available = this.data.allItems.filter(item => {
            const d = item.storeData;
            if (d.brand !== 'dailymarket') return false;
            if (state && d.state !== state) return false;
            if (county && d.county !== county) return false;
            return true;
        });

        const supervisors = [...new Set(available.map(i => i.storeData.supervisor_name))].filter(Boolean).sort();

        if (this.state.filters.supervisor && !supervisors.includes(this.state.filters.supervisor)) {
            this.state.filters.supervisor = '';
            $('#supervisor-search').value = '';
        }

        this.setupDropdown('supervisor', supervisors);
    },

    createBrandCheckboxHtml(key, label, isAll = false) {
        let isChecked;
        if (isAll) {
            isChecked = Object.keys(BRANDS).every(b => BRANDS[b].defaultChecked);
        } else {
            isChecked = BRANDS[key]?.defaultChecked !== false;
        }

        return `
            <div class="brand-checkbox">
                <input type="checkbox" id="brand-${key}" ${isAll ? 'data-all="true"' : ''} ${isChecked ? 'checked' : ''}>
                <label for="brand-${key}">
                    ${label}
                    ${!isAll && BRANDS[key] ? `<img src="${BRANDS[key].icon}">` : ''}
                </label>
            </div>`;
    },

    setupDropdown(name, options) {
        const input = $(`#${name}-search`);
        const dropdown = $(`#${name}-dropdown`);

        const renderOpts = (list) => {
            dropdown.innerHTML = `<div class="select-option selected" data-value="">همه</div>` +
                list.map(o => `<div class="select-option" data-value="${o}">${o}</div>`).join('');
        };
        renderOpts(options);

        const newInput = input.cloneNode(true);
        input.parentNode.replaceChild(newInput, input);
        const activeInput = $(`#${name}-search`);

        activeInput.addEventListener('focus', () => {
            $$('.select-dropdown').forEach(d => d.style.display = 'none');
            dropdown.style.display = 'block';
        });

        activeInput.addEventListener('input', (e) => {
            const val = e.target.value.toLowerCase();
            const filtered = options.filter(o => o.toLowerCase().includes(val));
            renderOpts(filtered);
            dropdown.style.display = 'block';
        });

        dropdown.onclick = (e) => {
            const opt = e.target.closest('.select-option');
            if (!opt) return;

            const val = opt.dataset.value;
            activeInput.value = val ? opt.textContent.trim() : '';
            this.state.filters[name] = val;
            dropdown.style.display = 'none';

            if (name === 'state') this.handleStateChange(val);
            if (name === 'county') this.updateSupervisorOptions();

            if (['state', 'county', 'supervisor'].includes(name)) {
                this.state.shouldFitBounds = true;
            }

            this.applyFilters();
        };

        document.addEventListener('click', (e) => {
            if (!e.target.closest('.custom-select')) dropdown.style.display = 'none';
        });
    },

    handleStateChange(state) {
        this.state.filters.county = '';

        if (state) {
            const counties = [...new Set(
                this.data.allStores.filter(r => r.state === state).map(r => r.county)
            )].filter(Boolean).sort();
            this.setupDropdown('county', counties);
            const ci = $('#county-search');
            ci.disabled = false;
            ci.value = '';
            ci.placeholder = "انتخاب شهرستان...";
        } else {
            const ci = $('#county-search');
            ci.disabled = true;
            ci.value = '';
            ci.placeholder = "ابتدا استان را انتخاب کنید";
            $('#county-dropdown').innerHTML = '';
        }

        this.updateSupervisorOptions();
    },

    // ===================== EVENT LISTENERS =====================

    setupEventListeners() {
        $('#mobile-menu-btn').addEventListener('click', () => $('#sidebar').classList.add('open'));
        $('#close-sidebar').addEventListener('click', () => $('#sidebar').classList.remove('open'));

        $('#brand-list').addEventListener('change', (e) => {
            if (e.target.tagName !== 'INPUT') return;
            const id = e.target.id.replace('brand-', '');
            if (id === 'all') {
                $$('#brand-list input:not([id="brand-all"])').forEach(cb => cb.checked = e.target.checked);
            } else {
                const allChecked = $$('#brand-list input:not([id="brand-all"])').every(cb => cb.checked);
                $('#brand-all').checked = allChecked;
            }
            this.state.shouldFitBounds = false;
            this.applyFilters();
        });

        $$('input[type=radio]').forEach(r => {
            r.addEventListener('change', () => {
                this.state.filters[r.name] = r.value;
                this.state.shouldFitBounds = false;
                this.applyFilters();
            });
        });

        $('#toggle-cluster-btn').addEventListener('click', (e) => {
            this.state.isClustering = !this.state.isClustering;
            e.target.textContent = this.state.isClustering ? "غیرفعال کردن کلاسترینگ" : "فعال کردن کلاسترینگ";
            e.target.className = `control-btn ${this.state.isClustering ? 'success' : 'warning'}`;
            this.render();
        });

        $('#reset-filters-btn').addEventListener('click', () => this.resetFilters());
        $('#fit-bounds-btn').addEventListener('click', () => this.fitToAll());
         
    },

    // ===================== FILTERING =====================

        applyFilters() {
        const f = this.state.filters;
        const allBrandCb = $('#brand-all');
        const checkedBrands = $$('#brand-list input:not([id="brand-all"]):checked').map(cb => cb.id.replace('brand-', ''));
        const brandMode = allBrandCb && allBrandCb.checked ? 'all' : checkedBrands;

        this.data.filtered = this.data.allItems.filter(item => {
            const d = item.storeData;

            if (brandMode !== 'all' && !brandMode.includes(d.brand)) return false;
            if (f.state && d.state !== f.state) return false;
            if (f.county && d.county !== f.county) return false;
            if (f.traffic_zone && d.in_traffic_zone !== f.traffic_zone) return false;
            if (f.oddeven_zone && d.in_odd_even_zone !== f.oddeven_zone) return false;
            if (f.franchise && d.is_franchise !== f.franchise) return false;

            if (f.supervisor) {
                if (d.brand === 'dailymarket' && d.supervisor_name !== f.supervisor) return false;
            }

            if (f.sales_type !== 'all' && d.brand === 'dailymarket') {
                const online = parseNum(d.dailyInfo?.online_sales_amount);
                if (f.sales_type === 'online' && online <= 1) return false;
                if (f.sales_type === 'offline' && online > 1) return false;
            }

            if (d.brand === 'dailymarket' || d.brand === 'seven') {
                const info = d.dailyInfo || d.haftInfo;
                if (info) {
                    const avgSalesStr = info.avg_daily_sales || "0";
                    const avgSalesNum = parseNum(avgSalesStr);
                    const avgSalesInMillionToman = avgSalesNum / 10000000; 

                    if (avgSalesInMillionToman < f.sales_min || avgSalesInMillionToman > f.sales_max) {
                        return false;
                    }
                } else {
                    if (f.sales_min > 0 || f.sales_max < 600) return false;
                }
            }
            // ---------------------------------------------------

            return true;
        });

        this.render();

        if (this.state.shouldFitBounds && this.data.filtered.length > 0) {
            let boundsItems = this.data.filtered;
            if (f.supervisor) {
                const supItems = this.data.filtered.filter(i => i.storeData.supervisor_name === f.supervisor);
                if (supItems.length > 0) boundsItems = supItems;
            }
            this._fitBoundsToItems(boundsItems);
        }

        this.state.shouldFitBounds = false;
    },


    _fitBoundsToItems(items) {
        if (items.length === 0) return;

        const bounds = new nmp_mapboxgl.LngLatBounds();
        items.forEach(i => bounds.extend([i.lng, i.lat]));

        const maxZoom = items.length > 1000 ? 10 : 15;
        this.map.fitBounds(bounds, {
            padding: 50,
            maxZoom: maxZoom,
            duration: 1000
        });
    },

    // ===================== RENDERING =====================

    render() {
        if (!this._mapLoaded) {
            console.log('[Render] Map not loaded yet, deferring...');
            this._needsRenderAfterLoad = true;
            return;
        }

        this._clearAllMarkers();
        this._updateCircleSource();

        if (this.data.filtered.length === 0) {
            this.updateVisibleStats();
            return;
        }

        if (this.state.isClustering) {
            this._buildClusterIndexes();
        }

        this._renderVisibleItems();
        this.updateVisibleStats();
    },

    _debouncedUpdate() {
        clearTimeout(this._updateTimer);
        this._updateTimer = setTimeout(() => {
            if (!this._mapLoaded) return;

            this._clearAllMarkers();
            this._renderVisibleItems();
            this.updateVisibleStats();
        }, 150);
    },

    _clearAllMarkers() {
        this._activeMarkers.forEach(m => m.remove());
        this._activeMarkers = [];
        this._activeTooltips.forEach(m => m.remove());
        this._activeTooltips = [];
        if (this._activePopup) {
            this._activePopup.remove();
            this._activePopup = null;
        }
    },

    _buildClusterIndexes() {
        this._clusterIndexes = {};

        const brandGroups = {};
        this.data.filtered.forEach((item, idx) => {
            const brand = item.storeData.brand;
            if (!brandGroups[brand]) brandGroups[brand] = [];
            brandGroups[brand].push({ item, idx });
        });

        Object.keys(brandGroups).forEach(brandKey => {
            const points = brandGroups[brandKey].map(({ item, idx }) => ({
                type: 'Feature',
                geometry: { type: 'Point', coordinates: [item.lng, item.lat] },
                properties: { index: idx, brand: brandKey }
            }));

            const index = new Supercluster({
                radius: 24,    
                maxZoom: 16,    
                minZoom: 0
            });
            index.load(points);
            this._clusterIndexes[brandKey] = index;
        });

        console.log('[Cluster] Built separate indexes for brands:', Object.keys(this._clusterIndexes).join(', '));
    },

    _renderVisibleItems() {
        if (!this._mapLoaded) return;
        if (this.data.filtered.length === 0) return;

        const bounds = this.map.getBounds();
        const zoom = Math.floor(this.map.getZoom());

        if (this.state.isClustering && Object.keys(this._clusterIndexes).length > 0) {
            const bbox = [
                bounds.getWest(), bounds.getSouth(),
                bounds.getEast(), bounds.getNorth()
            ];

            Object.keys(this._clusterIndexes).forEach(brandKey => {
                const index = this._clusterIndexes[brandKey];
                const clusters = index.getClusters(bbox, zoom);

                clusters.forEach(feature => {
                    const coords = feature.geometry.coordinates;
                    const props = feature.properties;

                    if (props.cluster) {
                        this._addClusterMarker(coords, props, brandKey, index);
                    } else {
                        const item = this.data.filtered[props.index];
                        if (item) this._addStoreMarker(item);
                    }
                });
            });
        } else {
            // حالت بدون کلاستر
            this.data.filtered.forEach(item => {
                if (bounds.contains([item.lng, item.lat])) {
                    this._addStoreMarker(item);
                }
            });
        }
    },

    _addClusterMarker(coords, props, brandKey, clusterIndex) {
        const count = props.point_count;
        const clusterId = props.cluster_id;

        const color = BRANDS[brandKey]?.color || '#666';

        const size = count < 10 ? 36 : count < 50 ? 44 : count < 100 ? 52 : 60;

        const el = document.createElement('div');
        el.className = 'cluster-marker';
        el.style.cssText = `
            width: ${size}px;
            height: ${size}px;
            border-radius: 50%;
            background: ${color};
            color: #fff;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            font-size: ${size < 44 ? 13 : 15}px;
            border: 3px solid rgba(255,255,255,0.8);
            box-shadow: 0 2px 8px rgba(0,0,0,0.3);
            cursor: pointer;
        `;

        el.textContent = count >= 1000 ? Math.round(count / 1000) + 'k' : count;

        el.addEventListener('mouseenter', () => {
            el.style.boxShadow = '0 4px 16px rgba(0,0,0,0.5)';
            el.style.borderWidth = '4px';
        });
        el.addEventListener('mouseleave', () => {
            el.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
            el.style.borderWidth = '3px';
        });

        el.addEventListener('click', () => {
            const expansionZoom = clusterIndex.getClusterExpansionZoom(clusterId);
            this.map.easeTo({
                center: coords,
                zoom: Math.min(expansionZoom, 18),
                duration: 500
            });
        });

        const marker = new nmp_mapboxgl.Marker({ element: el })
            .setLngLat(coords)
            .addTo(this.map);

        this._activeMarkers.push(marker);
    },

    _addStoreMarker(item) {
        const d = item.storeData;
        const brandKey = d.brand;
        const iconUrl = BRANDS[brandKey]?.icon || 'assets/img/marker-icon-2x-blue.png';

        const el = document.createElement('div');
        el.className = 'store-marker';
        el.style.cssText = `
            width: 32px;
            height: 32px;
            background-image: url('${iconUrl}');
            background-size: cover;
            background-repeat: no-repeat;
            cursor: pointer;
        `;

        const marker = new nmp_mapboxgl.Marker({ element: el, anchor: 'bottom' })
            .setLngLat([item.lng, item.lat])
            .addTo(this.map);

        el.addEventListener('click', (e) => {
            e.stopPropagation();
            this._showStorePopup(item);
        });

        this._activeMarkers.push(marker);

        if (this.state.showTooltips) {
            const info = d.dailyInfo || d.haftInfo;
            if (info) {
                this._addTooltip(item, info);
            }
        }
    },

    _addTooltip(item, info) {
        const d = item.storeData;
        const sales = (parseNum(info.avg_daily_sales) / 10000000).toFixed(1);
        const basketDivisor = d.dailyInfo ? 10 : 1;
        const basketVal = (parseNum(info.avg_basket_value) / basketDivisor).toLocaleString();

        const el = document.createElement('div');
        el.className = 'permanent-tooltip';
        el.innerHTML = `
            <div class="dt-tooltip">
                <span class="dt-row sales">${sales} <small>م.ت</small></span>
                <span class="dt-row trans">${parseNum(info.avg_daily_transactions)} <small>فاکتور</small></span>
                <span class="dt-row basket">${basketVal} <small>سبد</small></span>
            </div>
        `;
        el.style.pointerEvents = 'none';

        const tooltipMarker = new nmp_mapboxgl.Marker({
            element: el,
            anchor: 'bottom',
            offset: [0, -38]
        })
            .setLngLat([item.lng, item.lat])
            .addTo(this.map);

        this._activeTooltips.push(tooltipMarker);
    },

    _showStorePopup(item) {
        if (this._activePopup) {
            this._activePopup.remove();
        }

        const d = item.storeData;
        const brandKey = d.brand;
        const brandName = BRANDS[brandKey]?.name || brandKey;
        const storeCode = d.store_code || 'unknown';
        const neshanLink = `https://nshn.ir/?lat=${d.lat}&lng=${d.lng}`;
        const dailyInfo = d.dailyInfo;

        const html = `
            <div class="popup-content">
                <div class="popup-header">
                    ${BRANDS[brandKey] ? `<img src="${BRANDS[brandKey].icon}" width="24">` : ''}
                    <strong>${d.store_name || brandName}</strong>
                </div>
                <div class="popup-row"><span>کد:</span> <b>${storeCode}</b></div>
                ${d.state ? `<div class="popup-row"><span>استان:</span> ${d.state}</div>` : ''}
                ${d.county ? `<div class="popup-row"><span>شهرستان:</span> ${d.county}</div>` : ''}
                ${d.city ? `<div class="popup-row"><span>شهر:</span> ${d.city}</div>` : ''}
                ${d.formatted_address ? `<div class="popup-row"><span>آدرس:</span> ${d.formatted_address}</div>` : ''}
                ${dailyInfo ? `<div class="popup-row supervisor"><span>سرپرست:</span> ${dailyInfo.supervisor_name}</div>` : ''}
                ${dailyInfo ? `<div class="popup-row"><span>مدیر منطقه:</span> ${dailyInfo.area_supervisor_name}</div>` : ''}
                <div class="popup-nav-btn">
                    <a href="${neshanLink}" target="_blank" rel="noopener noreferrer" class="neshan-nav-link">
                        <span>مسیریابی تا فروشگاه</span>
                        <img src="assets/neshan/icon.png" alt="نشان" width="20" height="20">
                    </a>
                </div>
            </div>
        `;

        this._activePopup = new nmp_mapboxgl.Popup({
            offset: 25,
            maxWidth: '320px',
            className: 'store-popup'
        })
            .setLngLat([item.lng, item.lat])
            .setHTML(html)
            .addTo(this.map);

        this.showDailyDetails(dailyInfo);
    },

    // ===================== CIRCLES =====================

    _updateCircleSource() {
        if (!this._mapLoaded) return;
        const source = this.map.getSource('radius-circles');
        if (!source) return;

        if (this.data.filtered.length > 10000) {
            source.setData({ type: 'FeatureCollection', features: [] });
            return;
        }

        const features = this.data.filtered.map(item => ({
            type: 'Feature',
            geometry: { type: 'Point', coordinates: [item.lng, item.lat] },
            properties: { color: BRANDS[item.storeData.brand]?.color || '#333' }
        }));

        source.setData({ type: 'FeatureCollection', features });
    },

    _updateCircleRadius() {
        if (!this._mapLoaded) return;
        try {
            this.map.setPaintProperty(
                'radius-circles-layer',
                'circle-radius',
                this._calcCircleRadiusStops(this.state.radius)
            );
        } catch(e) {
            console.warn('Could not update circle radius:', e);
        }
    },

    // ===================== STATS =====================

    updateVisibleStats() {
        if (!this.map) return;

        const bounds = this.map.getBounds();
        const visible = this.data.filtered.filter(item =>
            bounds.contains([item.lng, item.lat])
        );

        const counts = {};
        visible.forEach(item => {
            const b = item.storeData.brand;
            counts[b] = (counts[b] || 0) + 1;
        });

        const sorted = Object.keys(counts).sort((a, b) => counts[b] - counts[a]);
        const total = visible.length;

        let html = sorted.map(k => {
            const pct = total > 0 ? ((counts[k] / total) * 100).toFixed(1) : 0;
            return `
                <div class="stat-row">
                    <div class="stat-name">
                        <img src="${BRANDS[k]?.icon}" width="18">
                        <span>${BRANDS[k]?.name}:</span>
                    </div>
                    <div class="stat-val">${counts[k]} <small>(${pct}%)</small></div>
                </div>
            `;
        }).join('');

        html += `<div class="stat-total">کل: ${total}</div>`;

        if (this.isAreaSupervisor() || this.isRegionalManager()) {
    html += `<div class="access-info">
        <small>📍 محدوده: ${this.access.allowedStates.join('، ')}</small>
    </div>`;
}

        $('#info-display').innerHTML = html || '<div style="text-align:center;color:#777">موردی یافت نشد</div>';
    },

    // ===================== DAILY DETAILS =====================

    showDailyDetails(info) {
        const box = $('#daily-details-box');
        if (!info) {
            box.style.display = 'none';
            return;
        }

        let tpl = document.getElementById('details-template').innerHTML;
        const keys = {
            store_name: info.store_name,
            store_code: info.store_code,
            supervisor_name: info.supervisor_name,
            opening_date: info.opening_date,
            total_sales: formatNum(info.total_sales_amount),
            offline_sales: formatNum(info.offline_sales_amount),
            online_sales: formatNum(info.online_sales_amount),
            avg_daily_sales: formatNum(info.avg_daily_sales),
            avg_daily_sales_offline: formatNum(info.avg_daily_sales_offline),
            avg_daily_sales_online: formatNum(info.avg_daily_sales_online),
            avg_daily_trans: formatNum(info.avg_daily_transactions),
            avg_daily_trans_offline: formatNum(info.avg_daily_transactions_offline),
            avg_daily_trans_online: formatNum(info.avg_daily_transactions_online),
            avg_basket: formatNum(info.avg_basket_value),
            avg_basket_offline: formatNum(info.avg_basket_value_offline),
            avg_basket_online: formatNum(info.avg_basket_value_online),
            rent_amount: formatNum(info.rent_amount),
            rent_pct: parseFloat(info.rent_to_sales_pct || 0).toFixed(2) + '%',
            rent_ratio: parseFloat(info.rent_to_avg_sales_ratio || 0).toFixed(2) + '%'
        };

        Object.keys(keys).forEach(k => {
            tpl = tpl.replace(new RegExp(`{{${k}}}`, 'g'), keys[k] || '-');
        });

        box.innerHTML = tpl;
        box.style.display = 'block';
    },

    // ===================== RESET =====================

        resetFilters() {
        $$('#brand-list input').forEach(c => {
            const brandKey = c.id.replace('brand-', '');
            if (brandKey === 'all') {
                c.checked = Object.keys(BRANDS).every(b => BRANDS[b].defaultChecked);
            } else if (BRANDS[brandKey]) {
                c.checked = BRANDS[brandKey].defaultChecked !== false;
            }
        });

        $$('.select-search').forEach(i => i.value = '');
        $('#county-search').disabled = true;
        $$('input[type=radio][value=""], input[type=radio][value="all"]').forEach(r => r.checked = true);

        $('#radius-slider').value = 250;
        this.state.radius = 250;
        $('#radius-value').textContent = '250 متر';
        this._updateCircleRadius();

        const minSalesSlider = $('#sales-min-slider');
        const maxSalesSlider = $('#sales-max-slider');
        if (minSalesSlider && maxSalesSlider) {
            minSalesSlider.value = 0;
            maxSalesSlider.value = 600;
            $('#sales-min-val').textContent = "۰";
            $('#sales-max-val').textContent = "600 میلیون";
        }
        this.state.filters.sales_min = 0;
        this.state.filters.sales_max = 600;
        
        // ------------------------------------------------------

        $('#daily-details-box').style.display = 'none';

        Object.keys(this.state.filters).forEach(k => {
            if (k === 'sales_min') this.state.filters[k] = 0;
            else if (k === 'sales_max') this.state.filters[k] = 600;
            else this.state.filters[k] = (k === 'brand' || k === 'sales_type') ? 'all' : '';
        });


        this.state.showTooltips = false;
        const tooltipBtn = $('#toggle-tooltip-btn');
        if (tooltipBtn) {
            tooltipBtn.textContent = "نمایش آمار فروش";
            tooltipBtn.className = 'control-btn warning';
        }

        this.updateSupervisorOptions();

        this.state.shouldFitBounds = true;
        this.applyFilters();
    },


    // ===================== FIT BOUNDS =====================

    fitToAll() {
        if (this.data.allItems.length) {
            this._fitBoundsToItems(this.data.allItems);
        }
    },

};

document.addEventListener("DOMContentLoaded", () => App.init());