/* ============================================================
   BAN YARO — Events (Hundeveranstaltungen)
   Liste/Karte · Filter · Erstellen/Bearbeiten
   ============================================================ */

window.Page_events = (() => {

  // ----------------------------------------------------------
  // Konstanten
  // ----------------------------------------------------------
  const TYPEN = [
    { id: 'alle',        label: 'Alle',         icon: 'ticket'        },
    { id: 'ausstellung', label: 'Ausstellung',  icon: 'trophy'        },
    { id: 'training',    label: 'Training',     icon: 'graduation-cap'},
    { id: 'treffen',     label: 'Treffen',      icon: 'dog'           },
    { id: 'markt',       label: 'Markt',        icon: 'shopping-bag'  },
    { id: 'wettkampf',   label: 'Wettkampf',    icon: 'medal'         },
    { id: 'sonstiges',   label: 'Sonstiges',    icon: 'push-pin'      },
  ];

  const TYP_COLOR = {
    ausstellung: '#8b5cf6',
    training:    '#3b82f6',
    treffen:     '#10b981',
    markt:       '#f59e0b',
    wettkampf:   '#ef4444',
    sonstiges:   '#6b7280',
  };

  // ----------------------------------------------------------
  // State
  // ----------------------------------------------------------
  let _container    = null;
  let _state        = null;
  let _events       = [];
  let _filter       = 'alle';
  let _quellFilter  = 'alle';   // 'alle' | 'vdh' | 'nutzer'
  let _search       = '';
  let _view         = 'liste';  // liste | karte
  let _map          = null;
  let _markers      = [];
  let _clusterGroup = null;
  let _myRsvp       = {};   // { [event_id]: 'going'|'maybe'|null }

  // ----------------------------------------------------------
  // Phosphor-Icon-Helper
  // ----------------------------------------------------------
  function _icon(name) {
    return `<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#${name}"></use></svg>`;
  }

  // _emptyState ersetzt durch UI.emptyState()

  // ----------------------------------------------------------
  // init
  // ----------------------------------------------------------
  async function init(container, appState) {
    _container = container;
    _state     = appState;
    _render();
    await _load();
  }

  async function refresh() {
    await _load();
  }

  // ----------------------------------------------------------
  // Render Grundstruktur
  // ----------------------------------------------------------
  function _render() {
    _container.innerHTML = `
      <div class="by-toolbar">
        <div class="events-view-toggle">
          <button class="events-view-btn active" data-ev-view="liste">${UI.icon('list')} Liste</button>
          <button class="events-view-btn" data-ev-view="karte">${UI.icon('map-trifold')} Karte</button>
        </div>
        <div style="flex:1"></div>
        ${_state.user ? `<button class="btn btn-primary btn-sm" id="ev-new-btn">${UI.icon('plus')} Event</button>` : ''}
      </div>

      <div class="diary-search-wrap" style="margin:var(--space-2) var(--space-3) 0" id="ev-search-wrap">
        <svg class="ph-icon diary-search-icon" aria-hidden="true"><use href="/icons/phosphor.svg#magnifying-glass"></use></svg>
        <input type="search" class="diary-search-input" id="ev-search"
               placeholder="Events durchsuchen…" autocomplete="off">
      </div>

      <div class="events-filter-bar by-tabs" id="ev-filter-bar">
        ${TYPEN.map(t => `
          <button class="by-tab ${t.id === 'alle' ? 'active' : ''}" data-ev-typ="${t.id}">
            ${UI.icon(t.icon)} ${t.label}
          </button>
        `).join('')}
      </div>

      <div class="events-source-bar by-tabs" id="ev-source-bar">
        <button class="by-tab active" data-ev-quelle="alle">Alle Quellen</button>
        <button class="by-tab" data-ev-quelle="vdh">
          <span class="ev-vdh-badge">VDH</span> VDH-Events
        </button>
        <button class="by-tab" data-ev-quelle="nutzer">Von Nutzern</button>
      </div>

      <div class="events-list" id="ev-list"></div>
      <div class="events-map" id="ev-map" style="display:none"></div>
    `;

    _container.addEventListener('click', _onClick);

    // Suche mit Debounce
    let _searchTimer = null;
    document.getElementById('ev-search')?.addEventListener('input', e => {
      clearTimeout(_searchTimer);
      _searchTimer = setTimeout(() => {
        _search = e.target.value.trim().toLowerCase();
        if (_view === 'karte') { _renderMap(_filtered()); } else { _renderList(); }
      }, 300);
    });
  }

  // ----------------------------------------------------------
  // Daten laden
  // ----------------------------------------------------------
  async function _load() {
    const listEl = document.getElementById('ev-list');
    if (!listEl) return;
    listEl.innerHTML = UI.skeleton(3);
    try {
      _events = await API.events.list();
      _renderList();
    } catch (e) {
      UI.toast(e.message, 'error');
    }
  }

  // ----------------------------------------------------------
  // Gefilterte Events ermitteln
  // ----------------------------------------------------------
  function _filtered() {
    let evs = _filter === 'alle' ? _events : _events.filter(e => e.typ === _filter);
    if (_quellFilter !== 'alle') {
      evs = evs.filter(e => (e.quelle || 'nutzer') === _quellFilter);
    }
    if (_search) {
      const q = _search;
      evs = evs.filter(e =>
        (e.titel       || '').toLowerCase().includes(q) ||
        (e.ort_name    || '').toLowerCase().includes(q) ||
        (e.beschreibung|| '').toLowerCase().includes(q)
      );
    }
    return evs;
  }

  // ----------------------------------------------------------
  // Liste rendern
  // ----------------------------------------------------------
  function _renderList() {
    const listEl = document.getElementById('ev-list');
    if (!listEl) return;

    const filtered = _filtered();
    if (!filtered.length) {
      listEl.innerHTML = UI.emptyState({
        icon:  UI.icon('calendar-blank'),
        title: _search ? 'Keine Events gefunden' : 'Keine Events in der Nähe',
        text:  _search
          ? `Keine Events passen zu „${UI.escape(_search)}".`
          : 'Hier erscheinen Hundeveranstaltungen, Treffen und Aktivitäten in deiner Umgebung.',
      });
      return;
    }

    // Monats-Gruppierung
    const groups = {};
    for (const ev of filtered) {
      const d    = new Date(ev.datum + 'T00:00:00');
      const key  = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
      const label = d.toLocaleDateString('de-DE', { month: 'long', year: 'numeric' });
      if (!groups[key]) groups[key] = { label, items: [] };
      groups[key].items.push(ev);
    }

    let html = '';
    for (const key of Object.keys(groups).sort()) {
      const g = groups[key];
      html += `<div class="events-month-label">${g.label}</div>`;
      for (const ev of g.items) {
        html += _cardHTML(ev);
      }
    }
    listEl.innerHTML = html;
  }

  function _cardHTML(ev) {
    const d     = new Date(ev.datum + 'T00:00:00');
    const dow   = d.toLocaleDateString('de-DE', { weekday: 'short' });
    const day   = d.getDate();
    const mon   = d.toLocaleDateString('de-DE', { month: 'short' });
    const typ   = TYPEN.find(t => t.id === ev.typ) || TYPEN[TYPEN.length - 1];
    const color = TYP_COLOR[ev.typ] || '#6b7280';
    const isOwn = _state.user?.id === ev.user_id;
    const isVdh = ev.quelle === 'vdh';

    return `
      <div class="events-card" data-ev-id="${ev.id}" style="border-left-color:${color}">
        <div class="events-date-badge">
          <span class="day">${dow}</span>
          <span class="num">${day}</span>
          <span class="month">${mon}</span>
        </div>
        <div class="events-card-body">
          <div class="events-card-title">
            ${UI.escape(ev.titel)}
            ${isVdh ? `<span class="ev-vdh-badge" title="Vom VDH importiert">VDH</span>` : ''}
          </div>
          <div class="events-card-meta">
            <span class="events-badge" style="background:${color}20;color:${color}">${UI.icon(typ.icon)} ${typ.label}</span>
            ${ev.uhrzeit ? `· ${_icon('clock')} ${ev.uhrzeit} Uhr` : ''}
            ${ev.ort_name ? `· ${_icon('map-pin')} ${UI.escape(ev.ort_name)}` : ''}
          </div>
          ${ev.rsvp_count ? `<span class="event-attendees" data-ev-attendees="${ev.id}">${_icon('users')} ${ev.rsvp_count} nehmen teil</span>` : ''}
          ${ev.link ? `<div class="events-card-actions">
            <a class="btn btn-ghost btn-xs ev-ext-link" href="${UI.escape(ev.link)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">
              ${_icon('arrow-square-out')} Details
            </a>
          </div>` : ''}
        </div>
        <div style="display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-1)">
          ${isOwn ? `<button class="btn-icon" data-ev-edit="${ev.id}" title="Bearbeiten" onclick="event.stopPropagation()">${_icon('pencil-simple')}</button>` : ''}
          ${_state.user ? `<button class="btn-icon ev-note-btn" data-ev-note-id="${ev.id}"
                              data-ev-note-label="${UI.escape(ev.titel + ' ' + ev.datum)}"
                              data-ev-note-ort="${UI.escape(ev.ort_name || '')}"
                              title="Notiz" style="color:var(--c-text-muted)" onclick="event.stopPropagation()">
                              ${_icon('note-pencil')}</button>` : ''}
        </div>
      </div>
    `;
  }

  // ----------------------------------------------------------
  // Karte
  // ----------------------------------------------------------
  async function _renderMap(filtered) {
    const mapEl = document.getElementById('ev-map');
    if (!mapEl) return;

    await UI.loadLeaflet(true); // true = mit MarkerCluster

    if (!_map) {
      _map = L.map('ev-map', { zoomControl: true }).setView([51.1657, 10.4515], 6);
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(_map);
    }

    // Cluster-Gruppe aufräumen und neu befüllen
    if (_clusterGroup) {
      _map.removeLayer(_clusterGroup);
    }
    _clusterGroup = L.markerClusterGroup();
    _markers = [];

    const bounds = [];
    for (const ev of filtered) {
      if (!ev.lat || !ev.lon) continue;
      const color = TYP_COLOR[ev.typ] || '#6b7280';
      const typ   = TYPEN.find(t => t.id === ev.typ) || TYPEN[TYPEN.length - 1];
      const d     = new Date(ev.datum + 'T00:00:00');
      const datum = d.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric' });
      // Events nutzen rotierten Diamant-Marker (nicht Kreis) — UI.leafletMarker() nicht anwendbar
      const icon  = L.divIcon({
        className: '',
        html: `<div style="width:32px;height:32px;border-radius:50% 50% 50% 0;background:${color};border:2px solid #fff;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 6px rgba(0,0,0,0.3);transform:rotate(-45deg);color:#fff"><svg style="width:14px;height:14px;transform:rotate(45deg);fill:currentColor" viewBox="0 0 256 256"><use href="/icons/phosphor.svg#${typ.icon}"></use></svg></div>`,
        iconSize: [32, 32], iconAnchor: [16, 32],
      });
      const popup = `
        <div style="min-width:180px">
          <strong>${UI.escape(ev.titel)}</strong><br>
          <span style="color:var(--c-text-muted);font-size:12px">${datum}</span><br>
          ${ev.ort_name ? `<span style="font-size:12px">📍 ${UI.escape(ev.ort_name)}</span><br>` : ''}
          ${ev.beschreibung ? `<span style="font-size:12px">${UI.escape(ev.beschreibung.slice(0, 80))}${ev.beschreibung.length > 80 ? '…' : ''}</span><br>` : ''}
          <a href="#" onclick="event.preventDefault();Page_events._openDetail(${ev.id})"
             style="font-size:12px;color:var(--c-primary,#2563eb)">Details</a>
        </div>
      `;
      const m = L.marker([ev.lat, ev.lon], { icon }).bindPopup(popup);
      _clusterGroup.addLayer(m);
      _markers.push(m);
      bounds.push([ev.lat, ev.lon]);
    }

    _map.addLayer(_clusterGroup);

    if (bounds.length) {
      _map.fitBounds(bounds, { padding: [40, 40], maxZoom: 12 });
    } else {
      // Versuche Nutzerstandort, sonst Deutschland-Übersicht
      try {
        const pos = await API.getLocation({ timeout: 5000 });
        _map.setView([pos.lat, pos.lon], 10);
      } catch {
        _map.setView([51.1657, 10.4515], 6);
      }
    }

    _map.invalidateSize();
    setTimeout(() => _map.invalidateSize(), 100);
  }

  // _loadLeaflet und _loadMarkerCluster ersetzt durch UI.loadLeaflet(true)

  // ----------------------------------------------------------
  // Detail-Modal
  // ----------------------------------------------------------
  async function _showDetail(id) {
    let ev;
    try { ev = await API.events.get(id); } catch { return; }

    const typ    = TYPEN.find(t => t.id === ev.typ) || TYPEN[TYPEN.length - 1];
    const color  = TYP_COLOR[ev.typ] || '#6b7280';
    const d      = new Date(ev.datum + 'T00:00:00');
    const datum  = d.toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
    const isOwn  = _state.user?.id === ev.user_id;
    const isVdh  = ev.quelle === 'vdh';
    const myRsvp = _myRsvp[id] ?? null;

    // RSVP-Bar (nur für eingeloggte User)
    const rsvpBar = _state.user ? `
      <div class="event-rsvp-bar" id="ev-rsvp-bar-${id}">
        <button class="btn event-rsvp-btn ${myRsvp === 'going' ? 'active' : ''}" data-rsvp-id="${id}" data-rsvp-status="going">
          ${_icon('check-circle')} Ich komme
        </button>
        <button class="btn event-rsvp-btn ${myRsvp === 'maybe' ? 'active' : ''}" data-rsvp-id="${id}" data-rsvp-status="maybe">
          ${_icon('question')} Vielleicht
        </button>
        ${ev.rsvp_count ? `<span class="event-attendees" id="ev-attendees-${id}" data-ev-attendees="${id}" style="margin-left:auto">${_icon('users')} ${ev.rsvp_count} nehmen teil</span>` : `<span class="event-attendees" id="ev-attendees-${id}" data-ev-attendees="${id}" style="margin-left:auto;display:none">${_icon('users')} 0 nehmen teil</span>`}
      </div>
    ` : (ev.rsvp_count ? `<div class="event-rsvp-bar"><span class="event-attendees" data-ev-attendees="${id}">${_icon('users')} ${ev.rsvp_count} nehmen teil</span></div>` : '');

    const body = `
      <div style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-3)">
        <span class="events-badge" style="background:${color}20;color:${color};font-size:var(--text-sm)">${UI.icon(typ.icon)} ${typ.label}</span>
        ${isVdh ? `<span class="ev-vdh-badge">VDH</span>` : ''}
      </div>
      <div class="events-detail-row">${_icon('calendar-dots')} ${datum}${ev.uhrzeit ? ' · ' + ev.uhrzeit + ' Uhr' : ''}</div>
      ${ev.ort_name ? `<div class="events-detail-row">${_icon('map-pin')} ${UI.escape(ev.ort_name)}</div>` : ''}
      ${ev.beschreibung ? `<div class="events-detail-desc">${UI.escape(ev.beschreibung)}</div>` : ''}
      ${ev.link ? `<div class="events-detail-row">
        ${_icon('arrow-square-out')}
        <a href="${UI.escape(ev.link)}" target="_blank" rel="noopener">Mehr Infos</a>
      </div>` : ''}
      <div class="events-detail-row" style="color:var(--c-text-muted);font-size:var(--text-xs)">
        ${_icon('user')} Veranstalter: ${UI.escape(ev.veranstalter_name || '–')}
      </div>
      ${rsvpBar}
      <div id="ev-attendees-panel-${id}"></div>
    `;

    const footer = isOwn ? `
      <button class="btn btn-secondary" id="ev-detail-edit">${_icon('pencil-simple')} Bearbeiten</button>
      <button class="btn btn-danger"    id="ev-detail-del">${_icon('trash')} Löschen</button>
    ` : (ev.link ? `
      <a class="btn btn-primary" href="${UI.escape(ev.link)}" target="_blank" rel="noopener">
        ${_icon('arrow-square-out')} Zur Veranstaltung
      </a>
    ` : '');

    UI.modal.open({ title: UI.escape(ev.titel), body, footer });

    document.getElementById('ev-detail-edit')?.addEventListener('click', () => {
      UI.modal.close(); setTimeout(() => _openForm(ev), 50);
    });
    document.getElementById('ev-detail-del')?.addEventListener('click', () => _deleteEvent(ev));

    // RSVP-Buttons
    document.querySelectorAll(`[data-rsvp-id="${id}"]`).forEach(btn => {
      btn.addEventListener('click', () => _handleRsvp(id, btn.dataset.rsvpStatus));
    });
  }

  async function _handleRsvp(eventId, status) {
    const current = _myRsvp[eventId] ?? null;
    try {
      if (current === status) {
        // Toggle off → absagen
        await API.events.cancelRsvp(eventId);
        _myRsvp[eventId] = null;
      } else {
        const res = await API.events.rsvp(eventId, status);
        _myRsvp[eventId] = status;
        // Teilnehmerzähler aktualisieren
        _updateAttendeeCount(eventId, res.rsvp_count);
      }
      // Button-Styles aktualisieren
      document.querySelectorAll(`[data-rsvp-id="${eventId}"]`).forEach(btn => {
        btn.classList.toggle('active', btn.dataset.rsvpStatus === (_myRsvp[eventId] ?? ''));
      });
      // Bei Absage Zähler neu laden
      if (current === status) {
        const attendees = await API.events.listRsvp(eventId);
        const goingCount = attendees.filter(a => a.status === 'going').length;
        _updateAttendeeCount(eventId, goingCount);
      }
    } catch (e) { UI.toast(e.message, 'error'); }
  }

  function _updateAttendeeCount(eventId, count) {
    // Im Modal
    const span = document.getElementById(`ev-attendees-${eventId}`);
    if (span) {
      if (count > 0) {
        span.innerHTML = `${_icon('users')} ${count} nehmen teil`;
        span.style.display = '';
      } else {
        span.style.display = 'none';
      }
    }
    // In der Listenansicht (Event-Objekt aktualisieren)
    const ev = _events.find(x => x.id === eventId);
    if (ev) {
      ev.rsvp_count = count;
      // Karte neu rendern falls sichtbar
      const card = document.querySelector(`[data-ev-id="${eventId}"]`);
      if (card) card.outerHTML = _cardHTML(ev);
    }
  }

  async function _showAttendees(eventId) {
    const panel = document.getElementById(`ev-attendees-panel-${eventId}`);
    if (!panel) return;
    if (panel.dataset.loaded) { panel.innerHTML = ''; delete panel.dataset.loaded; return; }
    try {
      const attendees = await API.events.listRsvp(eventId);
      if (!attendees.length) { panel.innerHTML = '<p style="font-size:var(--text-sm);color:var(--c-text-muted);margin-top:var(--space-2)">Noch keine Zusagen.</p>'; }
      else {
        panel.innerHTML = `
          <div class="ev-attendees-list">
            ${attendees.map(a => `
              <span class="ev-attendee-chip">
                ${a.status === 'going' ? _icon('check-circle') : _icon('question')}
                ${UI.escape(a.name)}
              </span>
            `).join('')}
          </div>`;
      }
      panel.dataset.loaded = '1';
    } catch { /* ignore */ }
  }

  async function _deleteEvent(ev) {
    if (!confirm(`"${ev.titel}" wirklich löschen?`)) return;
    try {
      await API.events.delete(ev.id);
      UI.modal.close();
      UI.toast('Event gelöscht.');
      await _load();
    } catch (e) { UI.toast(e.message, 'error'); }
  }

  // ----------------------------------------------------------
  // Erstellen / Bearbeiten
  // ----------------------------------------------------------
  function openNew() { _openForm(null); }

  function _openForm(ev) {
    const isEdit = !!ev;
    const id     = 'ev-form';
    const body   = `
      <form id="${id}">
        <div class="form-group">
          <label class="form-label">Titel *</label>
          <input class="form-control" name="titel" required value="${ev ? UI.escape(ev.titel) : ''}">
        </div>
        <div class="form-row-2">
          <div class="form-group">
            <label class="form-label">Datum *</label>
            <input class="form-control" type="date" name="datum" required value="${ev?.datum || ''}">
          </div>
          <div class="form-group">
            <label class="form-label">Uhrzeit</label>
            <input class="form-control" type="time" name="uhrzeit" value="${ev?.uhrzeit || ''}">
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">Typ *</label>
          <select class="form-control" name="typ">
            ${TYPEN.filter(t => t.id !== 'alle').map(t =>
              `<option value="${t.id}" ${ev?.typ === t.id ? 'selected' : ''}>${UI.icon(t.icon)} ${t.label}</option>`
            ).join('')}
          </select>
        </div>
        <div class="form-group">
          <label class="form-label">Ort / Veranstaltungsort</label>
          <input class="form-control" name="ort_name" placeholder="z.B. Stadtpark München" value="${ev?.ort_name || ''}">
        </div>
        <div class="form-group">
          <label class="form-label">GPS-Position</label>
          <div id="ev-location-picker"></div>
        </div>
        <div class="form-group" style="margin-top:var(--space-3)">
          <label class="form-label">Beschreibung</label>
          <textarea class="form-control" name="beschreibung" rows="3">${ev?.beschreibung || ''}</textarea>
        </div>
        <div class="form-group">
          <label class="form-label">Link / Website</label>
          <input class="form-control" type="url" name="link" placeholder="https://..." value="${ev?.link || ''}">
        </div>
      </form>
    `;

    const footer = `
      <div style="display:flex;flex-direction:column;gap:var(--space-2);width:100%">
        <button class="btn btn-primary" type="submit" form="${id}" id="ev-submit-btn" style="width:100%">
          ${isEdit ? 'Speichern' : 'Event erstellen'}
        </button>
        <div style="display:flex;gap:var(--space-2)">
          ${isEdit ? `<button type="button" class="btn btn-danger" id="ev-form-delete">Löschen</button>` : ''}
          <button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
        </div>
      </div>
    `;

    UI.modal.open({ title: isEdit ? 'Event bearbeiten' : 'Neues Event', body, footer });

    document.getElementById('ev-form-delete')?.addEventListener('click', async () => {
      const ok = await UI.modal.confirm({
        title: 'Event löschen?', message: 'Nicht rückgängig.', confirmText: 'Löschen', danger: true,
      });
      if (ok) await _deleteEvent(ev);
    });

    // Location-Picker initialisieren
    const _picker = UI.locationPicker({
      containerId: 'ev-location-picker',
    });
    if (ev?.lat && ev?.lon) {
      _picker.setValue(ev.lat, ev.lon, ev.ort_name || null);
    }

    const form      = document.getElementById(id);
    const submitBtn = document.querySelector(`[form="${id}"][type="submit"]`) || form.querySelector('[type="submit"]');

    form.addEventListener('submit', async e => {
      e.preventDefault();
      const fd  = new FormData(form);
      const loc = _picker.getValue();
      const data = {
        titel:        fd.get('titel'),
        datum:        fd.get('datum'),
        uhrzeit:      fd.get('uhrzeit') || null,
        typ:          fd.get('typ'),
        ort_name:     loc.name || fd.get('ort_name') || null,
        lat:          loc.lat  || null,
        lon:          loc.lon  || null,
        beschreibung: fd.get('beschreibung') || null,
        link:         fd.get('link') || null,
      };
      if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = '…'; }
      try {
        isEdit ? await API.events.update(ev.id, data) : await API.events.create(data);
        UI.modal.close();
        UI.toast(isEdit ? 'Event aktualisiert.' : 'Event erstellt!');
        await _load();
      } catch (err) {
        UI.toast(err.message, 'error');
      } finally {
        if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = isEdit ? 'Speichern' : 'Event erstellen'; }
      }
    });
  }

  // ----------------------------------------------------------
  // Click-Handler
  // ----------------------------------------------------------
  function _onClick(e) {
    // Quelle-Filter
    const sourceBtn = e.target.closest('[data-ev-quelle]');
    if (sourceBtn) {
      _quellFilter = sourceBtn.dataset.evQuelle;
      document.querySelectorAll('[data-ev-quelle]').forEach(b => b.classList.toggle('active', b.dataset.evQuelle === _quellFilter));
      if (_view === 'karte') { _renderMap(_filtered()); } else { _renderList(); }
      return;
    }

    // Typ-Filter
    const filterBtn = e.target.closest('[data-ev-typ]');
    if (filterBtn) {
      _filter = filterBtn.dataset.evTyp;
      document.querySelectorAll('[data-ev-typ]').forEach(b => b.classList.toggle('active', b.dataset.evTyp === _filter));
      if (_view === 'karte') { _renderMap(_filtered()); } else { _renderList(); }
      return;
    }

    // View-Toggle
    const viewBtn = e.target.closest('[data-ev-view]');
    if (viewBtn) {
      _view = viewBtn.dataset.evView;
      document.querySelectorAll('[data-ev-view]').forEach(b => b.classList.toggle('active', b.dataset.evView === _view));
      const listEl = document.getElementById('ev-list');
      const mapEl  = document.getElementById('ev-map');
      if (_view === 'karte') {
        listEl.style.display = 'none';
        mapEl.style.display  = 'block';
        // Erst div sichtbar machen, dann Karte initialisieren
        _renderMap(_filtered());
      } else {
        // Karte sauber entfernen
        if (_map) {
          _map.remove();
          _map          = null;
          _clusterGroup = null;
          _markers      = [];
        }
        mapEl.style.display  = 'none';
        listEl.style.display = '';
        _renderList();
      }
      return;
    }

    // Neu-Button
    if (e.target.closest('#ev-new-btn')) { openNew(); return; }

    // Externer Link — nicht als Karten-Klick behandeln
    if (e.target.closest('.ev-ext-link')) return;

    // Bearbeiten-Icon auf Karte
    const editBtn = e.target.closest('[data-ev-edit]');
    if (editBtn) {
      e.stopPropagation();
      const id = parseInt(editBtn.dataset.evEdit);
      const ev = _events.find(x => x.id === id);
      if (ev) _openForm(ev);
      return;
    }

    // Teilnehmer-Liste anzeigen (Karten-Ansicht oder Modal)
    const attendeesBtn = e.target.closest('[data-ev-attendees]');
    if (attendeesBtn) {
      e.stopPropagation();
      _showAttendees(parseInt(attendeesBtn.dataset.evAttendees));
      return;
    }

    // Notiz-Button
    const noteBtn = e.target.closest('.ev-note-btn');
    if (noteBtn) {
      e.stopPropagation();
      _openNoteModal(
        'event',
        parseInt(noteBtn.dataset.evNoteId),
        noteBtn.dataset.evNoteLabel,
        noteBtn.dataset.evNoteOrt || null
      );
      return;
    }

    // Karten-Klick → Detail
    const card = e.target.closest('[data-ev-id]');
    if (card) { _showDetail(parseInt(card.dataset.evId)); }
  }

  // ----------------------------------------------------------
  // Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
  // ----------------------------------------------------------
  async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
    let existingNote = null;
    try { existingNote = await API.notes.get(parentType, String(parentId)); } catch {}

    const ovl = document.createElement('div');
    ovl.style.cssText = 'position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,0.55);display:flex;align-items:flex-end;justify-content:center';
    ovl.innerHTML = `
      <div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
                  padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
        <div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
          <svg class="ph-icon" aria-hidden="true" style="color:var(--c-primary)"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
          <span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
          <button id="ev-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
            <svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
          </button>
        </div>
        <textarea id="ev-note-text" rows="5"
                  style="width:100%;box-sizing:border-box;padding:var(--space-3);
                         border:1.5px solid var(--c-border);border-radius:var(--radius-md);
                         font-size:var(--text-sm);font-family:inherit;
                         background:var(--c-bg);color:var(--c-text);resize:vertical;flex:1"
                  placeholder="Deine Notiz zu diesem Event…">${UI.escape(existingNote?.text || '')}</textarea>
        <div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
          <button id="ev-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
          <button id="ev-note-save"   class="btn btn-primary flex-1">Speichern</button>
        </div>
      </div>
    `;
    document.body.appendChild(ovl);

    const close = () => ovl.remove();
    ovl.querySelector('#ev-note-close')?.addEventListener('click', close);
    ovl.querySelector('#ev-note-cancel')?.addEventListener('click', close);
    ovl.addEventListener('click', e => { if (e.target === ovl) close(); });

    ovl.querySelector('#ev-note-save')?.addEventListener('click', async () => {
      const text = ovl.querySelector('#ev-note-text')?.value?.trim() || '';
      const payload = { text, parent_label: parentLabel, location_name: locationName || null };
      try {
        if (existingNote?.id) {
          await API.notes.update(existingNote.id, payload);
        } else {
          await API.notes.create(parentType, String(parentId), payload);
        }
        UI.toast.success('Notiz gespeichert.');
        close();
      } catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); }
    });
  }

  return { init, refresh, openNew, _openDetail: _showDetail };

})();
