/**
 * KLASTER_RULES — ParcelSelector Cluster Behavior Constitution
 * =============================================================
 * Ten plik jest JEDYNYM miejscem gdzie definiowane są zasady zachowania klastrów.
 * Każda zmiana logiki klastrów MUSI być wprowadzona tutaj.
 * anunaki-popup.js czyta te zasady przez window.PS_CLUSTER_RULES.
 *
 * ═══════════════════════════════════════════════════════════════
 * ZASADY (CLUSTER RULES)
 * ═══════════════════════════════════════════════════════════════
 *
 *  RULE-1  Nie pokazuj ramienia dla przewoźnika z 0 punktów w klastrze.
 *          Klaster z 3 przewoźnikami ma 3 ramiona — nie 8 (enabled count).
 *
 *  RULE-2  Kliknięcie klastra zawsze kończy się na zoom >= 17 (poziom pinów).
 *          Zoom 17 = disableClusteringAtZoom → piny widoczne indywidualnie.
 *          Zakazane: zatrzymanie na zoom 15-16 gdzie sub-klastry nadal istnieją.
 *
 *  RULE-3  Ładowanie markerów MUSI być "chunked" (nie blokuj przeglądarki).
 *          Leaflet.markercluster wspiera to natywnie przez chunkedLoading.
 *          animateAddingMarkers WYŁĄCZONE dla zbiorów > 10k markerów.
 *
 *  RULE-4  Klaster jednego przewoźnika → single-bubble layout (buildSingleClusterSVG).
 *
 *  RULE-5  Klaster dwóch przewoźników → dual-bubble layout (buildDualClusterSVG).
 *
 *  RULE-6  Klaster 3+ przewoźników → running-circle layout (buildRunningCircleClusterSVG).
 *
 *  RULE-7  Guard: nie renderuj klastra z total=0. Fallback na inpost jeśli brak danych.
 *
 *  RULE-8  Animacja kliknięcia: pop (scale down→up) na ikonę klastra — natychmiastowy feedback.
 *          Klasa CSS: ps-cluster-pop (dodawana i usuwana przez JS, styl w CSS).
 *
 *  RULE-10 Rozmiar ikony klastra skaluje się logarytmicznie z liczbą punktów (RULE-10).
 *          Mały klaster (kilka punktów) → mała ikona. Duży klaster → duża ikona.
 *          Daje to natychmiastową wizualną hierarchię gęstości bez czytania liczby.
 *          Skala: <10→50px, 10→56px, 50→66px, 200→74px, 500→84px, 2000→96px, 10000+→110px.
 *          Funkcja: _szForCount(n) w anunaki-popup.js. Wzrasta: buildSingle, buildDual, buildRunningCircle.
 *
 *  RULE-9  Małe miejscowości i ciasne klastry → POMIJAJ sub-klastry, idź prosto do pinów.
 *          Trzy poziomy agresywności na podstawie naturalnego zoom bounding-box klastra:
 *
 *          • WIOSKA (naturalZoom >= 12):
 *            1 kliknięcie → zoom 17 → indywidualne piny + Spider dla co-located.
 *            Uzasadnienie: klaster mieści się w ~2km — absurdem jest pokazywanie
 *            sub-klastrów w małej miejscowości. Spider pokazuje że jest więcej
 *            punktów na około, bez mylących "bąbelków" sub-klastrów.
 *
 *          • MIASTO (naturalZoom 9–11):
 *            1 kliknięcie → zoom += 4 (agresywny skok) → maksymalnie 2 kliknięcia
 *            do osiągnięcia poziomu pinów.
 *
 *          • REGION/KRAJ (naturalZoom < 9):
 *            flyToBounds krok po kroku — kraj jest zbyt duży żeby skakać do pinów.
 *
 * ═══════════════════════════════════════════════════════════════
 * JAK DODAĆ NOWY PRZEWOŹNIK:
 * ═══════════════════════════════════════════════════════════════
 *  1. Dodaj go do CARRIER_CONFIG w anunaki-popup.js
 *  2. Dodaj do tablicy ALL_CARRIERS poniżej
 *  3. Klaster auto-adaptuje się (RULE-1: ramię tylko jeśli count > 0)
 *  4. Nie zmieniaj ręcznie liczby ramion — oblicza się dynamicznie
 */

