/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { FileDesignator } from "#bdx/FileDesignator.js";
import { ImplementationError, InternalError, Logger } from "@matter/general";
import { BdxStatusCode } from "@matter/types";
import { BdxError } from "./BdxError.js";
import { BdxMessenger } from "./BdxMessenger.js";
import { Flow } from "./flow/Flow.js";
import { BDX_VERSION } from "./schema/BdxInitMessagesSchema.js";
const logger = Logger.get("bdxSessionInitiator");
async function bdxSessionInitiator(messenger, config) {
  if (config.isInitiator) {
    if (config.isSender) {
      const initMessage2 = await buildInitMessage();
      const acceptMessage3 = await messenger.sendSendInit(initMessage2);
      return collectAcceptParameters(
        acceptMessage3,
        // We define the data, so we know it are numbers
        initMessage2.startOffset !== void 0 ? Number(initMessage2.startOffset) : void 0,
        initMessage2.maxLength !== void 0 ? Number(initMessage2.maxLength) : void 0
      );
    }
    const acceptMessage2 = await messenger.sendReceiveInit(await buildInitMessage());
    return collectAcceptParameters(acceptMessage2);
  }
  const initMessage = config.initMessage;
  if (initMessage === void 0) {
    throw new InternalError("Initial message must be set before starting from initial message");
  }
  logger.debug(`Initialize BDX ${config.isSender ? "ReceiveInit" : "SendInit"} from incoming Message`, initMessage);
  const incomingFileDesignator = new FileDesignator(initMessage.fileDesignator);
  if (incomingFileDesignator.text !== config.transferFileDesignator.text) {
    throw new BdxError(
      `File designator in Init message (${incomingFileDesignator.text}) does not match the configured transfer file designator (${config.transferFileDesignator.text})`,
      BdxStatusCode.FileDesignatorUnknown
    );
  }
  if (config.isSender) {
    if (!config.fileDesignator.exists()) {
      throw new BdxError(
        `File designator ${config.fileDesignator.text} does not point to an existing file in the storage to send data`,
        BdxStatusCode.FileDesignatorUnknown
      );
    }
    let { startOffset } = initMessage;
    if (startOffset !== void 0) {
      startOffset = BdxMessenger.asSafeNumber(startOffset, "Start offset", BdxStatusCode.StartOffsetNotSupported);
    }
    const acceptMessage2 = await determineAcceptParameters(initMessage);
    const length = "length" in acceptMessage2 && typeof acceptMessage2.length === "number" ? acceptMessage2.length : void 0;
    const blobToRead = await config.fileDesignator.openBlob();
    if (length !== void 0 && length > 0) {
      const availableSize = blobToRead.size - (startOffset ?? 0);
      if (length > availableSize) {
        throw new BdxError(
          `Requested maxLength ${length}bytes${startOffset ? ` with startOffset ${startOffset}` : ""} exceeds available size ${blobToRead.size} for blob ${config.fileDesignator.text}`,
          BdxStatusCode.LengthTooLarge
        );
      }
    }
    await messenger.sendReceiveAccept(acceptMessage2);
    return collectAcceptParameters(acceptMessage2, startOffset, length);
  }
  const acceptMessage = {
    ...await determineAcceptParameters(initMessage),
    length: void 0
    // Length is not included in SendAccept, so take out to not confuse in logs
  };
  await messenger.sendSendAccept(acceptMessage);
  return collectAcceptParameters(acceptMessage);
  async function buildInitMessage() {
    const {
      senderStartOffset,
      maxTransferSize,
      senderMaxLength,
      preferredDriverModes,
      asynchronousTransferAllowed
    } = config.transferConfig;
    const { isSender, fileDesignator, transferFileDesignator } = config;
    let startOffset;
    let maxLength;
    if (isSender) {
      maxLength = (await fileDesignator.openBlob()).size;
      if (senderStartOffset !== void 0) {
        if (maxLength <= senderStartOffset) {
          throw new ImplementationError(
            `Available data of ${maxLength}bytes are smaller than senderStartOffset ${senderStartOffset}bytes`
          );
        }
        startOffset = senderStartOffset;
        maxLength -= startOffset;
      }
      if (maxTransferSize !== void 0) {
        if (maxLength > maxTransferSize) {
          throw new ImplementationError(
            `Requested maxLength ${maxLength}bytes exceeds maximum transfer size ${maxTransferSize}bytes`
          );
        }
      }
      if (senderMaxLength !== void 0) {
        if (maxLength >= senderMaxLength) {
          maxLength = senderMaxLength;
        } else {
          logger.info(
            `Ignoring requested senderMaxLength ${senderMaxLength}bytes as it is larger then the relevant payload size of ${maxLength}bytes`
          );
        }
      }
    }
    let maxBlockSize = messenger.maxPayloadSize - 4;
    const requestedBlockSize = config.transferConfig.maxBlockSize;
    if (requestedBlockSize !== void 0) {
      if (maxBlockSize > requestedBlockSize) {
        maxBlockSize = requestedBlockSize;
      } else {
        logger.info(
          `Ignoring requested maxBlockSize ${requestedBlockSize}, as it is larger then the transport max payload size ${maxBlockSize}bytes`
        );
      }
    }
    return {
      transferProtocol: {
        version: BDX_VERSION,
        senderDrive: !!preferredDriverModes?.includes(Flow.DriverMode.SenderDrive),
        receiverDrive: !!preferredDriverModes?.includes(Flow.DriverMode.ReceiverDrive),
        asynchronousTransfer: asynchronousTransferAllowed
        // always false for now
      },
      maxBlockSize,
      fileDesignator: transferFileDesignator.bytes,
      startOffset,
      maxLength
    };
  }
  async function determineAcceptParameters(initMessage2) {
    const { transferProtocol, maxLength: initMaxLength = 0 } = initMessage2;
    let { maxBlockSize } = initMessage2;
    let finalDriverMode;
    for (const mode of config.transferConfig.preferredDriverModes) {
      if (transferProtocol[mode]) {
        finalDriverMode = mode;
        break;
      }
    }
    if (finalDriverMode === void 0) {
      throw new BdxError("Can not determine a valid transfer mode", BdxStatusCode.TransferMethodNotSupported);
    }
    const requestedMaxBlockSize = config.transferConfig.maxBlockSize;
    if (requestedMaxBlockSize !== void 0 && maxBlockSize > requestedMaxBlockSize) {
      maxBlockSize = requestedMaxBlockSize;
    }
    const maxLength = BdxMessenger.asSafeNumber(initMaxLength, "Max length", BdxStatusCode.LengthTooLarge);
    const transferControl = {
      version: BDX_VERSION,
      // We support the minimum version, so no need for further checks
      [finalDriverMode]: true,
      // this sets either senderDrive or receiveDriver property
      asynchronousTransfer: false
      // Not supported so ignore if it was received
    };
    return {
      transferControl,
      maxBlockSize,
      length: maxLength > 0 ? maxLength : void 0
    };
  }
  function collectAcceptParameters(acceptMessage2, startOffset = 0, maxLength) {
    const {
      transferControl: { senderDrive, asynchronousTransfer },
      maxBlockSize
    } = acceptMessage2;
    if (asynchronousTransfer) {
      throw new BdxError(
        "Asynchronous transfer is not supported in this implementation",
        BdxStatusCode.TransferMethodNotSupported
      );
    }
    const dataLength = "length" in acceptMessage2 && acceptMessage2.length !== void 0 ? BdxMessenger.asSafeNumber(
      acceptMessage2.length,
      "Accept message length field",
      BdxStatusCode.LengthTooLarge
    ) : maxLength;
    const transferParameters = {
      transferMode: senderDrive ? Flow.DriverMode.SenderDrive : Flow.DriverMode.ReceiverDrive,
      asynchronousTransfer: false,
      // always false for now
      dataLength,
      startOffset,
      blockSize: maxBlockSize,
      isDriver: config.isSender && senderDrive || !config.isSender && !senderDrive,
      fileDesignator: config.fileDesignator
    };
    logger.debug(
      `${transferParameters.isDriver ? "We are" : "Peer is"} driving-${config.isSender ? "sender" : "receiver"} with negotiated transfer parameters`,
      transferParameters
    );
    return transferParameters;
  }
}
export {
  bdxSessionInitiator
};
//# sourceMappingURL=bdx-session-initiator.js.map
