/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import {
  AsyncObservable,
  Diagnostic,
  InternalError,
  Lifetime,
  Logger,
  MaybePromise,
  Transaction
} from "@matter/general";
import { AccessLevel } from "@matter/model";
import { AccessControl, Mark } from "@matter/protocol";
import { Contextual } from "../Contextual.js";
let nextInternalId = 1;
const logger = Logger.get("LocalActorContext");
let ReadOnly;
const LocalActorContext = {
  /**
   * Operate on behalf of a local actor.  This is the context for operations on nodes initiated locally, without
   * authentication.
   *
   * {@link act} provides an {@link ActionContext} you can use to access agents for a {@link Endpoint}.
   * State changes and change events occur once {@link actor} returns.
   * It can return a promise even if the actor method does not return a promise, so manual checks are needed.
   *
   * The {@link Transaction} is destroyed with {@link act} exits so you should not keep a reference to any agents
   * beyond the lifespan of {@link actor}.
   *
   * Offline context is very permissive.  You should use carefully.
   */
  act(purpose, actor, options) {
    const interactionComplete = new AsyncObservable();
    const context = this.open(purpose, { ...options, interactionComplete });
    let result;
    try {
      result = actor(context);
    } catch (e) {
      return context.reject(e);
    }
    context.transaction.onShared(() => {
      function handleErr(err) {
        logger.warn("interactionComplete observer failed", Diagnostic.error(err));
      }
      if (interactionComplete.isObserved) {
        try {
          const result2 = interactionComplete.emit(context);
          if (MaybePromise.is(result2)) {
            return MaybePromise.then(result2, void 0, handleErr);
          }
        } catch (e) {
          handleErr(e);
        }
      }
    }, true);
    return context.resolve(result);
  },
  /**
   * Create an offline context.
   *
   * This context operates with a {@link Transaction} created via {@link Transaction.open} and the same rules
   * apply for lifecycle management using {@link Transaction.Finalization}.
   */
  open(purpose, options) {
    const id = nextInternalId;
    nextInternalId = (nextInternalId + 1) % 65535;
    const via = Diagnostic.via(`${Mark.LOCAL_SESSION}${purpose}#${id.toString(16)}`);
    let frame;
    let transaction;
    try {
      frame = options?.activity?.begin(via);
      transaction = Transaction.open(via, options?.lifetime ?? Lifetime.process, options?.isolation);
      if (frame) {
        transaction.onClose(frame.close.bind(frame));
      }
      const context = Object.freeze({
        ...options,
        transaction,
        activity: frame,
        interactionComplete: options?.interactionComplete,
        authorityAt(desiredAccessLevel) {
          return desiredAccessLevel === AccessLevel.View ? AccessControl.Authority.Granted : AccessControl.Authority.Unauthorized;
        },
        get [Contextual.context]() {
          return this;
        },
        [Symbol.toStringTag]: "OfflineContext",
        resolve: transaction.resolve.bind(transaction),
        reject: transaction.reject.bind(transaction),
        offline: true
      });
      return context;
    } catch (e) {
      if (transaction) {
        transaction.reject(e);
      } else {
        frame?.close();
        throw e;
      }
    }
    throw new InternalError("Unexpected end of open");
  },
  /**
   * Normally you need to use {@link LocalActorContext.act} to work with behaviors, and you can only interact with the
   * behaviors in the actor function.  This {@link ActionContext} allows you to create offline agents that remain
   * functional for the lifespan of the node.
   *
   * Write operations will throw an error with this context.
   */
  get ReadOnly() {
    if (ReadOnly === void 0) {
      ReadOnly = LocalActorContext.open("read-only", { isolation: "ro" });
    }
    return ReadOnly;
  },
  [Symbol.toStringTag]: "LocalActorContext"
};
export {
  LocalActorContext,
  nextInternalId
};
//# sourceMappingURL=LocalActorContext.js.map
