import { unsafeCSS, LitElement, html } from "lit";
import { property } from "lit/decorators.js";
import { customElement } from "../../decorator.js";
import componentStyle from "./poi-layer-stack.css.js";
import "../poi-layer/poi-layer.js";
import "../poi-group/poi-group.js";
import "../poi-data/poi-data.js";
import "../building-blocks/poi-header/poi-header.js";
import { isPoi, POI_ATTR, ObcPoiValue, PoiDataVisualRectPreference } from "../building-blocks/poi/poi.js";
import "../building-blocks/poi/poi-base.js";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result) __defProp(target, key, result);
  return result;
};
const JUMP_DURATION_MS = 100;
const JUMP_BEZIER = [0.2, 0, 0, 1];
var PoiLayerSelectionMode = /* @__PURE__ */ ((PoiLayerSelectionMode2) => {
  PoiLayerSelectionMode2["None"] = "none";
  PoiLayerSelectionMode2["Single"] = "single";
  PoiLayerSelectionMode2["Multi"] = "multi";
  return PoiLayerSelectionMode2;
})(PoiLayerSelectionMode || {});
let ObcPoiLayerStack = class extends LitElement {
  constructor() {
    super(...arguments);
    this.selectionMode = "none";
    this.handleStackClick = (event) => this.onStackClick(event);
    this.handleSlotChange = () => this.schedulePlacement();
    this.selectionMap = /* @__PURE__ */ new Map();
    this.selectionCounter = 0;
    this.selectionIds = /* @__PURE__ */ new WeakMap();
    this.movingTargets = /* @__PURE__ */ new Set();
    this.placementRaf = 0;
  }
  connectedCallback() {
    super.connectedCallback();
    this.addEventListener("click", this.handleStackClick);
  }
  firstUpdated() {
    const slot = this.shadowRoot?.querySelector("slot");
    slot?.addEventListener("slotchange", this.handleSlotChange);
    this.setupMutationObserver();
    this.schedulePlacement();
    requestAnimationFrame(() => {
      this.schedulePlacement();
      requestAnimationFrame(() => this.schedulePlacement());
    });
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    this.removeEventListener("click", this.handleStackClick);
    const slot = this.shadowRoot?.querySelector("slot");
    slot?.removeEventListener("slotchange", this.handleSlotChange);
    this.mutationObserver?.disconnect();
    this.mutationObserver = void 0;
    if (this.placementRaf) {
      cancelAnimationFrame(this.placementRaf);
      this.placementRaf = 0;
    }
    this.selectionMap.clear();
    this.movingTargets.clear();
    this.selectionCounter = 0;
    this.selectionIds = /* @__PURE__ */ new WeakMap();
  }
  onStackClick(event) {
    if (this.selectionMode === "none") return;
    const target = this.getPoiTargetFromEvent(event);
    if (!target) return;
    this.cleanupSelection();
    const originLayer = this.getTargetLayer(target);
    if (!originLayer) return;
    const selectedLayer = this.getLayer("selected") ?? this.getLayer("top");
    if (!selectedLayer) return;
    const existing = this.selectionMap.get(target);
    if (existing) {
      this.setSelectedTargetInteractivity(target, false);
      this.clearTargetGroupingAttributes(target);
      const currentLayer = this.getTargetLayer(target);
      if (existing.originLayer !== currentLayer) {
        this.clearTargetSelectedId(target);
        this.moveTargetToLayer(target, existing.originLayer, () => {
          this.animateTargetLineCompensation(target, 0, false);
        });
      } else {
        this.animateTargetLineCompensation(target, 0, true);
        this.clearTargetSelectedId(target);
      }
      this.selectionMap.delete(target);
      this.schedulePlacement();
      return;
    }
    if (this.selectionMode === "single") {
      this.clearOtherTopSelections(target);
      this.clearSelectionMapExcept(target);
    }
    const originLineLength = Number.isFinite(target.y) ? target.y : 0;
    this.selectionMap.set(target, {
      originLayer,
      originLineLength
    });
    this.clearTargetGroupingAttributes(target);
    this.setSelectedTargetInteractivity(target, true);
    if (selectedLayer !== originLayer) {
      this.moveTargetToLayer(target, selectedLayer, void 0, () => {
        if (target.isConnected) {
          this.setTargetSelectedId(target);
        }
      });
    } else {
      this.setTargetSelectedId(target);
    }
    this.schedulePlacement();
  }
  getPoiTargetFromEvent(event) {
    const path = event.composedPath?.() ?? [];
    for (const item of path) {
      if (item instanceof HTMLElement && isPoi(item)) {
        return item;
      }
    }
    const direct = event.target instanceof HTMLElement ? event.target : null;
    if (!direct) return null;
    const closest = direct.closest(`[${POI_ATTR}]`);
    return closest && isPoi(closest) ? closest : null;
  }
  getTargetLayer(target) {
    return target.closest("obc-poi-layer");
  }
  getLayerTargets(layer) {
    return Array.from(layer.querySelectorAll(`[${POI_ATTR}]`)).filter(
      (el) => isPoi(el)
    );
  }
  getAllLayers() {
    return Array.from(this.querySelectorAll("obc-poi-layer"));
  }
  isLayerSelected(layer) {
    return layer.isSelected === true || layer.hasAttribute("is-selected");
  }
  cleanupSelection() {
    this.selectionMap.forEach((_, target) => {
      if (!target.isConnected) this.selectionMap.delete(target);
    });
    this.movingTargets.forEach((target) => {
      if (!target.isConnected) this.movingTargets.delete(target);
    });
  }
  getLayer(kind) {
    const layers = this.getAllLayers();
    switch (kind) {
      case "top":
        return layers[layers.length - 1] ?? null;
      case "secondTop":
        return layers.length >= 2 ? layers[layers.length - 2] ?? null : null;
      case "selected":
        return layers.find((layer) => this.isLayerSelected(layer)) ?? null;
      case "default": {
        const nonSelected = layers.filter(
          (layer) => !this.isLayerSelected(layer)
        );
        return nonSelected[nonSelected.length - 1] ?? null;
      }
    }
  }
  clearOtherTopSelections(target) {
    const topLayer = this.getLayer("top");
    const activeLayer = this.getLayer("selected") ?? topLayer;
    if (!activeLayer) return;
    const fallbackLayer = this.getLayer("default") ?? (activeLayer === topLayer ? this.getLayer("secondTop") : null);
    const topTargets = this.getLayerTargets(activeLayer);
    topTargets.forEach((other) => {
      if (other === target) return;
      const record = this.selectionMap.get(other);
      this.resetSelectionForTarget(other, record, fallbackLayer);
    });
  }
  collectPoiHeaders(target) {
    const headers = /* @__PURE__ */ new Set();
    const visit = (node) => {
      if (node instanceof Element && node.matches("obc-poi-header")) {
        headers.add(node);
      }
      const children = node instanceof ShadowRoot || node instanceof Element ? Array.from(node.children) : [];
      for (const child of children) {
        if (child.matches("obc-poi-header")) {
          headers.add(child);
        }
        if (child.shadowRoot) {
          visit(child.shadowRoot);
        }
        visit(child);
      }
    };
    visit(target);
    if (target.shadowRoot) {
      visit(target.shadowRoot);
    }
    return Array.from(headers);
  }
  setTargetSelectedId(target) {
    if (target.hasHeader === void 0) return;
    const headers = this.collectPoiHeaders(target);
    const hasExternalHeader = headers.some(
      (header) => !header.hasAttribute("data-stack-header")
    );
    if (hasExternalHeader) {
      target.hasHeader = true;
      return;
    }
    const existing = this.selectionIds.get(target);
    const selectedId = existing ?? String(++this.selectionCounter);
    if (!existing) {
      this.selectionIds.set(target, selectedId);
    }
    if ("headerContent" in target) {
      target.headerContent = selectedId;
    }
    target.hasHeader = true;
  }
  clearTargetSelectedId(target) {
    const headers = this.collectPoiHeaders(target);
    headers.filter((header) => header.hasAttribute("data-stack-header")).forEach((header) => header.remove());
    if (target.hasHeader !== void 0) {
      target.hasHeader = false;
    }
    if ("headerContent" in target) {
      target.headerContent = "";
    }
  }
  clearTargetGroupingAttributes(target) {
    target.removeAttribute("data-grouped");
    target.removeAttribute("data-pregrouped");
    target.removeAttribute("data-behind");
    target.removeAttribute("data-front");
    target.removeAttribute("data-front-exit");
    target.removeAttribute("data-exiting");
    target.removeAttribute("data-exit-lock");
  }
  setSelectedTargetInteractivity(target, selected) {
    const isInAutoGroup = target.hasAttribute("data-grouped") || target.closest("obc-poi-group") !== null;
    target.selected = selected;
    if (selected) {
      target.style.setProperty("--obc-poi-overlap-pointer-events", "auto");
      if (!isInAutoGroup) {
        target.value = ObcPoiValue.Checked;
        target.removeAttribute("data-behind");
        target.setAttribute("data-front", "true");
      }
    } else {
      target.value = ObcPoiValue.Unchecked;
      target.style.removeProperty("--obc-poi-overlap-pointer-events");
      target.removeAttribute("data-front");
    }
  }
  resetSelectionForTarget(target, record, fallbackLayer) {
    this.setSelectedTargetInteractivity(target, false);
    this.clearTargetGroupingAttributes(target);
    const currentLayer = this.getTargetLayer(target);
    if (record) {
      if (record.originLayer !== currentLayer) {
        this.clearTargetSelectedId(target);
        this.moveTargetToLayer(target, record.originLayer, () => {
          this.animateTargetLineCompensation(target, 0, false);
        });
      } else {
        this.animateTargetLineCompensation(target, 0, true);
        this.clearTargetSelectedId(target);
      }
      this.selectionMap.delete(target);
      return;
    }
    if (fallbackLayer && fallbackLayer !== currentLayer) {
      this.clearTargetSelectedId(target);
      this.moveTargetToLayer(target, fallbackLayer);
      return;
    }
    this.clearTargetSelectedId(target);
  }
  clearSelectionMapExcept(target) {
    const entries = Array.from(this.selectionMap.entries());
    entries.forEach(([other, record]) => {
      if (other === target) return;
      this.resetSelectionForTarget(other, record);
    });
  }
  setupMutationObserver() {
    this.mutationObserver?.disconnect();
    this.mutationObserver = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (mutation.type === "attributes" && mutation.target instanceof HTMLElement) {
          if (mutation.target.tagName.toLowerCase() === "obc-poi-layer" || mutation.attributeName === POI_ATTR) {
            this.schedulePlacement();
            return;
          }
        }
        for (const node of Array.from(mutation.addedNodes)) {
          if (!(node instanceof HTMLElement)) continue;
          if (isPoi(node) || node.querySelector?.(`[${POI_ATTR}]`)) {
            this.schedulePlacement();
            return;
          }
        }
      }
    });
    this.mutationObserver.observe(this, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ["is-selected", POI_ATTR]
    });
  }
  moveTargetToLayer(target, nextLayer, afterMove, beforeReveal) {
    const currentParent = target.parentElement;
    if (!currentParent || currentParent === nextLayer) {
      afterMove?.();
      return;
    }
    this.movingTargets.add(target);
    const finalizeMove = () => {
      target.style.removeProperty("--obc-poi-moving-line-opacity");
      target.style.removeProperty("--obc-poi-moving-pointer-opacity");
      target.style.removeProperty("--obc-poi-moving-outside-arrow-opacity");
      target.style.removeProperty("--obc-poi-position-transition-duration");
      target.style.removeProperty("--obc-poi-transition-duration");
      target.style.removeProperty("--obc-poi-opacity-transition-duration");
      target.style.removeProperty("--obc-poi-size-transition-duration");
      target.style.removeProperty("--obc-poi-color-transition-duration");
      this.movingTargets.delete(target);
      afterMove?.();
    };
    const reduceMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
    let beforeRevealCalled = false;
    const runBeforeReveal = () => {
      if (beforeRevealCalled) return;
      beforeRevealCalled = true;
      beforeReveal?.();
    };
    const wasInGroup = currentParent.tagName.toLowerCase() === "obc-poi-group";
    const firstVisualAnchor = this.getTargetVisualAnchor(target);
    const firstTargetAnchor = this.getTargetPointAnchor(target);
    const previousVisibility = target.style.visibility;
    target.style.visibility = "hidden";
    target.style.setProperty("--obc-poi-moving-line-opacity", "1");
    target.style.setProperty("--obc-poi-moving-pointer-opacity", "0");
    target.style.setProperty("--obc-poi-moving-outside-arrow-opacity", "0");
    target.style.setProperty("--obc-poi-position-transition-duration", "0s");
    target.style.setProperty("--obc-poi-transition-duration", "0s");
    target.style.setProperty("--obc-poi-opacity-transition-duration", "0s");
    target.style.setProperty("--obc-poi-size-transition-duration", "0s");
    target.style.setProperty("--obc-poi-color-transition-duration", "0s");
    const restoreVisibility = () => {
      if (previousVisibility) {
        target.style.visibility = previousVisibility;
      } else {
        target.style.removeProperty("visibility");
      }
    };
    nextLayer.appendChild(target);
    if (wasInGroup) {
      const group = currentParent;
      const groupLayer = group.closest("obc-poi-layer");
      if (groupLayer && groupLayer !== nextLayer) {
        group.expand = false;
      }
      target.buttonOffsetX = 0;
      target.targetOffsetX = 0;
      target.style.removeProperty("position");
      target.style.removeProperty("width");
      target.style.removeProperty("min-width");
      target.style.removeProperty("height");
      target.style.removeProperty("transform");
    }
    const immediateTargetAnchor = this.getTargetPointAnchor(target);
    const lineDy = firstTargetAnchor.y - immediateTargetAnchor.y;
    const hasLineDelta = Number.isFinite(lineDy) && Math.abs(lineDy) >= 0.5;
    if (hasLineDelta) {
      this.adjustTargetLineLengthByOffset(target, lineDy, false);
    }
    nextLayer.requestGroupingUpdate();
    const startSettledMove = (lastVisualAnchor, hasVisualDelta) => {
      if (reduceMotion) {
        runBeforeReveal();
        restoreVisibility();
        nextLayer.requestGroupingUpdate();
        finalizeMove();
        return;
      }
      if (!hasVisualDelta && !hasLineDelta) {
        runBeforeReveal();
        restoreVisibility();
        nextLayer.requestGroupingUpdate();
        finalizeMove();
        return;
      }
      if (!hasVisualDelta) {
        runBeforeReveal();
        restoreVisibility();
        nextLayer.requestGroupingUpdate();
        finalizeMove();
        return;
      }
      const dx = firstVisualAnchor.x - lastVisualAnchor.x;
      const dy = firstVisualAnchor.y - lastVisualAnchor.y;
      const baseTransform = getComputedStyle(target).transform;
      const startTransform = baseTransform === "none" ? `translate(${dx}px, ${dy}px)` : `translate(${dx}px, ${dy}px) ${baseTransform}`;
      target.style.transform = startTransform;
      target.style.willChange = "transform";
      runBeforeReveal();
      restoreVisibility();
      const duration = JUMP_DURATION_MS;
      const [x1, y1, x2, y2] = JUMP_BEZIER;
      const easing = `cubic-bezier(${x1}, ${y1}, ${x2}, ${y2})`;
      const animation = target.animate(
        [
          { transform: startTransform },
          { transform: baseTransform === "none" ? "none" : baseTransform }
        ],
        { duration, easing }
      );
      const completeMove = () => {
        target.style.willChange = "";
        target.style.removeProperty("transform");
        nextLayer.requestGroupingUpdate();
        finalizeMove();
      };
      animation.addEventListener("finish", completeMove, { once: true });
      animation.addEventListener("cancel", completeMove, { once: true });
    };
    const waitForStableVisualAnchor = (remainingFrames, previousAnchor) => {
      requestAnimationFrame(() => {
        if (!target.isConnected || this.getTargetLayer(target) !== nextLayer) {
          restoreVisibility();
          finalizeMove();
          return;
        }
        const lastVisualAnchor = this.getTargetVisualAnchor(target);
        const dx = firstVisualAnchor.x - lastVisualAnchor.x;
        const dy = firstVisualAnchor.y - lastVisualAnchor.y;
        const hasVisualDelta = Number.isFinite(dx) && Number.isFinite(dy) && (Math.abs(dx) >= 0.5 || Math.abs(dy) >= 0.5);
        if (previousAnchor && Math.abs(lastVisualAnchor.x - previousAnchor.x) < 0.5 && Math.abs(lastVisualAnchor.y - previousAnchor.y) < 0.5) {
          startSettledMove(lastVisualAnchor, hasVisualDelta);
          return;
        }
        if (remainingFrames <= 0) {
          startSettledMove(lastVisualAnchor, hasVisualDelta);
          return;
        }
        waitForStableVisualAnchor(remainingFrames - 1, lastVisualAnchor);
      });
    };
    requestAnimationFrame(() => {
      waitForStableVisualAnchor(2);
    });
  }
  getRectBottomCenter(rect) {
    return { x: rect.left + rect.width / 2, y: rect.bottom };
  }
  getTargetVisualAnchor(target) {
    const rect = target.getVisualRect(PoiDataVisualRectPreference.Anchor);
    return this.getRectBottomCenter(rect);
  }
  getTargetPointAnchor(target) {
    const pointer = target.getPointerElement();
    if (pointer) {
      return this.getRectBottomCenter(pointer.getBoundingClientRect());
    }
    return this.getTargetVisualAnchor(target);
  }
  schedulePlacement() {
    if (this.placementRaf) return;
    this.placementRaf = requestAnimationFrame(() => {
      this.placementRaf = 0;
      this.updateLayerOrders();
      this.syncSelectedLayerTargets();
    });
  }
  updateLayerInactiveState(selectedLayer) {
    const layers = this.getAllLayers();
    layers.forEach((layer) => {
      if (selectedLayer && layer !== selectedLayer) {
        layer.setAttribute("data-stack-inactive", "true");
      } else {
        layer.removeAttribute("data-stack-inactive");
      }
    });
  }
  updateLayerOrders() {
    const layers = this.getAllLayers();
    layers.forEach((layer, index) => {
      layer.style.order = String(index);
    });
    const selectedLayer = this.getLayer("selected");
    this.updateLayerInactiveState(selectedLayer);
  }
  syncSelectedLayerTargets() {
    const selectedLayer = this.getLayer("selected");
    this.updateLayerInactiveState(selectedLayer);
    const allTargets = this.getAllLayers().flatMap(
      (layer) => this.getLayerTargets(layer)
    );
    if (!selectedLayer) {
      allTargets.forEach((target) => {
        if (this.movingTargets.has(target)) {
          return;
        }
        this.setSelectedTargetInteractivity(target, false);
        this.clearTargetSelectedId(target);
        this.selectionMap.delete(target);
      });
      return;
    }
    allTargets.forEach((target) => {
      if (this.movingTargets.has(target)) {
        return;
      }
      const currentLayer = this.getTargetLayer(target);
      const isInSelectedLayer = currentLayer === selectedLayer;
      if (!isInSelectedLayer) {
        this.setSelectedTargetInteractivity(target, false);
        this.clearTargetSelectedId(target);
        this.selectionMap.delete(target);
        return;
      }
      this.setTargetSelectedId(target);
      this.setSelectedTargetInteractivity(target, true);
      if (this.selectionMode === "none") {
        return;
      }
      if (this.selectionMap.has(target)) return;
      if (this.selectionMode === "single" && this.selectionMap.size > 0) {
        return;
      }
      const fallbackOrigin = this.getLayer("default");
      const originLayer = fallbackOrigin && fallbackOrigin !== selectedLayer ? fallbackOrigin : currentLayer;
      if (!originLayer) return;
      const originLineLength = Number.isFinite(target.y) ? target.y : 0;
      this.selectionMap.set(target, {
        originLayer,
        originLineLength
      });
      this.syncInitialSelectedTargetLineCompensation(
        target,
        originLayer,
        selectedLayer
      );
    });
  }
  syncInitialSelectedTargetLineCompensation(target, originLayer, selectedLayer) {
    if (originLayer === selectedLayer) {
      return;
    }
    const originRect = originLayer.getBoundingClientRect();
    const selectedRect = selectedLayer.getBoundingClientRect();
    const layerBottomDelta = originRect.bottom - selectedRect.bottom;
    if (!Number.isFinite(layerBottomDelta) || Math.abs(layerBottomDelta) < 0.5) {
      return;
    }
    const expectedCompensation = target.fixedTarget ? -layerBottomDelta : layerBottomDelta;
    const currentCompensation = this.getTargetLineCompensation(target);
    if (Math.abs(expectedCompensation - currentCompensation) < 0.5) {
      return;
    }
    this.animateTargetLineCompensation(target, expectedCompensation, false);
  }
  getTargetLineCompensation(target) {
    const compensation = target.lineCompensationY;
    return Number.isFinite(compensation) ? compensation : 0;
  }
  animateTargetLineCompensation(target, targetCompensation, animate, startTimeOverride) {
    if (!animate) {
      target.lineCompensationY = targetCompensation;
      return;
    }
    const duration = JUMP_DURATION_MS;
    const [x1, y1, x2, y2] = JUMP_BEZIER;
    const startTime = startTimeOverride ?? performance.now();
    const startCompensation = this.getTargetLineCompensation(target);
    const animateLineLength = (now) => {
      try {
        const elapsed = now - startTime;
        const progress = Math.min(elapsed / duration, 1);
        const eased = this.evaluateCubicBezier(x1, y1, x2, y2, progress);
        const nextCompensation = startCompensation + (targetCompensation - startCompensation) * eased;
        target.lineCompensationY = nextCompensation;
        if (progress < 1) {
          requestAnimationFrame(animateLineLength);
        }
      } catch (error) {
        console.error(
          "[poi-layer-stack] Error in line length animation:",
          error
        );
      }
    };
    requestAnimationFrame(animateLineLength);
  }
  adjustTargetLineLengthByOffset(target, offset, animate = true, startTimeOverride) {
    if (!Number.isFinite(offset) || Math.abs(offset) < 0.5) return;
    const currentCompensation = this.getTargetLineCompensation(target);
    const targetCompensation = target.fixedTarget ? currentCompensation - offset : currentCompensation + offset;
    this.animateTargetLineCompensation(
      target,
      targetCompensation,
      animate,
      startTimeOverride
    );
  }
  evaluateCubicBezier(x1, y1, x2, y2, t) {
    const cx = 3 * x1;
    const bx = 3 * (x2 - x1) - cx;
    const ax = 1 - cx - bx;
    let tParam = t;
    for (let i = 0; i < 4; i++) {
      const x = ((ax * tParam + bx) * tParam + cx) * tParam;
      const dx = (3 * ax * tParam + 2 * bx) * tParam + cx;
      if (Math.abs(dx) < 1e-6) break;
      tParam -= (x - t) / dx;
    }
    const cy = 3 * y1;
    const by = 3 * (y2 - y1) - cy;
    const ay = 1 - cy - by;
    return ((ay * tParam + by) * tParam + cy) * tParam;
  }
  render() {
    return html`
      <div class="wrapper">
        <slot></slot>
      </div>
    `;
  }
};
ObcPoiLayerStack.styles = unsafeCSS(componentStyle);
__decorateClass([
  property({ type: String, attribute: "selection-mode" })
], ObcPoiLayerStack.prototype, "selectionMode", 2);
ObcPoiLayerStack = __decorateClass([
  customElement("obc-poi-layer-stack")
], ObcPoiLayerStack);
export {
  ObcPoiLayerStack,
  PoiLayerSelectionMode
};
//# sourceMappingURL=poi-layer-stack.js.map
