"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var DnssdName_exports = {};
__export(DnssdName_exports, {
  DnssdName: () => DnssdName
});
module.exports = __toCommonJS(DnssdName_exports);
var import_DnsCodec = require("#codec/DnsCodec.js");
var import_Logger = require("#log/Logger.js");
var import_Time = require("#time/Time.js");
var import_Observable = require("#util/Observable.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_Logger.Logger.get("DnssdName");
class DnssdName extends import_Observable.BasicObservable {
  constructor(qname, context) {
    super((e) => logger.error(`Unhandled error in observer for DNS name "${qname}":`, e));
    this.qname = qname;
    this.#context = context;
  }
  #context;
  #records = /* @__PURE__ */ new Map();
  #recordCount = 0;
  #changes;
  #notified;
  #maybeDeleting;
  #parameters;
  #dependencies;
  #nullObserver;
  off(observer) {
    super.off(observer);
    this.#deleteIfUnused();
  }
  async close() {
    if (this.#notified) {
      await this.#notified;
    }
    if (this.#maybeDeleting) {
      await this.#maybeDeleting;
    }
  }
  get records() {
    return this.#records.values();
  }
  get parameters() {
    if (this.#parameters === void 0) {
      this.#parameters = /* @__PURE__ */ new Map();
    }
    return this.#parameters;
  }
  get isDiscovered() {
    return !!this.#recordCount;
  }
  installRecord(record) {
    if (record.recordType === import_DnsCodec.DnsRecordType.TXT) {
      const entries = record.value;
      for (const entry of entries) {
        const pos = entry.indexOf("=");
        if (pos === -1) {
          this.parameters.set(entry, "");
        } else {
          this.parameters.set(entry.slice(0, pos), entry.slice(pos + 1));
        }
      }
    }
    const key = keyOf(record);
    if (key === void 0) {
      this.#deleteIfUnused();
      return false;
    }
    const oldRecord = this.#records.get(key);
    if (oldRecord) {
      this.#context.unregisterForExpiration(oldRecord);
    } else {
      this.#recordCount++;
    }
    const recordWithExpire = { ...record, expiresAt: import_Time.Time.nowMs + record.ttl };
    this.#records.set(key, recordWithExpire);
    this.#context.registerForExpiration(recordWithExpire);
    if (record.recordType === import_DnsCodec.DnsRecordType.SRV && !this.#dependencies?.has(key)) {
      const dependency = this.#context.get(record.value.target);
      dependency.on(this.#nullObserver ??= () => void 0);
      (this.#dependencies ??= /* @__PURE__ */ new Map()).set(key, dependency);
    }
    this.#notify("update", key, recordWithExpire);
  }
  deleteRecord(record, ifOlderThan) {
    const key = keyOf(record);
    if (key === void 0) {
      this.#deleteIfUnused();
      return;
    }
    const recordWithExpire = this.#records?.get(key);
    if (!recordWithExpire) {
      this.#deleteIfUnused();
      return;
    }
    if (ifOlderThan !== void 0 && recordWithExpire.expiresAt - recordWithExpire.ttl >= ifOlderThan) {
      return;
    }
    this.#records.delete(key);
    this.#recordCount--;
    const dependency = this.#dependencies?.get(key);
    if (dependency) {
      this.#dependencies.delete(key);
      dependency.off(this.#nullObserver);
    }
    this.#context.unregisterForExpiration(recordWithExpire);
    if (this.#deleteIfUnused()) {
      return;
    }
    this.#notify("delete", key, recordWithExpire);
  }
  /**
   * Delete if unused.
   *
   * This is async so we assess whether deletion is appropriate after a batch of updates.
   */
  #deleteIfUnused() {
    if (this.isObserved || this.isDiscovered) {
      return false;
    }
    if (this.#maybeDeleting) {
      return true;
    }
    const maybeDelete = async () => {
      this.#maybeDeleting = void 0;
      if (this.isObserved || this.isDiscovered) {
        return;
      }
      this.#context.delete(this);
    };
    this.#maybeDeleting = maybeDelete();
    return true;
  }
  /**
   * Notification of observers.
   *
   * This is async so we coalesce changes into a single notification.
   */
  #notify(kind, key, record) {
    if (this.#changes === void 0) {
      this.#changes = /* @__PURE__ */ new Map();
    }
    this.#changes.set(key, { kind, record });
    if (this.#notified) {
      return;
    }
    const notify = async () => {
      while (this.#changes?.size) {
        const changes = { name: this };
        for (const { kind: kind2, record: record2 } of this.#changes.values()) {
          const key2 = `${kind2}d`;
          const list = changes[key2];
          if (list === void 0) {
            changes[key2] = [record2];
          } else {
            list.push(record2);
          }
        }
        this.#changes.clear();
        await this.emit(changes);
      }
      this.#notified = void 0;
    };
    this.#notified = notify();
  }
}
function keyOf(record) {
  switch (record.recordType) {
    case import_DnsCodec.DnsRecordType.A:
    case import_DnsCodec.DnsRecordType.AAAA:
    case import_DnsCodec.DnsRecordType.PTR:
      if (typeof record.value === "string") {
        return `${record.recordType} ${record.value}`;
      }
      break;
    case import_DnsCodec.DnsRecordType.SRV:
      if (typeof record.value === "object") {
        const srv = record.value;
        return `${record.recordType} ${srv.target}:${srv.port}`;
      }
      break;
    case import_DnsCodec.DnsRecordType.TXT:
      if (Array.isArray(record.value)) {
        return `${record.recordType} ${record.value.sort().join(" ")}`;
      }
      break;
  }
}
//# sourceMappingURL=DnssdName.js.map
