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 { MatterAggregateError } from "#MatterError.js";
import { Minutes } from "#time/TimeUnit.js";
import { Lifetime } from "#util/Lifetime.js";
import { Logger } from "../../log/Logger.js";
import { AsyncCache } from "../../util/Cache.js";
import { asError } from "../../util/Error.js";
import { isIPv4 } from "../../util/Ip.js";
import { NoAddressAvailableError } from "../Network.js";
const logger = Logger.get("UdpMulticastServer");
class UdpMulticastServer {
  #lifetime;
  network;
  netInterface;
  #broadcastAddressIpv4;
  #broadcastAddressIpv6;
  #broadcastPort;
  #serverIpv4;
  #serverIpv6;
  static async create({
    netInterface,
    broadcastAddressIpv4,
    broadcastAddressIpv6,
    listeningPort,
    network,
    lifetime: lifetimeOwner
  }) {
    const lifetime = (lifetimeOwner || Lifetime.process)?.join("multicast server");
    try {
      let ipv4UdpSocket = void 0;
      if (broadcastAddressIpv4 !== void 0) {
        try {
          ipv4UdpSocket = await network.createUdpSocket({
            lifetime,
            type: "udp4",
            netInterface,
            listeningPort,
            reuseAddress: true
          });
          await ipv4UdpSocket.addMembership(broadcastAddressIpv4);
        } catch (error) {
          NoAddressAvailableError.accept(error);
          logger.info(
            `IPv4 UDP channel not created because IPv4 is not available: ${asError(error).message}`
          );
        }
      }
      let ipv6UdpSocket;
      try {
        ipv6UdpSocket = await network.createUdpSocket({
          lifetime,
          type: "udp6",
          netInterface,
          listeningPort,
          reuseAddress: true
        });
        await ipv6UdpSocket.addMembership(broadcastAddressIpv6);
      } catch (error) {
        NoAddressAvailableError.accept(error);
        logger.info(`IPv6 UDP interface not created because IPv6 is not available, but required my Matter`);
        throw error;
      }
      return new UdpMulticastServer(
        lifetime,
        network,
        broadcastAddressIpv4,
        broadcastAddressIpv6,
        listeningPort,
        ipv4UdpSocket,
        ipv6UdpSocket,
        netInterface
      );
    } catch (error) {
      lifetime[Symbol.dispose]();
      throw error;
    }
  }
  broadcastChannels = new AsyncCache(
    "UDP broadcast channel",
    (netInterface, isIPv42) => this.createBroadcastChannel(netInterface, isIPv42),
    Minutes(5),
    async (_key, channel) => channel.close()
  );
  constructor(lifetime, network, broadcastAddressIpv4, broadcastAddressIpv6, broadcastPort, serverIpv4, serverIpv6, netInterface) {
    this.#lifetime = lifetime;
    this.network = network;
    this.#broadcastAddressIpv4 = broadcastAddressIpv4;
    this.#broadcastAddressIpv6 = broadcastAddressIpv6;
    this.#broadcastPort = broadcastPort;
    this.#serverIpv4 = serverIpv4;
    this.#serverIpv6 = serverIpv6;
    this.netInterface = netInterface;
  }
  get supportsIpv4() {
    return this.#serverIpv4 !== void 0;
  }
  onMessage(listener) {
    this.#serverIpv4?.onData((netInterface, peerAddress, _port, message) => {
      if (netInterface === void 0) {
        return;
      }
      listener(message, peerAddress, netInterface);
    });
    this.#serverIpv6.onData((netInterface, peerAddress, _port, message) => {
      if (netInterface === void 0) {
        return;
      }
      listener(message, peerAddress, netInterface);
    });
  }
  async send(message, netInterface, uniCastTarget) {
    netInterface = netInterface ?? this.netInterface;
    if (uniCastTarget !== void 0 && netInterface !== void 0) {
      const ipV4Target = isIPv4(uniCastTarget);
      try {
        await (await this.broadcastChannels.get(netInterface, ipV4Target)).send(uniCastTarget, this.#broadcastPort, message);
      } catch (error) {
        logger.info(`${netInterface} ${uniCastTarget}: ${asError(error).message}`);
        await this.#evictBroadcastChannel(netInterface, ipV4Target);
      }
    } else {
      const netInterfaces = netInterface !== void 0 ? [{ name: netInterface }] : await this.network.getNetInterfaces();
      await MatterAggregateError.allSettled(
        netInterfaces.map(async ({ name: netInterface2 }) => {
          const { ipV4, ipV6 } = await this.network.getIpMac(netInterface2) ?? {
            mac: "",
            ipV4: [],
            ipV6: []
          };
          const targets = new Array();
          if (ipV4.length > 0 && this.#broadcastAddressIpv4 !== void 0) {
            targets.push({ isIPv4: true, address: this.#broadcastAddressIpv4 });
          }
          if (ipV6.length > 0) {
            targets.push({ isIPv4: false, address: this.#broadcastAddressIpv6 });
          }
          await MatterAggregateError.allSettled(
            targets.map(async ({ isIPv4: isIPv42, address }) => {
              try {
                await (await this.broadcastChannels.get(netInterface2, isIPv42)).send(address, this.#broadcastPort, message);
              } catch (error) {
                logger.info(`${netInterface2}: ${asError(error).message}`);
                await this.#evictBroadcastChannel(netInterface2, isIPv42);
              }
            }),
            `Error sending UDP Multicast message on interface ${netInterface2}`
          );
        }),
        "Error sending UDP Multicast message"
      );
    }
  }
  /**
   * Drop a broadcast channel after a send failure so the next send rebinds it. A failed send can mean the cached
   * socket is bound to an interface address that no longer exists (e.g. after the host resumed from standby).
   * Eviction must never escalate a send failure, so closing the stale socket is best-effort.
   */
  async #evictBroadcastChannel(netInterface, isIPv42) {
    try {
      await this.broadcastChannels.evict(netInterface, isIPv42);
    } catch (error) {
      logger.info(`${netInterface}: failed to evict broadcast channel: ${asError(error).message}`);
    }
  }
  async createBroadcastChannel(netInterface, isIPv42) {
    return await this.network.createUdpSocket({
      lifetime: this.#lifetime,
      type: isIPv42 ? "udp4" : "udp6",
      listeningPort: this.#broadcastPort,
      netInterface,
      reuseAddress: true
    });
  }
  async close() {
    var _stack = [];
    try {
      const _closing = __using(_stack, this.#lifetime.closing());
      await this.#serverIpv4?.close();
      await this.#serverIpv6.close();
      await this.broadcastChannels.close();
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
}
export {
  UdpMulticastServer
};
//# sourceMappingURL=UdpMulticastServer.js.map