window.PS_CLUSTER_RULES = (function () {
    'use strict';

    /* ── Stałe ─────────────────────────────────────────────────────────── */

    /** RULE-2 + RULE-9: Zoom powyżej którego klastry są wyłączone (piny indywidualne).
     *  MUSI być identyczny jak disableClusteringAtZoom w anunaki-popup.js.
     *  Spider mode aktywuje się przy >= DISABLE_CLUSTER_ZOOM. */
    var DISABLE_CLUSTER_ZOOM = 15;

    /** RULE-9: Próg "wioski" — naturalZoom >= tej wartości = mała miejscowość.
     *  Jedno kliknięcie → od razu poziom pinów (zoom 17).
     *  naturalZoom 12 ≈ bounding box ~2–3 km → widać całą małą miejscowość. */
    var VILLAGE_ZOOM_THRESHOLD = 12;

    /** RULE-9: Próg "miasto" — naturalZoom >= tej wartości = dzielnica / małe miasto.
     *  Skok o +4 zoom levele zamiast naturalnego kroku. Maks. 2 kliknięcia do pinów. */
    var TOWN_ZOOM_THRESHOLD = 9;

    /** InPost Paczkomaty only (free version, Poland). */
    var ALL_CARRIERS = ['inpost'];

    /* ── RULE-3: Opcje chunked loading dla L.markerClusterGroup ─────────── */
    /**
     * Te opcje MUSZĄ być przekazane do L.markerClusterGroup() w anunaki-popup.js.
     * chunkedLoading = addLayers() działa w kawałkach ze setTimeout między nimi.
     * Dzięki temu przeglądarka może obsługiwać gesty/scroll podczas ładowania 160k pinów.
     */
    var CHUNK_OPTS = {
        animateAddingMarkers: false,  // RULE-3: wyłączone — animacja 160k markerów = freeze
        chunkedLoading:       true,   // RULE-3: przetwarzaj w kawałkach
        chunkSize:            2000,   // 2000 markerów na chunk — dobry balans perf/responsywność
        chunkDelay:           0       // setTimeout(0) — oddaj kontrolę przeglądarce między chunkami
    };

    /* ── RULE-2 + RULE-9: Logika kliknięcia klastra ─────────────────────── */
    /**
     * Oblicza CEL nawigacji po kliknięciu klastra.
     *
     * Trzy ścieżki (RULE-9):
     *
     *   WIOSKA (naturalZoom >= VILLAGE_ZOOM_THRESHOLD = 12):
     *     → flyTo(cluster.center, 17) — 1 kliknięcie, od razu piny + Spider
     *
     *   STACKED (naturalZoom <= currentZoom + 1.5) lub naturalZoom >= 17:
     *     → flyTo(cluster.center, 17) — klaster "utknął", wymuś poziom pinów
     *
     *   MIASTO (naturalZoom >= TOWN_ZOOM_THRESHOLD = 9):
     *     → flyTo(cluster.center, min(naturalZoom + 4, 17)) — agresywny skok
     *       Uzasadnienie: +4 zoom = maks. 2 kliknięcia do poziomu pinów
     *
     *   REGION/KRAJ (naturalZoom < 9):
     *     → flyToBounds(bounds, maxZoom: 17) — krok po kroku, kraj jest zbyt duży
     *
     * @param {L.MarkerCluster} cluster
     * @param {number}          currentZoom
     * @param {L.Map}           map
     * @returns {{ action: 'flyTo'|'flyToBounds', latlng?, zoom?, bounds?,
     *             maxZoom?, duration: number, easeLinearity: number }}
     */
    function getClusterClickTarget(cluster, currentZoom, map) {
        var bounds = cluster.getBounds();
        var naturalZoom = currentZoom;
        try {
            naturalZoom = map.getBoundsZoom(bounds, false, [80, 80]);
        } catch (e) {}

        // STACKED: klaster już przy granicy zoom lub utknął (naturalZoom ≈ currentZoom).
        // flyToBounds zamiast flyTo(centroid) — centroid może być nad pustym polem!
        // flyToBounds(bounds, maxZoom:17) zawsze trafia NA punkty, nie obok nich.
        if (naturalZoom <= currentZoom + 1.5 || naturalZoom >= DISABLE_CLUSTER_ZOOM) {
            return {
                action:        'flyToBounds',
                bounds:        bounds,
                maxZoom:       DISABLE_CLUSTER_ZOOM,
                padding:       [60, 60],
                duration:      0.85,
                easeLinearity: 0.12
            };
        }

        // RULE-9: WIOSKA — mała miejscowość, 1 klik = piny.
        // flyToBounds gwarantuje że widać WSZYSTKIE punkty klastra (nie tylko centroid).
        if (naturalZoom >= VILLAGE_ZOOM_THRESHOLD) {
            return {
                action:        'flyToBounds',
                bounds:        bounds,
                maxZoom:       DISABLE_CLUSTER_ZOOM,
                padding:       [60, 60],
                duration:      0.9,
                easeLinearity: 0.10
            };
        }

        // RULE-9: MIASTO — agresywny skok +4 poziomy, maks. 2 kliknięcia do pinów.
        // flyTo(centroid) tu jest OK — zagłębiamy się w miasto, nie szukamy konkretnych pinów.
        if (naturalZoom >= TOWN_ZOOM_THRESHOLD) {
            var townTarget = Math.min(naturalZoom + 4, DISABLE_CLUSTER_ZOOM);
            return {
                action:        'flyTo',
                latlng:        cluster.getLatLng(),
                zoom:          townTarget,
                duration:      1.0,
                easeLinearity: 0.10
            };
        }

        // REGION/KRAJ — flyToBounds krok po kroku (naturalZoom < 9)
        return {
            action:        'flyToBounds',
            bounds:        bounds,
            maxZoom:       DISABLE_CLUSTER_ZOOM,
            padding:       [80, 80],
            duration:      1.1,
            easeLinearity: 0.09
        };
    }

    /* ── RULE-1: Filtr aktywnych przewoźników w klastrze ────────────────── */
    /**
     * Zwraca TYLKO przewoźników z count > 0 w danym klastrze.
     * Nigdy nie zwraca przewoźnika z count = 0 (RULE-1).
     *
     * @param {L.MarkerCluster} cluster
     * @returns {{ carrier: string, count: number }[]}  posortowane jak ALL_CARRIERS
     */
    function getActiveCarriersInCluster(cluster) {
        var counts = {};
        ALL_CARRIERS.forEach(function (c) { counts[c] = 0; });
        cluster.getAllChildMarkers().forEach(function (m) {
            var c = (m.options && m.options.carrier) || 'inpost';
            if (counts.hasOwnProperty(c)) { counts[c]++; }
        });
        return ALL_CARRIERS
            .filter(function (c) { return counts[c] > 0; })
            .map(function (c) { return { carrier: c, count: counts[c] }; });
    }

    /* ── RULE-7: Guard ────────────────────────────────────────────────────── */
    /**
     * Zwraca false jeśli klaster jest "pusty" (total = 0).
     * iconCreateFunction powinna wywołać ten guard przed budową SVG.
     *
     * @param {{ [carrier: string]: number }} counts
     * @returns {boolean}  true = OK do renderowania
     */
    function validateClusterCounts(counts) {
        return ALL_CARRIERS.some(function (c) { return (counts[c] || 0) > 0; });
    }

    /* ── RULE-8: Animacja kliknięcia ─────────────────────────────────────── */
    /**
     * Dodaj animację pop do ikony klastra po kliknięciu.
     * Klasa 'ps-cluster-pop' jest zdefiniowana w anunaki-popup.css.
     * Usuwa się automatycznie po 450ms.
     *
     * @param {L.MarkerCluster} cluster
     */
    function triggerClusterPopAnimation(cluster) {
        var icon = cluster._icon;
        if (!icon) { return; }
        icon.classList.add('ps-cluster-pop');
        setTimeout(function () {
            if (icon) { icon.classList.remove('ps-cluster-pop'); }
        }, 450);
    }

    /* ── Public API ───────────────────────────────────────────────────────── */
    return {
        DISABLE_CLUSTER_ZOOM:        DISABLE_CLUSTER_ZOOM,
        VILLAGE_ZOOM_THRESHOLD:      VILLAGE_ZOOM_THRESHOLD,
        TOWN_ZOOM_THRESHOLD:         TOWN_ZOOM_THRESHOLD,
        ALL_CARRIERS:                ALL_CARRIERS,
        CHUNK_OPTS:                  CHUNK_OPTS,
        getClusterClickTarget:       getClusterClickTarget,
        getActiveCarriersInCluster:  getActiveCarriersInCluster,
        validateClusterCounts:       validateClusterCounts,
        triggerClusterPopAnimation:  triggerClusterPopAnimation
    };

}());
