/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Fabric as RealFabric } from "#fabric/Fabric.js";
import { MessageType } from "#interaction/InteractionMessenger.js";
import { NodeSession as RealNodeSession } from "#session/NodeSession.js";
import { SessionParameters } from "#session/SessionParameters.js";
import {
  b$,
  Bytes,
  ChannelType,
  Crypto,
  DataReadQueue,
  Environment,
  ImplementationError,
  MAX_UDP_MESSAGE_SIZE,
  MaybePromise,
  MockCrypto,
  Observable,
  ServerAddress
} from "@matter/general";
import { Specification } from "@matter/model";
import {
  FabricId,
  FabricIndex,
  GlobalFabricId,
  NodeId,
  SECURE_CHANNEL_PROTOCOL_ID,
  Status,
  TlvStatusResponse,
  VendorId
} from "@matter/types";
import { MessageChannel as RealMessageChannel } from "./MessageChannel.js";
import { MessageExchange } from "./MessageExchange.js";
var ProtocolMocks;
((ProtocolMocks2) => {
  class Fabric extends RealFabric {
    constructor(config, crypto) {
      if (!crypto) {
        crypto = Environment.default.maybeGet(Crypto);
        if (!(crypto instanceof MockCrypto)) {
          crypto = MockCrypto();
        }
      }
      const keyPair = config?.keyPair ?? crypto.createKeyPair();
      if (MaybePromise.is(keyPair)) {
        throw new ImplementationError("Must provide key pair with async crypto");
      }
      super(crypto, {
        ...Fabric.defaults,
        ...config,
        keyPair
      });
    }
  }
  ProtocolMocks2.Fabric = Fabric;
  ((Fabric2) => {
    Fabric2.defaults = {
      fabricId: FabricId(0x2906c908d115d362n),
      fabricIndex: FabricIndex(1),
      identityProtectionKey: Bytes.empty,
      intermediateCACert: Bytes.empty,
      keyPair: { publicKey: Bytes.empty, privateKey: Bytes.empty },
      label: "test-fabric",
      nodeId: NodeId(0xcd5544aa7b13ef14n),
      operationalCert: Bytes.empty,
      globalId: GlobalFabricId(0),
      operationalIdentityProtectionKey: b$`9bc61cd9c62a2df6d64dfcaa9dc472d4`,
      rootCert: Bytes.empty,
      rootNodeId: NodeId(1),
      rootPublicKey: b$`044a9f42b1ca4840d37292bbc7f6a7e11e22200c976fc900dbc98a7a383a641cb8254a2e56d4e295a847943b4e3897c4a773e930277b4d9fbede8a052686bfacfa`,
      rootVendorId: VendorId(1)
    };
  })(Fabric = ProtocolMocks2.Fabric || (ProtocolMocks2.Fabric = {}));
  class NodeSession extends RealNodeSession {
    constructor(config) {
      const index = config?.index ?? 1;
      const fabricIndex = config?.fabricIndex ?? 1;
      const crypto = config?.crypto ?? Environment.default.get(Crypto);
      const fabric = config && "fabric" in config ? config.fabric : new Fabric({ fabricIndex });
      const maxPayloadSize = config?.maxPayloadSize;
      let channel;
      if (config && "channel" in config) {
        channel = config.channel;
      } else {
        channel = new NetworkChannel({ index, maxPayloadSize });
      }
      const fullConfig = {
        id: index,
        peerNodeId: NodeId(index),
        attestationKey: Bytes.empty,
        caseAuthenticatedTags: [],
        peerSessionId: index,
        decryptKey: Bytes.empty,
        encryptKey: Bytes.empty,
        isInitiator: true,
        ...config,
        crypto,
        fabric
      };
      delete fullConfig.channel;
      super(fullConfig);
      this.channel = new MessageChannel({ channel, session: this });
    }
    static async create(config) {
      const crypto = config?.crypto ?? config?.manager?.crypto ?? Environment.default.get(Crypto);
      const index = config?.index ?? 1;
      return RealNodeSession.create.call(this, {
        id: index,
        crypto,
        peerNodeId: NodeId(0),
        peerSessionId: index,
        sharedSecret: Bytes.empty,
        salt: Bytes.empty,
        isInitiator: true,
        isResumption: false,
        ...config
      });
    }
  }
  ProtocolMocks2.NodeSession = NodeSession;
  class NetworkChannel {
    maxPayloadSize;
    isReliable = true;
    supportsLargeMessages = false;
    name = "mock-byte-channel";
    type = ChannelType.UDP;
    #networkAddress;
    networkAddressChanged = new Observable();
    constructor(config) {
      const index = config.index ?? 1;
      this.maxPayloadSize = config.maxPayloadSize ?? MAX_UDP_MESSAGE_SIZE;
      this.#networkAddress = { type: "udp", ip: `::${index}`, port: 5540 };
    }
    get networkAddress() {
      return this.#networkAddress;
    }
    set networkAddress(networkAddress) {
      if (ServerAddress.isEqual(networkAddress, this.#networkAddress)) {
        return;
      }
      this.#networkAddress = networkAddress;
      this.networkAddressChanged.emit(this.networkAddress);
    }
    async send() {
    }
    async close() {
    }
  }
  ProtocolMocks2.NetworkChannel = NetworkChannel;
  class MessageChannel extends RealMessageChannel {
    #requests = new DataReadQueue();
    #readReady;
    constructor(config) {
      const index = config?.index ?? 1;
      const fabricIndex = config?.fabricIndex ?? 1;
      const channel = config?.channel ?? new NetworkChannel({ index });
      const session = config?.session ?? new NodeSession({ index, fabricIndex });
      super(channel, session, config?.onClose);
    }
    async send(message) {
      this.#requests.write(message);
      await this.#readReady?.emit();
    }
    read() {
      return this.#requests.read();
    }
    get readReady() {
      return this.#readReady ??= new Observable();
    }
  }
  ProtocolMocks2.MessageChannel = MessageChannel;
  class Exchange extends MessageExchange {
    #responses = new DataReadQueue();
    constructor(config) {
      const context = config?.context;
      const index = config?.index ?? 1;
      const fabricIndex = config?.fabricIndex ?? 1;
      const maxPayloadSize = config?.maxPayloadSize;
      super({
        isInitiator: true,
        exchangeId: index,
        peerSessionId: index,
        protocolId: config?.protocolId ?? SECURE_CHANNEL_PROTOCOL_ID,
        ...config,
        context: {
          session: context?.session ?? new NodeSession({ index, fabricIndex, maxPayloadSize }),
          localSessionParameters: SessionParameters(
            context?.localSessionParameters ?? SessionParameters.defaults
          ),
          async peerLost() {
          },
          retry() {
          }
        }
      });
    }
    /**
     * Enqueue a mock response.
     */
    async write(message) {
      this.#responses.write(message);
    }
    /**
     * Enqueue a mock status response.
     */
    async writeStatus(status = Status.Success) {
      await this.write({
        packetHeader: {
          messageId: 123
        },
        payloadHeader: {
          messageType: MessageType.StatusResponse
        },
        payload: TlvStatusResponse.encode({
          status,
          interactionModelRevision: Specification.INTERACTION_MODEL_REVISION
        })
      });
    }
    /**
     * Wait for a mock request.
     */
    read() {
      return this.#mockChannel.read();
    }
    /**
     * Emits when read() won't block.
     */
    get readReady() {
      return this.#mockChannel.readReady;
    }
    nextMessage() {
      return this.#responses.read();
    }
    get #mockChannel() {
      const channel = this.channel;
      if (!(channel instanceof MessageChannel)) {
        throw new ImplementationError("Message channel is not mocked");
      }
      return channel;
    }
  }
  ProtocolMocks2.Exchange = Exchange;
})(ProtocolMocks || (ProtocolMocks = {}));
export {
  ProtocolMocks
};
//# sourceMappingURL=ProtocolMocks.js.map
