/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { isDeepEqual } from "@matter/general";
import { ValidationError } from "@matter/types";
import { ServiceArea } from "@matter/types/clusters/service-area";
import { ServiceAreaBehavior } from "./ServiceAreaBehavior.js";
const ServiceAreaBase = ServiceAreaBehavior.with(ServiceArea.Feature.Maps, ServiceArea.Feature.ProgressReporting);
class ServiceAreaBaseServer extends ServiceAreaBase {
  initialize() {
    this.#assertSupportedAreas(this.state.supportedAreas);
    this.reactTo(this.events.supportedAreas$Changing, this.#assertSupportedAreas);
    this.#assertSupportedMaps(this.state.supportedMaps);
    this.reactTo(this.events.supportedMaps$Changing, this.#assertSupportedMaps);
    this.#assertSelectedAreas(this.state.selectedAreas);
    this.reactTo(this.events.selectedAreas$Changing, this.#assertSelectedAreas);
    if (this.state.currentArea !== void 0) {
      this.#assertCurrentArea(this.state.currentArea);
      this.maybeReactTo(this.events.currentArea$Changing, this.#assertCurrentArea);
    }
    if (this.state.progress !== void 0) {
      this.#assertProgress(this.state.progress);
      this.reactTo(this.events.progress$Changing, this.#assertProgress);
    }
    if (this.state.currentArea !== void 0 && this.state.estimatedEndTime !== void 0) {
      if (this.state.currentArea === null) {
        this.state.estimatedEndTime = null;
      }
      if (this.events.estimatedEndTime$Changed !== void 0) {
        this.events.estimatedEndTime$Changed.quiet.shouldEmit = (newValue, oldValue) => {
          return oldValue === null || oldValue === 0 || newValue === null || newValue === 0 || newValue < oldValue ? "now" : true;
        };
      }
    }
  }
  #assertSupportedMaps(maps) {
    if (maps.length === 0) {
      return;
    }
    const mapIds = /* @__PURE__ */ new Set();
    const mapNames = /* @__PURE__ */ new Set();
    for (const { mapId, name } of maps) {
      if (mapIds.has(mapId)) {
        throw new ValidationError(`MapID ${mapId} is not unique`);
      }
      if (mapNames.has(name)) {
        throw new ValidationError(`MapName "${name}" is not unique`);
      }
      mapIds.add(mapId);
      mapNames.add(name);
    }
  }
  #assertSupportedAreas(areas) {
    const areaIds = /* @__PURE__ */ new Set();
    if (areas.length > 0) {
      for (const { areaId, areaInfo } of areas) {
        if (areaIds.has(areaId)) {
          throw new ValidationError(`AreaID ${areaId} is not unique`);
        }
        const { locationInfo, landmarkInfo } = areaInfo;
        if (locationInfo === null && landmarkInfo === null) {
          throw new ValidationError(`Area ${areaId} has no location or landmark info`);
        }
        if (locationInfo !== null) {
          const { locationName, floorNumber, areaType } = locationInfo;
          if (locationName === "" && floorNumber === null && areaType === null && landmarkInfo === null) {
            throw new ValidationError(`Area ${areaId} has no location info`);
          }
        }
        areaIds.add(areaId);
      }
      const mapIds = /* @__PURE__ */ new Set();
      for (let i = 0; i < areas.length; i++) {
        const { areaId, mapId, areaInfo } = areas[i];
        mapIds.add(mapId);
        for (let j = i + 1; j < areas.length; j++) {
          const otherArea = areas[j];
          if (otherArea.mapId !== mapId) {
            continue;
          }
          if (isDeepEqual(areaInfo, otherArea.areaInfo)) {
            throw new ValidationError(
              `Areas must have a unique AreaInfo field, but area ${areaId} and area ${otherArea.areaId} are equal`
            );
          }
        }
      }
      if (this.state.supportedMaps !== void 0 && this.state.supportedMaps.length > 0) {
        if (mapIds.has(null)) {
          throw new ValidationError(`Areas must not have a null mapId when supportedMaps is defined`);
        }
      } else {
        if (!mapIds.has(null) || mapIds.size > 1) {
          throw new ValidationError(`Areas must have a null mapId when supportedMaps is empty`);
        }
      }
    }
  }
  #assertSelectedAreas(areas) {
    const selectedAreaIds = /* @__PURE__ */ new Set();
    for (const areaId of areas) {
      if (!this.state.supportedAreas.some((area) => area.areaId === areaId)) {
        throw new ValidationError(`AreaID ${areaId} is not in the supported areas list`);
      }
      if (selectedAreaIds.has(areaId)) {
        throw new ValidationError(`AreaID ${areaId} is not unique`);
      }
      selectedAreaIds.add(areaId);
    }
  }
  #assertCurrentArea(areaId) {
    if (areaId === null) {
      if (this.state.estimatedEndTime !== void 0) {
        this.state.estimatedEndTime = null;
      }
      return;
    }
    if (!this.state.supportedAreas.some((area) => area.areaId === areaId)) {
      throw new ValidationError(`AreaID ${areaId} is not in the supported areas list`);
    }
  }
  #assertProgress(progress) {
    const areaIds = /* @__PURE__ */ new Set();
    for (const area of progress) {
      if (areaIds.has(area.areaId)) {
        throw new ValidationError(`AreaID ${area.areaId} is not unique`);
      }
      areaIds.add(area.areaId);
      if (!this.state.supportedAreas.some((supportedArea) => supportedArea.areaId === area.areaId)) {
        throw new ValidationError(`AreaID ${area.areaId} is not in the supported areas list`);
      }
    }
  }
  /**
   * This command is used to select a set of device areas, where the device is to operate.
   * On receipt of this command the device shall respond with a SelectAreasResponse command.
   *
   * The default implementation wil check the areaId as defined by the specification and internally calls
   * `assertSelectServiceArea`, which can be overridden to implement custom logic.
   * When a new list of areas is selected, the device shall clear the progress list and set the status of all areas to
   * Pending.
   */
  selectAreas({ newAreas }) {
    const { result, validatedAreas } = this.assertSelectServiceArea({ newAreas });
    if (result.status === ServiceArea.SelectAreasStatus.Success && validatedAreas !== void 0) {
      this.state.selectedAreas = validatedAreas;
      if (this.state.progress !== void 0 && validatedAreas.length > 0) {
        const progress = new Array();
        for (const area of validatedAreas) {
          progress.push({
            areaId: area,
            status: ServiceArea.OperationalStatus.Pending
          });
        }
        this.state.progress = progress;
      }
    }
    return result;
  }
  assertSelectServiceArea({ newAreas }) {
    const selectedAreaIds = /* @__PURE__ */ new Set();
    for (const areaId of newAreas) {
      if (!this.state.supportedAreas.some((area) => area.areaId === areaId)) {
        return {
          result: {
            status: ServiceArea.SelectAreasStatus.UnsupportedArea,
            statusText: `AreaID ${areaId} is not in the supported areas list`
          }
        };
      }
      selectedAreaIds.add(areaId);
    }
    const validatedAreas = Array.from(selectedAreaIds.values());
    return {
      result: {
        status: ServiceArea.SelectAreasStatus.Success,
        statusText: ""
      },
      validatedAreas
    };
  }
  assertSkipServiceArea({ skippedArea }) {
    if (this.state.selectedAreas.length === 0) {
      return {
        status: ServiceArea.SkipAreaStatus.InvalidAreaList,
        statusText: ""
      };
    }
    if (!this.state.selectedAreas.some((area) => area === skippedArea)) {
      return {
        status: ServiceArea.SkipAreaStatus.InvalidSkippedArea,
        statusText: `AreaID ${skippedArea} is not in the selected areas list`
      };
    }
    return {
      status: ServiceArea.SkipAreaStatus.Success,
      statusText: ""
    };
  }
  removeSupportedAreasEntry(areaId) {
    const supportedAreas = this.state.supportedAreas;
    if (supportedAreas === void 0 || supportedAreas.length === 0) {
      return;
    }
    this.state.selectedAreas = this.state.selectedAreas.filter((area) => area !== areaId);
    if (this.state.currentArea === areaId) {
      this.state.currentArea = null;
    }
    if (this.state.progress !== void 0) {
      this.state.progress = this.state.progress.filter((area) => area.areaId !== areaId);
    }
    this.state.supportedAreas = supportedAreas.filter((area) => area.areaId !== areaId);
  }
  /**
   * Convenience helper method to remove a map from the supported maps list.
   * The logic will also sync the SupportedAreas list with this removal.
   */
  removeSupportedMapsEntry(mapId) {
    const supportedMaps = this.state.supportedMaps;
    if (supportedMaps === void 0 || supportedMaps.length === 0) {
      return;
    }
    const mapToRemove = supportedMaps.find((map) => map.mapId === mapId);
    if (mapToRemove !== void 0) {
      const affectedArea = this.state.supportedAreas.find((area) => area.mapId === mapToRemove.mapId);
      if (affectedArea !== void 0) {
        this.removeSupportedAreasEntry(affectedArea.areaId);
      }
      this.state.supportedMaps = supportedMaps.filter((map) => map.mapId !== mapToRemove.mapId);
    }
  }
}
((ServiceAreaBaseServer2) => {
})(ServiceAreaBaseServer || (ServiceAreaBaseServer = {}));
class ServiceAreaServer extends ServiceAreaBaseServer.with() {
}
export {
  ServiceAreaBaseServer,
  ServiceAreaServer
};
//# sourceMappingURL=ServiceAreaServer.js.map
