"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, {
  DEFAULT_TTL_GRACE_FACTOR: () => DEFAULT_TTL_GRACE_FACTOR,
  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_TimeUnit = require("#time/TimeUnit.js");
var import_Bytes = require("#util/Bytes.js");
var import_Observable = require("#util/Observable.js");
var import_DnssdParameters = require("./DnssdParameters.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_Logger.Logger.get("DnssdName");
const DEFAULT_TTL_GRACE_FACTOR = 1.05;
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;
  }
  qname;
  #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) {
      const raw = /* @__PURE__ */ new Map();
      const txtRecords = new Array();
      for (const record of this.#records.values()) {
        if (record.recordType === import_DnsCodec.DnsRecordType.TXT) {
          txtRecords.push(record);
        }
      }
      txtRecords.sort((a, b) => b.installedAt - a.installedAt);
      for (const record of txtRecords) {
        for (const entry of record.value) {
          const bytes = import_Bytes.Bytes.of(entry);
          if (bytes.byteLength === 0) {
            continue;
          }
          const eqIndex = bytes.indexOf(61);
          if (eqIndex === 0) {
            continue;
          }
          const key = eqIndex === -1 ? import_Bytes.Bytes.toString(bytes) : import_Bytes.Bytes.toString(bytes.subarray(0, eqIndex));
          if (raw.has(key)) {
            continue;
          }
          raw.set(key, eqIndex === -1 ? new Uint8Array(0) : bytes.subarray(eqIndex + 1));
        }
      }
      this.#parameters = new import_DnssdParameters.DnssdParameters(raw);
    }
    return this.#parameters;
  }
  get isDiscovered() {
    return !!this.#recordCount;
  }
  installRecord(record, options) {
    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 at = options?.installedAt ?? import_Time.Time.nowMs;
    const isHostRecord = record.recordType === import_DnsCodec.DnsRecordType.A || record.recordType === import_DnsCodec.DnsRecordType.AAAA;
    const recordWithExpire = {
      ...record,
      installedAt: at,
      expiresAt: at + (0, import_TimeUnit.Millis)(Math.round(record.ttl * this.#context.ttlGraceFactor)),
      ...isHostRecord ? { sourceIntf: record.recordType === import_DnsCodec.DnsRecordType.AAAA ? options?.sourceIntf : void 0 } : {}
    };
    this.#records.set(key, recordWithExpire);
    if (record.recordType === import_DnsCodec.DnsRecordType.TXT) {
      this.#parameters = void 0;
    }
    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.installedAt >= ifOlderThan) {
      return;
    }
    this.#records.delete(key);
    this.#recordCount--;
    if (record.recordType === import_DnsCodec.DnsRecordType.TXT) {
      this.#parameters = void 0;
    }
    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)) {
        const keys = record.value.map((entry) => import_Bytes.Bytes.toHex(entry));
        keys.sort();
        return `${record.recordType} ${keys.join(" ")}`;
      }
      break;
  }
}
//# sourceMappingURL=DnssdName.js.map
