var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import {
  asError,
  CancelablePromise,
  CanceledError,
  Diagnostic,
  Logger,
  MatterAggregateError,
  Time,
  Timespan
} from "@matter/general";
import { STANDARD_COMMISSIONING_TIMEOUT } from "@matter/types";
import { ServiceDescription } from "./ServiceDescription.js";
const logger = Logger.get("Advertisement");
class Advertisement {
  /**
   * The service advertised.
   *
   * This is a unique ID associated with the advertisement.
   */
  service;
  /**
   * The information advertised.
   */
  description;
  /**
   * The {@link Advertiser} that owns this advertisement.
   */
  advertiser;
  /**
   * Configuration options.
   */
  options;
  #isBroadcasting = false;
  #closed;
  #startedAt = Time.nowMs;
  #lifetime;
  /**
   * Current activity.  This is a promise with helpers for base implementations.
   */
  #activity;
  constructor(advertiser, service, description, options) {
    this.options = options ?? {};
    this.service = service;
    this.advertiser = advertiser;
    this.description = description;
    this.#lifetime = advertiser.join(service);
    advertiser.advertisements.add(this);
    this.#start(this.onCreate.bind(this));
  }
  /**
   * Begin automatic broadcast.
   */
  start(event) {
    if (this.#closed) {
      return;
    }
    if (this.isBroadcasting) {
      return;
    }
    this.#start((context) => this.#run(context, event));
  }
  /**
   * End automatic broadcast.
   */
  stop() {
    this.#activity?.cancel();
    this.#isBroadcasting = false;
  }
  /**
   * Notify the advertiser that a client connection to the advertised service is established.
   */
  serviceConnected() {
  }
  /**
   * Notify the advertiser that there is no client connection to the advertised service.
   */
  serviceDisconnected() {
  }
  /**
   * Close the advertisement but do not wait for the close to finish.
   *
   * The advertisement is still tracked by the advertiser until closed.
   */
  cancel() {
    this.close().catch((e) => {
      logger.error("Unhandled error closing advertisement for", Diagnostic.strong(this.service), e);
    });
  }
  /**
   * Close the advertisement.
   */
  close() {
    if (this.#closed) {
      return this.#closed;
    }
    return this.#closed = this.#close();
  }
  async #close() {
    var _stack3 = [];
    try {
      const closing = __using(_stack3, this.#lifetime.closing());
      try {
        if (this.#activity) {
          var _stack = [];
          try {
            const _activity = __using(_stack, closing.join("activity"));
            this.#activity.cancel();
            await this.#activity;
          } catch (_) {
            var _error = _, _hasError = true;
          } finally {
            __callDispose(_stack, _error, _hasError);
          }
        }
        try {
          var _stack2 = [];
          try {
            const _cleanup = __using(_stack2, closing.join("cleanup"));
            await this.onClose();
          } catch (_2) {
            var _error2 = _2, _hasError2 = true;
          } finally {
            __callDispose(_stack2, _error2, _hasError2);
          }
        } catch (e) {
          logger.error("Unhandled error closing advertisement for", Diagnostic.strong(this.service), e);
        }
      } finally {
        this.advertiser.advertisements.delete(this);
      }
    } catch (_3) {
      var _error3 = _3, _hasError3 = true;
    } finally {
      __callDispose(_stack3, _error3, _hasError3);
    }
  }
  /**
   * Async setup logic.
   */
  async onCreate() {
    const duplicates = [...this.advertiser.advertisements].filter(this.isDuplicate.bind(this));
    if (duplicates.length) {
      var _stack = [];
      try {
        const _deduplicating = __using(_stack, this.#lifetime.join("deduplicating"));
        await Advertisement.closeAll(duplicates);
      } catch (_) {
        var _error = _, _hasError = true;
      } finally {
        __callDispose(_stack, _error, _hasError);
      }
    }
  }
  /**
   * Async cleanup logic.
   */
  async onClose() {
  }
  /**
   * Indicates that broadcasts should omit private details.
   */
  get isPrivacyMasked() {
    if (this.options.omitPrivateDetails) {
      return true;
    }
    if (this.duration >= STANDARD_COMMISSIONING_TIMEOUT) {
      return true;
    }
    return false;
  }
  isCommissioning() {
    return ServiceDescription.isCommissioning(this.description);
  }
  isOperational() {
    return ServiceDescription.isOperational(this.description);
  }
  /**
   * Cancel a set of advertisements.
   */
  static cancelAll(ads) {
    for (const ad of ads) {
      ad.cancel();
    }
  }
  /**
   * Cancel a set of advertisements and wait for them to complete.
   */
  static async closeAll(ads) {
    await MatterAggregateError.allSettled([...ads].map((ad) => ad.close()));
  }
  /**
   * Total duration so far.
   */
  get duration() {
    return Timespan(this.#startedAt, Time.nowMs).duration;
  }
  /**
   * True if the advertisement is actively broadcasting.
   */
  get isBroadcasting() {
    return this.#isBroadcasting;
  }
  dict(values) {
    return Diagnostic.dict({ kind: this.description.kind, service: this.service, ...values });
  }
  #start(executor) {
    const previous = this.#activity;
    previous?.cancel();
    this.#activity = new ActivityContext(async (context) => {
      if (previous) {
        await previous;
      }
      try {
        await executor(context);
      } finally {
        if (this.#activity === context) {
          this.#activity = void 0;
        }
      }
    });
  }
  async #run(context, event) {
    var _stack = [];
    try {
      const dict = this.dict();
      const advertising = __using(_stack, this.advertiser.join("advertising"));
      Object.assign(advertising.details, dict);
      logger.debug("Begin auto broadcast", dict);
      try {
        this.#isBroadcasting = true;
        await this.run(context, event);
      } catch (e) {
        if (!(e instanceof CanceledError)) {
          logger.error("Unhandled error during automatic advertisement for", Diagnostic.strong(this.service), e);
        }
      } finally {
        if (this.#activity === context) {
          this.#isBroadcasting = false;
        }
      }
      logger.debug("End auto broadcast", this.dict());
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
}
class ActivityContext extends CancelablePromise {
  #sleep;
  #cancelReason;
  constructor(executor) {
    let resolve, reject;
    super((res, rej) => {
      resolve = res;
      reject = rej;
    });
    executor(this).then(resolve, (cause) => {
      if (cause instanceof CanceledError) {
        resolve();
      } else {
        reject(cause);
      }
    });
  }
  sleep(name, duration) {
    this.abortIfCanceled();
    const sleep = Time.sleep(name, duration).finally(() => {
      if (this.#sleep === sleep) {
        this.#sleep = void 0;
      }
    });
    this.#sleep = sleep;
    return sleep;
  }
  abortIfCanceled() {
    if (this.#cancelReason) {
      throw this.#cancelReason;
    }
  }
  onCancel(reason) {
    if (this.#cancelReason) {
      return;
    }
    this.#cancelReason = asError(reason);
    if (this.#sleep) {
      this.#sleep.cancel(reason);
    }
  }
  get cancelled() {
    return this.#cancelReason !== void 0;
  }
}
export {
  Advertisement
};
//# sourceMappingURL=Advertisement.js.map
