function exportIntelApp() {
  return {
    tab: 'map',
    tabIndicatorTop: 0,
    tabIndicatorH: 40,
    busy: false,
    syncMsg: '',
    stats: {},
    regions: [],
    territories: [],
    country: '',
    region: '',
    entities: [],
    contacts: [],
    tenders: [],
    catererPresence: [],
    catererBrands: [],
    govSources: [],
    selected: null,
    drawerOpen: false,
    q: '',
    entityTypeFilter: '',
    mapTypesSelected: [],
    mapHalalMode: false,
    mapHalalMin: 55,
    halalTopMarkets: [],
    halalTopEntities: [],
    listFocusId: null,
    listFocusDetail: null,
    listFocusLoading: false,
    drawerLoading: false,
    filterHasEmail: '',
    filterFavoritesOnly: false,
    filterCrmStatus: '',
    selectedIds: [],
    crmPipeline: [],
    crmModalOpen: false,
    crmCreateDeals: true,
    crmPushBusy: false,
    resultCount: 0,
    _searchTimer: null,

    tabs: [
      { id: 'map', icon: '🗺️', label: 'Kaart', color: '#0ea5e9', key: '1' },
      { id: 'distributors', icon: '📦', label: 'Distributeurs', color: '#fbbf24', key: '2' },
      { id: 'customers', icon: '🍽️', label: 'Eindklanten', color: '#34d399', key: '3' },
      { id: 'caterers', icon: '🏢', label: 'Cateraars', color: '#a78bfa', key: '4' },
      { id: 'contacts', icon: '📇', label: 'Contacten', color: '#38bdf8', key: '5' },
      { id: 'tenders', icon: '📋', label: 'Tenders', color: '#c084fc', key: '6' },
      { id: 'gov', icon: '🏛️', label: 'Overheid', color: '#94a3b8', key: '7' },
      { id: 'pipeline', icon: '🔗', label: 'Pipeline', color: '#fb923c', key: '8' },
    ],

    mapTypeChips: [
      { v: 'distributor', l: 'Distributeur', icon: '📦' },
      { v: 'wholesaler', l: 'Groothandel', icon: '🏪' },
      { v: 'importer', l: 'Importeur', icon: '🚢' },
      { v: 'logistics', l: 'Logistiek', icon: '🚚' },
      { v: 'restaurant', l: 'Restaurant', icon: '🍽️' },
      { v: 'doner_shoarma', l: 'Döner', icon: '🥙' },
      { v: 'butcher', l: 'Slager', icon: '🥩' },
      { v: 'contract_caterer', l: 'Cateraar', icon: '🏢' },
      { v: 'foodservice', l: 'Foodservice', icon: '🍴' },
    ],

    async init() {
      var params = new URLSearchParams(location.search);
      var t = params.get('tab');
      if (t) this.tab = t;
      var self = this;
      document.addEventListener('export-intel:open-entity', function (ev) {
        if (ev.detail && ev.detail.id) self.openEntity(ev.detail.id);
      });
      await this.loadMeta();
      await this.refresh();
      this.$nextTick(() => this.updateTabIndicator());
      window.addEventListener('keydown', (e) => {
        if (e.target.matches('input, textarea, select')) return;
        var n = parseInt(e.key, 10);
        if (n >= 1 && n <= 8) {
          var btn = this.$refs.vtabsNav?.querySelector('[data-tab="' + this.tabs[n - 1].id + '"]');
          this.setTab(this.tabs[n - 1].id, btn);
        }
      });
    },

    async loadMeta() {
      try {
        this.regions = await fetch('/api/export-intel/regions').then((r) => r.json());
        this.territories = await fetch('/api/export-intel/territories').then((r) => r.json());
      } catch (e) {
        console.error(e);
      }
    },

    async refresh() {
      this.busy = true;
      try {
        var cp = this.country ? '?country=' + encodeURIComponent(this.country) : '';
        var rp = this.region ? (cp ? '&' : '?') + 'region=' + encodeURIComponent(this.region) : '';
        this.stats = await fetch('/api/export-intel/stats' + cp + rp).then((r) => r.json());
        await this.loadTabData();
        if (this.tab === 'map') this.loadMap();
      } catch (e) {
        console.error(e);
      }
      this.busy = false;
    },

    filterQs() {
      var parts = [];
      if (this.country) parts.push('country=' + encodeURIComponent(this.country));
      if (this.region) parts.push('region=' + encodeURIComponent(this.region));
      if (this.q && this.q.trim()) parts.push('q=' + encodeURIComponent(this.q.trim()));
      if (this.entityTypeFilter) parts.push('entity_type=' + encodeURIComponent(this.entityTypeFilter));
      if (this.filterHasEmail === 'yes') parts.push('has_email=true');
      if (this.filterHasEmail === 'no') parts.push('has_email=false');
      if (this.filterFavoritesOnly) parts.push('favorite_only=true');
      if (this.filterCrmStatus === 'linked') parts.push('crm_linked=true');
      if (this.filterCrmStatus === 'not_linked') parts.push('crm_linked=false');
      return parts.length ? '&' + parts.join('&') : '';
    },

    onSearchInput() {
      clearTimeout(this._searchTimer);
      this._searchTimer = setTimeout(() => this.applyMapFilters(), 350);
    },

    clearFilters() {
      this.q = '';
      this.entityTypeFilter = '';
      this.mapTypesSelected = [];
      this.mapHalalMode = false;
      this.filterHasEmail = '';
      this.filterFavoritesOnly = false;
      this.filterCrmStatus = '';
      this.selectedIds = [];
      this.applyMapFilters();
    },

    showEntityTypeFilter() {
      return ['distributors', 'customers', 'caterers'].indexOf(this.tab) >= 0;
    },

    showMapTypeFilter() {
      return this.tab === 'map';
    },

    isMapTypeActive(v) {
      return this.mapTypesSelected.indexOf(v) >= 0;
    },

    toggleMapType(v) {
      var i = this.mapTypesSelected.indexOf(v);
      if (i >= 0) this.mapTypesSelected.splice(i, 1);
      else this.mapTypesSelected.push(v);
      this.entityTypeFilter = '';
      this.applyMapFilters();
    },

    clearMapTypes() {
      this.mapTypesSelected = [];
      this.applyMapFilters();
    },

    mapTypesParam() {
      if (this.entityTypeFilter) return { entity_type: this.entityTypeFilter };
      if (this.mapTypesSelected.length) {
        return { entity_types: this.mapTypesSelected.join(',') };
      }
      return {};
    },

    applyMapFilters() {
      if (this.tab !== 'map') {
        this.loadTabData();
        return;
      }
      this.loadTabData().then(() => this.loadMap());
    },

    showEmailFilter() {
      return this.tab === 'contacts';
    },

    entityTypeOptions() {
      if (this.tab === 'distributors') {
        return [
          { v: '', l: 'Alle types' },
          { v: 'distributor', l: 'Distributeur' },
          { v: 'wholesaler', l: 'Groothandel' },
          { v: 'importer', l: 'Importeur' },
          { v: 'logistics', l: 'Logistiek' },
        ];
      }
      if (this.tab === 'customers') {
        return [
          { v: '', l: 'Alle types' },
          { v: 'restaurant', l: 'Restaurant' },
          { v: 'doner_shoarma', l: 'Döner / shoarma' },
          { v: 'butcher', l: 'Slager' },
          { v: 'foodservice', l: 'Foodservice' },
        ];
      }
      if (this.tab === 'caterers') {
        return [{ v: '', l: 'Alle types' }, { v: 'contract_caterer', l: 'Contract cateraar' }];
      }
      return [
        { v: '', l: 'Alle types' },
        { v: 'distributor', l: 'Distributeur' },
        { v: 'wholesaler', l: 'Groothandel' },
        { v: 'contract_caterer', l: 'Cateraar' },
        { v: 'restaurant', l: 'Restaurant' },
      ];
    },

    async loadTabData() {
      var base = '/api/export-intel';
      var fq = this.filterQs();
      if (this.tab === 'map') {
        var mapQs = this.filterQs();
        var mt = this.mapTypesParam();
        if (mt.entity_type) mapQs += '&entity_type=' + encodeURIComponent(mt.entity_type);
        else if (mt.entity_types) mapQs += '&entity_types=' + encodeURIComponent(mt.entity_types);
        var rm = await fetch(base + '/entities?limit=200' + mapQs).then((x) => x.json());
        this.entities = rm.items || [];
        this.resultCount = rm.total != null ? rm.total : this.entities.length;
      } else if (this.tab === 'distributors') {
        var types = 'distributor,wholesaler,importer,logistics';
        var et = this.entityTypeFilter ? '&entity_type=' + encodeURIComponent(this.entityTypeFilter) : '';
        var r = await fetch(base + '/entities?entity_types=' + types + '&limit=500' + fq.replace(/&?entity_type=[^&]*/g, '') + et).then((x) => x.json());
        this.entities = r.items || [];
        this.resultCount = r.total != null ? r.total : this.entities.length;
      } else if (this.tab === 'customers') {
        var types2 = 'restaurant,doner_shoarma,butcher,foodservice';
        var et2 = this.entityTypeFilter ? '&entity_type=' + encodeURIComponent(this.entityTypeFilter) : '';
        var r2 = await fetch(base + '/entities?entity_types=' + types2 + '&limit=500' + fq.replace(/&?entity_type=[^&]*/g, '') + et2).then((x) => x.json());
        this.entities = r2.items || [];
        this.resultCount = r2.total != null ? r2.total : this.entities.length;
      } else if (this.tab === 'caterers') {
        var et3 = this.entityTypeFilter || 'contract_caterer';
        var r3 = await fetch(base + '/entities?entity_type=' + et3 + '&limit=500' + fq.replace(/&?entity_type=[^&]*/g, '')).then((x) => x.json());
        this.entities = r3.items || [];
        this.resultCount = r3.total != null ? r3.total : this.entities.length;
        this.catererPresence = await fetch(base + '/caterers/presence' + (this.country ? '?country=' + this.country : '')).then((x) => x.json());
        this.catererBrands = await fetch(base + '/caterers/brands').then((x) => x.json());
      } else if (this.tab === 'contacts') {
        var r4 = await fetch(base + '/contacts?limit=500' + fq).then((x) => x.json());
        this.contacts = r4.items || [];
        this.resultCount = r4.total != null ? r4.total : this.contacts.length;
      } else if (this.tab === 'tenders') {
        var tenders = await fetch(base + '/tenders' + (this.country ? '?country=' + this.country : '')).then((x) => x.json());
        var items = tenders.items || [];
        if (this.q && this.q.trim()) {
          var ql = this.q.trim().toLowerCase();
          items = items.filter((t) => (t.title || '').toLowerCase().includes(ql));
        }
        this.tenders = items;
        this.resultCount = items.length;
      } else if (this.tab === 'gov') {
        var gov = await fetch(base + '/gov-sources' + (this.country ? '?country=' + this.country : '')).then((x) => x.json());
        if (this.q && this.q.trim()) {
          var ql2 = this.q.trim().toLowerCase();
          gov = gov.filter((g) => (g.name || '').toLowerCase().includes(ql2) || (g.category || '').toLowerCase().includes(ql2));
        }
        this.govSources = gov;
        this.resultCount = gov.length;
      } else if (this.tab === 'pipeline') {
        var pipeQs = this.filterQs();
        var pipe = await fetch(base + '/crm/pipeline?limit=200' + pipeQs).then((x) => x.json());
        this.crmPipeline = pipe.items || [];
        this.resultCount = this.crmPipeline.length;
      }
    },

    async loadMap() {
      this.clearListFocus();
      var url = '/api/export-intel/map/bundle';
      var qs = [];
      if (this.country) qs.push('country=' + encodeURIComponent(this.country));
      if (this.region) qs.push('region=' + encodeURIComponent(this.region));
      var mt = this.mapTypesParam();
      if (mt.entity_type) qs.push('entity_type=' + encodeURIComponent(mt.entity_type));
      else if (mt.entity_types) qs.push('entity_types=' + encodeURIComponent(mt.entity_types));
      if (this.q && this.q.trim()) qs.push('q=' + encodeURIComponent(this.q.trim()));
      if (this.mapHalalMode) qs.push('halal_min=' + encodeURIComponent(this.mapHalalMin));
      if (this.filterFavoritesOnly) qs.push('favorite_only=true');
      if (this.filterCrmStatus === 'linked') qs.push('crm_linked=true');
      if (this.filterCrmStatus === 'not_linked') qs.push('crm_linked=false');
      if (qs.length) url += '?' + qs.join('&');
      var bundle = await fetch(url).then((r) => r.json());
      this.halalTopMarkets = (bundle.meta && bundle.meta.top_markets) || [];
      this.halalTopEntities = (bundle.meta && bundle.meta.top_halal_entities) || [];
      if (window.ExportIntelMap) {
        if (!document.getElementById('ei-map')) return;
        var self = this;
        window.ExportIntelMap.init('ei-map');
        window.ExportIntelMap.setOnEntityClick(function (id) { self.openEntity(id); });
        window.ExportIntelMap.setHalalMode(this.mapHalalMode);
        window.ExportIntelMap.setFeatures(bundle.entities || { features: [] });
        if (this.country) {
          var t = this.territories.find((x) => x.country_iso2 === this.country);
          if (t && t.lat && t.lon) window.ExportIntelMap.flyTo(t.lat, t.lon, t.map_zoom || 6);
        }
      }
    },

    setTab(id, el) {
      this.tab = id;
      this.entityTypeFilter = '';
      this.mapTypesSelected = [];
      this.mapHalalMode = false;
      this.filterHasEmail = '';
      var u = new URL(location.href);
      u.searchParams.set('tab', id);
      history.replaceState(null, '', u.pathname + u.search);
      this.loadTabData();
      if (id === 'map') this.$nextTick(() => this.loadMap());
      this.$nextTick(() => this.updateTabIndicator(el));
    },

    updateTabIndicator(el) {
      var nav = this.$refs.vtabsNav;
      if (!nav) return;
      var btn = el || nav.querySelector('.vtab-btn.active');
      if (!btn) return;
      var navRect = nav.getBoundingClientRect();
      var btnRect = btn.getBoundingClientRect();
      this.tabIndicatorTop = btnRect.top - navRect.top + nav.scrollTop;
      this.tabIndicatorH = btnRect.height;
    },

    async onCountryChange() {
      await this.refresh();
    },

    contactPreview(e) {
      if (!e) return '—';
      if (e.email || e.primary_email) return e.email || e.primary_email;
      if (e.phone || e.primary_phone) return e.phone || e.primary_phone;
      if (e.website) return e.website.replace(/^https?:\/\//, '').slice(0, 28);
      if (e.has_contact) return 'Contact in registry';
      return 'Geen contact';
    },

    async fetchEntityDetail(id) {
      var r = await fetch('/api/export-intel/entities/' + id);
      if (!r.ok) {
        var err = await r.json().catch(function () { return {}; });
        throw new Error((err && err.error) || ('HTTP ' + r.status));
      }
      var data = await r.json();
      if (data && data.error) throw new Error(data.error);
      return data;
    },

    async selectListEntity(id) {
      if (!id) return;
      this.listFocusId = id;
      this.listFocusLoading = true;
      this.listFocusDetail = null;
      try {
        var detail = await this.fetchEntityDetail(id);
        this.listFocusDetail = detail;
        if (window.ExportIntelMap && detail.lat && detail.lon) {
          window.ExportIntelMap.flyTo(detail.lat, detail.lon, 14);
          window.ExportIntelMap.highlightEntity(detail.id);
        }
      } catch (e) {
        this.syncMsg = 'Profiel laden mislukt: ' + e.message;
        this.listFocusId = null;
      }
      this.listFocusLoading = false;
    },

    clearListFocus() {
      this.listFocusId = null;
      this.listFocusDetail = null;
    },

    async openEntity(id) {
      if (!id) return;
      this.drawerLoading = true;
      this.drawerOpen = true;
      try {
        if (this.listFocusDetail && this.listFocusDetail.id === id) {
          this.selected = this.listFocusDetail;
        } else {
          this.selected = await this.fetchEntityDetail(id);
          this.listFocusId = id;
          this.listFocusDetail = this.selected;
        }
        if (window.ExportIntelMap && this.selected.lat && this.selected.lon) {
          window.ExportIntelMap.flyTo(this.selected.lat, this.selected.lon, 14);
          window.ExportIntelMap.highlightEntity(this.selected.id);
        }
        if (window.ExportIntelMap && window.ExportIntelMap.closePopup) {
          window.ExportIntelMap.closePopup();
        }
      } catch (e) {
        this.syncMsg = 'Profiel laden mislukt: ' + e.message;
        this.drawerOpen = false;
        this.selected = null;
      }
      this.drawerLoading = false;
    },

    closeDrawer() {
      this.drawerOpen = false;
      this.selected = null;
      this.drawerLoading = false;
    },

    async syncKind(kind) {
      if (kind === 'world') {
        if (!confirm('Wereld-sync: Europa, Midden-Oosten, Afrika en Amerika. OSM + contacten — kan lang duren. Doorgaan?')) return;
      }
      if (kind === 'region' && !this.region) {
        this.syncMsg = 'Selecteer eerst een regio in de filterbalk';
        return;
      }
      this.busy = true;
      this.syncMsg = kind === 'world' ? 'Wereld-sync gestart…' : 'Sync gestart…';
      try {
        var body = {
          country_iso2: this.country || null,
          region_code: kind === 'region' ? this.region : (this.region || null),
          max_priority: 2,
        };
        if (kind === 'world') body = { max_priority: 2 };
        var endpoint = kind === 'region' ? 'region' : kind;
        var r = await fetch('/api/export-intel/sync/' + endpoint, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(body),
        }).then((x) => x.json());
        if (r.error) this.syncMsg = 'Fout: ' + r.error;
        else if (kind === 'world') this.syncMsg = 'Wereld-sync voltooid — kaart wordt ververst';
        else if (kind === 'all' || kind === 'region') this.syncMsg = 'Regio-sync voltooid';
        else this.syncMsg = 'Sync voltooid';
        await this.refresh();
      } catch (e) {
        this.syncMsg = 'Sync mislukt: ' + e.message;
      }
      this.busy = false;
    },

    tabCount(id) {
      if (id === 'distributors') return this.stats.distributors || 0;
      if (id === 'caterers') return this.stats.caterers || 0;
      if (id === 'contacts') return this.stats.contacts || 0;
      if (id === 'tenders') return this.stats.tenders_open || 0;
      if (id === 'pipeline') return this.stats.crm_linked || 0;
      if (id === 'map') return this.stats.entities || 0;
      return 0;
    },

    tabSub(id) {
      var n = this.tabCount(id);
      if (id === 'map') return n + ' op kaart';
      if (id === 'gov') return 'Bronnen per land';
      if (id === 'pipeline') return 'CRM koppeling';
      if (n > 0) return n + ' records';
      return '';
    },

    contactsExportUrl() {
      var u = '/api/export-intel/contacts/export.csv?';
      var parts = [];
      if (this.country) parts.push('country=' + encodeURIComponent(this.country));
      if (this.entityTypeFilter) parts.push('entity_type=' + encodeURIComponent(this.entityTypeFilter));
      return u + parts.join('&');
    },

    typeLabel(t) {
      return (t || '').replace(/_/g, ' ');
    },

    territoryName(iso2) {
      if (!iso2) return '';
      var t = this.territories.find(function (x) { return x.country_iso2 === iso2; });
      return t ? t.name_nl : iso2;
    },

    isSelected(id) {
      return this.selectedIds.indexOf(id) >= 0;
    },

    toggleSelect(id, ev) {
      if (ev) ev.stopPropagation();
      var i = this.selectedIds.indexOf(id);
      if (i >= 0) this.selectedIds.splice(i, 1);
      else this.selectedIds.push(id);
    },

    toggleSelectAllVisible() {
      var list = this.mapHalalMode && this.halalTopEntities.length ? this.halalTopEntities : this.entities;
      if (!list.length) return;
      var allSelected = list.every((e) => this.isSelected(e.id));
      if (allSelected) {
        list.forEach((e) => {
          var i = this.selectedIds.indexOf(e.id);
          if (i >= 0) this.selectedIds.splice(i, 1);
        });
      } else {
        list.forEach((e) => {
          if (!this.isSelected(e.id)) this.selectedIds.push(e.id);
        });
      }
    },

    clearSelection() {
      this.selectedIds = [];
    },

    async setFavorites(ids, favorite) {
      if (!ids.length) return;
      this.busy = true;
      try {
        await fetch('/api/export-intel/entities/favorites', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ entity_ids: ids, favorite: favorite }),
        });
        this.syncMsg = favorite
          ? ids.length + ' favoriet' + (ids.length > 1 ? 'en' : '') + ' opgeslagen'
          : 'Favoriet verwijderd';
        await this.refresh();
      } catch (e) {
        this.syncMsg = 'Favoriet mislukt: ' + e.message;
      }
      this.busy = false;
    },

    async toggleFavorite(id, current, ev) {
      if (ev) ev.stopPropagation();
      await this.setFavorites([id], !current);
    },

    async favoriteSelection() {
      await this.setFavorites(this.selectedIds.slice(), true);
    },

    async unfavoriteSelection() {
      await this.setFavorites(this.selectedIds.slice(), false);
    },

    openCrmModal() {
      if (!this.selectedIds.length) {
        this.syncMsg = 'Selecteer eerst één of meer rijen (checkbox)';
        return;
      }
      this.crmModalOpen = true;
    },

    async pushToCrm(ids) {
      var entityIds = (ids && ids.length) ? ids : this.selectedIds.slice();
      if (!entityIds.length) return;
      this.crmPushBusy = true;
      this.syncMsg = 'CRM import gestart…';
      try {
        var r = await fetch('/api/export-intel/crm/push', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            entity_ids: entityIds,
            create_deals: this.crmCreateDeals,
          }),
        }).then((x) => x.json());
        if (r.error) throw new Error(r.error);
        var created = r.created || 0;
        var linked = r.linked_existing || 0;
        this.syncMsg = 'CRM: ' + created + ' nieuw, ' + linked + ' bestaand gekoppeld';
        this.crmModalOpen = false;
        this.selectedIds = [];
        await this.refresh();
        if (this.listFocusDetail && entityIds.indexOf(this.listFocusDetail.id) >= 0) {
          this.listFocusDetail = await this.fetchEntityDetail(this.listFocusDetail.id);
        }
        if (this.tab === 'pipeline') await this.loadTabData();
      } catch (e) {
        this.syncMsg = 'CRM import mislukt: ' + e.message;
      }
      this.crmPushBusy = false;
    },

    async pushEntityToCrm(id) {
      this.crmCreateDeals = true;
      await this.pushToCrm([id]);
    },

    onFilterCollectionChange() {
      this.applyMapFilters();
    },
  };
}
