/* COPYRIGHT Esri - https://js.arcgis.com/5.2/LICENSE.txt */
import { c as S } from "../../chunks/runtime.js";
import { css as M, html as m } from "lit";
import { keyed as C } from "lit/directives/keyed.js";
import { i as k, q as D } from "../../chunks/layer-utils.js";
import { createReactiveMap as I, findUtilityNetwork as V, getOrCreateMapValue as j } from "@arcgis/core/applications/Components/featureUtils.js";
import { stripHTML as v } from "@arcgis/core/applications/Components/stringUtils.js";
import w from "@arcgis/core/core/Collection.js";
import { ignoreAbortErrors as E } from "@arcgis/core/core/promiseUtils.js";
import { watch as A } from "@arcgis/core/core/reactiveUtils.js";
import * as x from "@arcgis/core/core/sql.js";
import P from "@arcgis/core/rest/networks/support/NetworkElement.js";
import U from "@arcgis/core/rest/networks/support/QueryAssociationsParameters.js";
import Q from "@arcgis/core/rest/support/Query.js";
import { LitElement as B, renderElement as q } from "@arcgis/lumina";
import { debounce as T } from "@arcgis/toolkit/function";
import G from "@arcgis/core/popup/Feature.js";
const R = M`.list-container{min-height:var(--calcite-spacing-xxxl)}.loading-container{display:flex;justify-content:center;padding:var(--calcite-spacing-md) 0;width:100%}.sticky-loading-container{display:flex;position:sticky;bottom:var(--calcite-spacing-sm);align-items:center;justify-content:center;z-index:2;margin:0;padding:0;height:var(--calcite-spacing-xxxl);pointer-events:none;height:500px}`, L = 100, $ = {
  assetGroup: "assetgroup",
  assetType: "assettype"
}, N = "{00000000-0000-0000-0000-000000000000}";
class O extends B {
  constructor() {
    super(...arguments), this._addFlowItem = (t) => {
      t && this.flowItems?.push(t);
    }, this._attachmentAssociations = new w(), this._attachmentFeatureCount = 0, this._cancelQuery = () => {
      const { _queryAbortController: t } = this;
      t && t.abort(), this._queryAbortController = null;
    }, this._cancelQueryFeatureCount = () => {
      const { _queryFeatureCountAbortController: t } = this;
      t && t.abort(), this._queryFeatureCountAbortController = null;
    }, this._connectivityAssociations = new w(), this._connectivityFeatureCount = 0, this._containerAssociations = new w(), this._containerFeatureCount = 0, this._contentAssociations = new w(), this._contentFeatureCount = 0, this._networkSourceIdsInUse = /* @__PURE__ */ new Set(), this._query = async (t) => {
      if (!t)
        return;
      await this._loadUtiltyNetworks();
      const { _queryAbortController: e } = this;
      this._destroyAssociatedFeatureViewModels(), this._clearFeatures(), this._featureCount && await this._queryAssociatedFeatures(t, { signal: e?.signal });
    }, this._queryController = async (t) => {
      this._cancelQuery();
      const e = new AbortController();
      this._queryAbortController = e, await E(this._query(t)), this._queryAbortController === e && (this._queryAbortController = null);
    }, this._queryFeatureCountController = async () => {
      this._loaded = !1, this._cancelQueryFeatureCount();
      const t = new AbortController();
      this._queryFeatureCountAbortController = t, await E(this._queryFeatureCount()), this._queryFeatureCountAbortController === t && (this._queryFeatureCountAbortController = null), this._loaded = !0;
    }, this._queryDebounced = T(this._queryController, L), this._queryFeatureCountDebounced = T(this._queryFeatureCountController, L), this._structureAssociations = new w(), this._structureFeatureCount = 0, this._queryAssociationType = async () => {
      this._activeAssociationType && this._queryDebounced(this._activeAssociationType);
    }, this._associationFeatures = I(), this._associationViewModels = I(), this._featureCount = 0, this._loaded = !1, this._queryAbortController = null, this._queryFeatureCountAbortController = null, this._queryPageAbortController = null, this.associationTypes = null, this.autoDestroyDisabled = !1, this.useFeatureNextFlowItem = !1, this.flowType = "feature-utility-network-associations", this.headingLevel = 2, this.source = "popup";
  }
  static {
    this.properties = { _activeAssociationType: 16, _associationFeatures: 16, _associationViewModels: 16, _canLoad: 16, _canQuery: 16, _featureCount: 16, _loaded: 16, _queryAbortController: 16, _queryFeatureCountAbortController: 16, _queryPageAbortController: 16, _state: 16, associationTypes: 0, autoDestroyDisabled: 5, description: 1, flowItems: 0, useFeatureNextFlowItem: 5, flowType: 1, graphic: 0, heading: 1, headingLevel: 9, layer: 0, map: 0, messages: 0, parentFeatureViewModel: 0, referenceElement: 1, source: 1, spatialReference: 0, timeZone: 1, view: 0 };
  }
  static {
    this.styles = R;
  }
  get _globalId() {
    return (this._globalIdField && this.graphic?.attributes?.[this._globalIdField]) ?? null;
  }
  get _globalIdField() {
    return this.layer?.globalIdField;
  }
  get _objectId() {
    return (this._objectIdField && this.graphic?.attributes?.[this._objectIdField]) ?? null;
  }
  get _objectIdField() {
    return this.layer?.objectIdField;
  }
  get _supportsCacheHint() {
    return !!this.layer?.capabilities?.queryRelated?.supportsCacheHint;
  }
  get _utilityNetwork() {
    const { layer: t, map: e } = this;
    if (!t?.loaded || !e)
      return null;
    const s = k(t) ? t.parent : t;
    return V(e, s);
  }
  get _canLoad() {
    return !!this.map;
  }
  get _canQuery() {
    return !!this.layer?.capabilities?.query?.supportsPagination;
  }
  get _state() {
    const { _canLoad: t, _canQuery: e, _featureCount: s, _loaded: o, _queryAbortController: i, _queryFeatureCountAbortController: a, _queryPageAbortController: u, associationTypes: n } = this;
    return a || t && !o ? "loading" : i || u ? "querying" : !this._globalId || !n || !e || !s ? "disabled" : "ready";
  }
  get heading() {
    return this._heading;
  }
  set heading(t) {
    this._heading = t;
  }
  async destroy() {
    await this.manager.destroy();
  }
  loaded() {
    this.manager.onLifecycle(() => [
      A(() => [
        this.graphic,
        this.layer,
        this.map,
        this.associationTypes,
        this._canQuery,
        this._objectId,
        this._globalId
      ], () => {
        this._queryFeatureCountDebounced(), this._queryAssociationType();
      }, { initial: !0 }),
      A(() => this._activeAssociationType, (t) => {
        this._queryDebounced(t);
      }),
      A(() => this._associationFeatures.values(), () => {
        this._setUpUtilityNetworkAssociationsViewModels();
      })
    ]), this.manager.onDestroy(() => {
      this._cancelQuery(), this._cancelQueryFeatureCount();
    });
  }
  _addAssociationTypeFlowItem(t) {
    const e = this.useFeatureNextFlowItem ? q(m`<arcgis-feature-utility-network-association-list .autoDestroyDisabled=${this.autoDestroyDisabled} .associationViewModels=${this._associationViewModels} .description=${this._getParentFeatureDescription()} .flowItems=${this.flowItems} .flowType=${this.flowType} .heading=${this._getAssociationTypeTitle(t)} .messages=${this.messages} .map=${this.map} .parentFeatureViewModel=${this.parentFeatureViewModel} .referenceElement=${this.referenceElement} .state=${this._state} .spatialReference=${this.spatialReference} .timeZone=${this.timeZone} use-feature-next-flow-item .view=${this.view}></arcgis-feature-utility-network-association-list>`) : q(m`<arcgis-feature-utility-network-association-list .autoDestroyDisabled=${this.autoDestroyDisabled} .associationViewModels=${this._associationViewModels} .description=${this._getParentFeatureDescription()} .flowItems=${this.flowItems} .flowType=${this.flowType} .heading=${this._getAssociationTypeTitle(t)} .messages=${this.messages} .map=${this.map} .parentFeatureViewModel=${this.parentFeatureViewModel} .referenceElement=${this.referenceElement} .state=${this._state} .spatialReference=${this.spatialReference} .timeZone=${this.timeZone} .view=${this.view}></arcgis-feature-utility-network-association-list>`);
    this._addFlowItem(e);
  }
  _clearAssociations() {
    this._attachmentAssociations.removeAll(), this._connectivityAssociations.removeAll(), this._containerAssociations.removeAll(), this._contentAssociations.removeAll(), this._structureAssociations.removeAll();
  }
  _clearFeatures() {
    this._associationFeatures.forEach((t) => t.removeAll()), this._associationFeatures.clear();
  }
  _compareByFeatureTitle(t, e) {
    return t.title.localeCompare(e.title, void 0, {
      numeric: !0
    });
  }
  async _countAssociatedFeatureCount(t, e, s, o, i) {
    const a = this._getFeatureQueryWhereClause(t, e, s, o);
    return await t.queryFeatureCount({
      where: a,
      outFields: ["*"],
      returnGeometry: !1
    }, {
      signal: i?.signal
    });
  }
  async _countAssociatedFeatures(t, e, s, o, i) {
    if (!e.length)
      return 0;
    const a = await this._findLayersBySourceId(t);
    await this._loadLayers(a);
    const u = a.map((c) => this._countAssociatedFeatureCount(c, e, s, o, i));
    return (await Promise.all(u)).reduce((c, d) => c + d, 0);
  }
  async _createAssociationFeatureObjects(t, e, s, o, i) {
    if (!t.length)
      return [];
    const a = /* @__PURE__ */ new Map(), u = Array.from(e.entries()).map(async ([c, d]) => {
      const l = await this._findLayersBySourceId(c);
      await this._loadLayers(l);
      const y = l.map((r) => this._queryLayer(r, d, s, o, i));
      (await Promise.all(y)).forEach((r) => {
        r.forEach((h) => {
          if (this.source === "popup" ? h.sourceLayer && h.getEffectivePopupTemplate() : !!h.sourceLayer) {
            const f = h.layer.globalIdField ?? "", g = h.getAttribute(f), F = a.get(g) ?? [];
            F.push(h), a.set(g, F);
          }
        });
      });
    });
    await Promise.all(u);
    const n = [];
    return await Promise.all(t.toArray().map(async (c) => {
      const { fromNetworkElement: d, toNetworkElement: l } = c, y = d.globalId === this._globalId ? l : d, p = a.get(y.globalId) ?? [];
      await Promise.all(p.map(async (r) => {
        const h = this._utilityNetwork?.getTerminalById(y?.terminalId ?? void 0)?.name, _ = await r.sourceLayer.getFeatureTitle(r) || this._getFeatureTitle(r);
        n.push({
          title: _,
          feature: r,
          association: c,
          terminalName: h
        });
      }));
    })), n;
  }
  _destroyAssociatedFeatureViewModels() {
    this._associationViewModels.forEach((t) => t.destroyAll());
  }
  _equals(t, e) {
    if (t.globalId === N && e.globalId === N) {
      let s = function(d, l) {
        return d.networkSourceId === l.networkSourceId && d.globalId === l.globalId && d.terminalId === l.terminalId && d.firstUnit === l.firstUnit && d.numUnits === l.numUnits;
      };
      const o = t.fromNetworkElement, i = t.toNetworkElement, a = e.fromNetworkElement, u = e.toNetworkElement, n = s(o, a), c = s(i, u);
      return n && c && t.associationType === e.associationType;
    }
    return t.globalId != null && e.globalId != null && t.globalId === e.globalId;
  }
  async _findLayersBySourceId(t) {
    const { _utilityNetwork: e, map: s } = this;
    e?.loaded || await e?.load();
    const o = e.getLayerIdBySourceId(t), i = s.allLayers.filter((n) => !!e?.isUtilityLayer(n) && n.layerId === o), a = s.allTables.filter((n) => !!e?.isUtilityLayer(n) && n.layerId === o);
    return i.concat(a).toArray();
  }
  _getAssociationsByType(t) {
    switch (t) {
      case "attachment":
        return this._attachmentAssociations;
      case "structure":
        return this._structureAssociations;
      case "connectivity":
        return this._connectivityAssociations;
      case "container":
        return this._containerAssociations;
      case "content":
        return this._contentAssociations;
    }
  }
  _getAssociationTypeTitle(t) {
    const { messages: e } = this;
    if (t.title)
      return t.title;
    switch (t.type) {
      case "attachment":
        return e?.associationsAttachments ?? this.messages?.noTitle ?? "";
      case "connectivity":
        return e?.associationsConnectivity ?? this.messages?.noTitle ?? "";
      case "structure":
        return e?.associationsStructure ?? this.messages?.noTitle ?? "";
      case "content":
        return e?.associationsContents ?? this.messages?.noTitle ?? "";
      case "container":
        return e?.associationsContainer ?? this.messages?.noTitle ?? "";
    }
  }
  _getFeatureCountForAssociationType(t) {
    switch (t) {
      case "attachment":
        return this._attachmentFeatureCount;
      case "structure":
        return this._structureFeatureCount;
      case "content":
        return this._contentFeatureCount;
      case "container":
        return this._containerFeatureCount;
      case "connectivity":
        return this._connectivityFeatureCount;
    }
  }
  _getFeatureQueryWhereClause(t, e, s, o) {
    const i = t.globalIdField, a = t.fieldsIndex.get($.assetGroup), u = t.fieldsIndex.get($.assetType), n = s != null, c = o != null, d = i ? x.sqlIn(i, e) : null, l = n ? `(${a?.name} = ${s})` : null, y = n && c ? `(${u?.name} = ${o})` : null;
    return [d, l, y].filter(Boolean).join(" AND ");
  }
  _getFeatureTitle(t) {
    const e = t.attributes, s = t.sourceLayer;
    if (!e || !s)
      return "";
    const o = "displayField" in s ? s.displayField : null, i = o != null ? e[o.toString()] : null, a = i != null ? i.toString() : null, u = t.getObjectId()?.toString();
    return a || u || "";
  }
  _getParentFeatureDescription() {
    const { parentFeatureViewModel: t } = this;
    if (t) {
      if ("heading" in t)
        return t.heading;
      if ("title" in t)
        return t.title;
    }
  }
  async _loadUtiltyNetworks() {
    const t = this.map;
    if (!t)
      return;
    t.utilityNetworks && await Promise.allSettled(t.utilityNetworks.map((s) => s.load()));
    const e = this._utilityNetwork;
    if (e) {
      const s = (o) => {
        if ("layerId" in o && e.isUtilityLayer(o)) {
          const i = o.layerId != null ? e.getSourceIdByLayerId(o.layerId) : null;
          i != null && this._networkSourceIdsInUse.add(i);
        }
      };
      this._networkSourceIdsInUse = /* @__PURE__ */ new Set(), t.allLayers.forEach(s), t.allTables.forEach(s);
    }
  }
  async _loadLayers(t) {
    const e = t.filter((s) => !s.loaded).map((s) => s.load());
    e.length && await Promise.allSettled(e);
  }
  _matchesGlobalFeature(t, e) {
    return this._equals(t.association, e.association);
  }
  async _queryAssociatedFeatures(t, e) {
    const { layer: s, _globalId: o, associationTypes: i, _utilityNetwork: a, _canQuery: u, _associationFeatures: n } = this;
    if (await Promise.allSettled([s?.load(), a?.load()]), !u || !s || !i || !a)
      return;
    const c = this._getAssociationsByType(t.type), { associatedAssetGroup: d, associatedAssetType: l } = t, y = /* @__PURE__ */ new Map();
    c.forEach((r) => {
      const { fromNetworkElement: h, toNetworkElement: _ } = r, { networkSourceId: f, elementGlobalId: g } = h.globalId === o ? {
        networkSourceId: _.networkSourceId,
        elementGlobalId: _.globalId
      } : {
        networkSourceId: h.networkSourceId,
        elementGlobalId: h.globalId
      }, F = y.get(f) || [];
      F.push(g), y.set(f, F);
    });
    const p = await this._createAssociationFeatureObjects(c, y, d, l, e);
    this._parseFeatureObjects(p, n);
  }
  async _queryAssociations(t) {
    const { layer: e, _globalId: s, associationTypes: o, _utilityNetwork: i, _canQuery: a } = this;
    if (await Promise.allSettled([e?.load(), i?.load()]), this._clearAssociations(), !a || !e || !o || !i || !s)
      return;
    const u = k(e) ? e.parent : e, n = new P({
      globalId: s,
      networkSourceId: i.getSourceIdByLayerId(u.layerId)
    }), c = /* @__PURE__ */ new Set();
    o.forEach((r) => {
      switch (r.type) {
        case "attachment":
        case "structure":
          c.add("attachment");
          break;
        case "container":
        case "content":
          c.add("containment");
          break;
        case "connectivity":
          c.add("connectivity"), c.add("junction-junction-connectivity"), c.add("junction-edge-from-connectivity"), c.add("junction-edge-midspan-connectivity"), c.add("junction-edge-to-connectivity");
          break;
      }
    });
    const d = await i?.queryAssociations(new U({
      elements: [n],
      types: Array.from(c)
    }), {
      signal: t?.signal
    }), l = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map();
    o.forEach((r) => {
      y.set(r.type, r), l.set(r.type, []);
    }), d.forEach((r) => {
      const { toNetworkElement: h, fromNetworkElement: _ } = r;
      switch (r.associationType) {
        case "attachment":
          if (_?.globalId === s) {
            if (this._shouldDiscardNetworkElement(h, "attachment", y))
              break;
            l.get("attachment")?.push(h.globalId), this._attachmentAssociations.add(r);
          } else {
            if (this._shouldDiscardNetworkElement(_, "structure", y))
              break;
            l.get("structure")?.push(_.globalId), this._structureAssociations.add(r);
          }
          break;
        case "connectivity":
        case "junction-edge-from-connectivity":
        case "junction-edge-midspan-connectivity":
        case "junction-edge-to-connectivity":
        case "junction-junction-connectivity":
          if (_?.globalId === s) {
            if (this._shouldDiscardNetworkElement(h, "connectivity", y))
              break;
            l.get("connectivity")?.push(h.globalId);
          } else {
            if (this._shouldDiscardNetworkElement(_, "connectivity", y))
              break;
            l.get("connectivity")?.push(_.globalId);
          }
          this._connectivityAssociations.add(r);
          break;
        case "containment":
          if (_?.globalId === s) {
            if (this._shouldDiscardNetworkElement(h, "content", y))
              break;
            l.get("content")?.push(h.globalId), this._contentAssociations.add(r);
          } else {
            if (this._shouldDiscardNetworkElement(_, "container", y))
              break;
            l.get("container")?.push(_.globalId), this._containerAssociations.add(r);
          }
          break;
      }
    });
    const p = o.map(async (r) => {
      const { associatedNetworkSourceId: h, associatedAssetGroup: _, associatedAssetType: f } = r, g = l.get(r.type), b = _ != null ? await this._countAssociatedFeatures(h, g, _, f, t) : g.length;
      switch (r.type) {
        case "attachment":
          this._attachmentFeatureCount = b;
          break;
        case "connectivity":
          this._connectivityFeatureCount = b;
          break;
        case "container":
          this._containerFeatureCount = b;
          break;
        case "content":
          this._contentFeatureCount = b;
          break;
        case "structure":
          this._structureFeatureCount = b;
          break;
      }
    });
    await Promise.allSettled(p);
  }
  async _queryFeatureCount() {
    await this._loadUtiltyNetworks();
    const { _queryFeatureCountAbortController: t, _canQuery: e } = this;
    if (!e) {
      this._featureCount = 0;
      return;
    }
    await this._queryAssociations(t), this._featureCount = this._attachmentFeatureCount + this._connectivityFeatureCount + this._containerFeatureCount + this._contentFeatureCount + this._structureFeatureCount;
  }
  async _queryLayer(t, e, s, o, i) {
    const a = this._getFeatureQueryWhereClause(t, e, s, o), u = new Q({
      where: a,
      outFields: ["*"],
      cacheHint: this._supportsCacheHint
    }), { features: n } = await D(t, u, i);
    return n;
  }
  _parseFeatureObjects(t, e) {
    const s = I();
    t.forEach((o) => {
      const a = (o?.feature).sourceLayer;
      j(s, a, () => new w()).add(o);
    });
    for (const [o, i] of s)
      this._sortFeatureObjectsByTitle(i), e.set(o, i);
  }
  _selectAssociationType(t) {
    const { flowItems: e } = this;
    e && (this._activeAssociationType = t, this._setUpUtilityNetworkAssociationsViewModels(), this._addAssociationTypeFlowItem(t));
  }
  _setUpUtilityNetworkAssociationsViewModels() {
    this._associationViewModels.forEach((i, a) => {
      const u = this._associationFeatures.get(a);
      u ? i.forEach((n) => {
        u.find((d) => this._matchesGlobalFeature(n, d)) || (i.remove(n), i.length || this._associationViewModels.delete(a));
      }) : (i.removeAll(), this._associationViewModels.delete(a));
    });
    const { map: t, view: e, spatialReference: s, timeZone: o } = this;
    this._associationFeatures.forEach((i, a) => {
      const u = this._associationViewModels.get(a) || new w();
      i?.filter(Boolean).forEach((n, c) => {
        if (!u.some((l) => this._matchesGlobalFeature(l, n))) {
          const { association: l, feature: y, terminalName: p, title: r } = n;
          u.add({
            title: r,
            association: l,
            featureViewModel: new G({
              abilities: { utilityNetworkAssociationsContent: !1 },
              map: t,
              view: e,
              spatialReference: s,
              timeZone: o,
              graphic: y
            }),
            terminalName: p
          }, c);
        }
      }), this._associationViewModels.set(a, u);
    });
  }
  _shouldDiscardNetworkElement(t, e, s) {
    if (!t)
      return !1;
    const { networkSourceId: o } = t, i = s.get(e)?.associatedNetworkSourceId, a = this._networkSourceIdsInUse.has(o);
    return i != null && i !== o || !a;
  }
  _sortFeatureObjectsByTitle(t) {
    t.sort(this._compareByFeatureTitle);
  }
  render() {
    const { _state: t } = this;
    return m`<div class="root"><arcgis-feature-element-info .heading=${this.heading} .headingLevel=${this.headingLevel} .description=${this.description}></arcgis-feature-element-info>${t === "loading" ? this._renderLoading() : t === "disabled" ? this._renderAssociationNotFound() : this._renderContent()}</div>`;
  }
  _renderAssociationNotFound() {
    return m`<calcite-notice icon=information kind=info open scale=s width=full><div slot=message>${this.messages?.noAssociations}</div></calcite-notice>`;
  }
  _renderAssociationTypeList(t) {
    return this._getFeatureCountForAssociationType(t.type) > 0 ? this._renderAssociationTypeListItem(t) : null;
  }
  _renderAssociationTypeListItem(t) {
    const e = this._getAssociationTypeTitle(t);
    return C(`association-type-${t.type}`, m`<calcite-list-item .description=${v(t.description)} .label=${v(e)} .value=${t.type} @calciteListItemSelect=${() => this._selectAssociationType(t)}><calcite-icon flip-rtl icon=chevron-right scale=s slot=content-end></calcite-icon></calcite-list-item>`);
  }
  _renderContent() {
    const { _state: t, associationTypes: e, messages: s } = this, o = s?.associationsList ?? s?.noTitle ?? "";
    return C("list-container", m`<div class="list-container">${t === "ready" ? m`<div><calcite-list display-mode=flat .label=${o}>${e.map((i) => this._renderAssociationTypeList(i))}</calcite-list></div>` : null}${this._renderStickyLoading()}</div>`);
  }
  _renderLoading() {
    return m`<div class="loading-container">${this._renderLoadingIcon()}${this._renderStickyLoading()}</div>`;
  }
  _renderLoadingIcon() {
    return m`<calcite-loader inline label></calcite-loader>`;
  }
  _renderStickyLoading() {
    return this._state === "querying" ? m`<div class="sticky-loading-container">${this._renderLoadingIcon()}</div>` : null;
  }
}
S("arcgis-feature-utility-network-associations", O);
export {
  O as ArcgisFeatureUtilityNetworkAssociations
};
