const require_rolldown_runtime = require('../../../../_virtual/rolldown_runtime.js');
const require_decorateMetadata = require('../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateMetadata.js');
const require_decorate = require('../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js');
const require_decorateParam = require('../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateParam.js');
const require_index = require('../../../../node_modules/.pnpm/async-mutex@0.5.0/node_modules/async-mutex/index.js');
const require_TenantsModuleConfig = require('../TenantsModuleConfig.js');
const require_TenantSessionMutex = require('./TenantSessionMutex.js');
let __credo_ts_core = require("@credo-ts/core");
__credo_ts_core = require_rolldown_runtime.__toESM(__credo_ts_core);

//#region ../tenants/src/context/TenantSessionCoordinator.ts
var _ref, _ref2;
let TenantSessionCoordinator = class TenantSessionCoordinator$1 {
	constructor(rootAgentContext, logger, tenantsModuleConfig) {
		this.tenantAgentContextMapping = {};
		this.rootAgentContext = rootAgentContext;
		this.logger = logger;
		this.tenantsModuleConfig = tenantsModuleConfig;
		this.sessionMutex = new require_TenantSessionMutex.TenantSessionMutex(this.logger, this.tenantsModuleConfig.sessionLimit, this.tenantsModuleConfig.sessionAcquireTimeout);
	}
	getSessionCountForTenant(tenantId) {
		const contextCorrelationId = this.getContextCorrelationIdForTenantId(tenantId);
		return this.tenantAgentContextMapping[contextCorrelationId]?.sessionCount ?? 0;
	}
	/**
	* Get agent context to use for a session. If an agent context for this tenant does not exist yet
	* it will create it and store it for later use. If the agent context does already exist it will
	* be returned.
	*
	* @parm tenantRecord The tenant record for which to get the agent context
	*/
	async getContextForSession(tenantRecord, { runInMutex, provisionContext = false } = {}) {
		this.logger.debug(`Getting context for session with tenant '${tenantRecord.id}'`);
		await this.sessionMutex.acquireSession();
		try {
			const contextCorrelationId = this.getContextCorrelationIdForTenantId(tenantRecord.id);
			return await this.mutexForTenant(contextCorrelationId).runExclusive(async () => {
				this.logger.debug(`Acquired lock for tenant '${tenantRecord.id}' to get context`);
				const tenantSessions = this.getTenantSessionsMapping(contextCorrelationId);
				if (!tenantSessions.agentContext) {
					this.logger.debug(`No agent context has been initialized for tenant '${tenantRecord.id}', creating one`);
					tenantSessions.agentContext = await this.createAgentContext(tenantRecord, { provisionContext });
				}
				tenantSessions.sessionCount++;
				this.logger.debug(`Increased agent context session count for tenant '${tenantRecord.id}' to ${tenantSessions.sessionCount}`);
				if (runInMutex) try {
					await runInMutex(tenantSessions.agentContext);
				} catch (error) {
					tenantSessions.sessionCount--;
					this.logger.debug(`Decreased agent context session count for tenant context '${contextCorrelationId}' to ${tenantSessions.sessionCount} due to failure in mutex script`, error);
					if (tenantSessions.sessionCount <= 0 && tenantSessions.agentContext) {
						await this.closeAgentContext(tenantSessions.agentContext);
						delete this.tenantAgentContextMapping[contextCorrelationId];
					}
					throw error;
				}
				return tenantSessions.agentContext;
			});
		} catch (error) {
			this.logger.debug(`Releasing session because an error occurred while getting the context for tenant ${tenantRecord.id}`, { errorMessage: error.message });
			this.sessionMutex.releaseSession();
			throw error;
		}
	}
	/**
	* End a session for the provided agent context. It will decrease the session count for the agent context.
	* If the number of sessions is zero after the context for this session has been ended, the agent context will be closed.
	*/
	async endAgentContextSession(agentContext) {
		this.logger.debug(`Ending session for agent context with contextCorrelationId ${agentContext.contextCorrelationId}'`);
		if (agentContext.contextCorrelationId === this.rootAgentContext.contextCorrelationId) {
			this.logger.debug("Ending session for root agent context. Not disposing dependency manager");
			return;
		}
		const contextCorrelationId = agentContext.contextCorrelationId;
		this.assertTenantContextCorrelationId(contextCorrelationId);
		if (!this.hasTenantSessionMapping(contextCorrelationId)) {
			this.logger.error(`Unknown agent context with contextCorrelationId '${contextCorrelationId}'.  Cannot end session`);
			throw new __credo_ts_core.CredoError(`Unknown agent context with contextCorrelationId '${contextCorrelationId}'. Cannot end session`);
		}
		await this.mutexForTenant(contextCorrelationId).runExclusive(async () => {
			this.logger.debug(`Acquired lock for tenant '${contextCorrelationId}' to end session context`);
			const tenantSessions = this.getTenantSessionsMapping(contextCorrelationId);
			tenantSessions.sessionCount--;
			this.logger.debug(`Decreased agent context session count for tenant '${contextCorrelationId}' to ${tenantSessions.sessionCount}`);
			if (tenantSessions.sessionCount <= 0 && tenantSessions.agentContext) {
				await this.closeAgentContext(tenantSessions.agentContext);
				delete this.tenantAgentContextMapping[contextCorrelationId];
			}
		}).finally(() => {
			this.sessionMutex.releaseSession();
		});
	}
	/**
	* Delete the provided agent context. All opens sessions will be disposed and not usable anymore
	*/
	async deleteAgentContext(agentContext) {
		this.logger.debug(`Deleting agent context with contextCorrelationId ${agentContext.contextCorrelationId}'`);
		if (agentContext.contextCorrelationId === this.rootAgentContext.contextCorrelationId) {
			this.logger.debug("Deleting agent context for root agent context.");
			await agentContext.dependencyManager.deleteAgentContext(agentContext);
			return;
		}
		const contextCorrelationId = agentContext.contextCorrelationId;
		this.assertTenantContextCorrelationId(contextCorrelationId);
		if (!this.hasTenantSessionMapping(contextCorrelationId)) {
			this.logger.error(`Unknown agent context with contextCorrelationId '${contextCorrelationId}'.  Cannot delete agent context`);
			throw new __credo_ts_core.CredoError(`Unknown agent context with contextCorrelationId '${contextCorrelationId}'. Cannot delete agent context`);
		}
		await this.mutexForTenant(contextCorrelationId).runExclusive(async () => {
			this.logger.debug(`Acquired lock for tenant '${contextCorrelationId}' to delete agent context`);
			const tenantSessions = this.getTenantSessionsMapping(contextCorrelationId);
			this.logger.debug(`Deleting agent context for tenant '${contextCorrelationId}' with ${tenantSessions.sessionCount} active sessions.`);
			if (!tenantSessions.agentContext) throw new __credo_ts_core.CredoError(`Unable to delete agent context for tenant '${contextCorrelationId}' as there are no active sessions.`);
			await agentContext.dependencyManager.deleteAgentContext(tenantSessions.agentContext);
			delete this.tenantAgentContextMapping[contextCorrelationId];
		}).finally(() => {
			this.sessionMutex.releaseSession();
		});
	}
	/**
	* The context correlation id for a tenant is the tenant id prefixed with tenant-
	*/
	getContextCorrelationIdForTenantId(tenantId) {
		if (tenantId.startsWith("tenant-")) throw new __credo_ts_core.CredoError(`Tenant id already starts with 'tenant-'. You are probalby passing a context correlation id`);
		return `tenant-${tenantId}`;
	}
	/**
	* The context correlation id for a tenant is the tenant id prefixed with tenant-
	*/
	getTenantIdForContextCorrelationId(contextCorrelationId) {
		if (!contextCorrelationId.startsWith("tenant-")) throw new __credo_ts_core.CredoError(`Could not extract tenant id from context correlation id. Context correlation id should start with 'tenant-'`);
		return contextCorrelationId.replace("tenant-", "");
	}
	isTenantContextCorrelationId(contextCorrelationId) {
		return contextCorrelationId.startsWith("tenant-");
	}
	assertTenantContextCorrelationId(contextCorrelationId) {
		if (!this.isTenantContextCorrelationId(contextCorrelationId)) throw new __credo_ts_core.CredoError(`Expected context correlation id for tenant to start with 'tenant-'`);
	}
	hasTenantSessionMapping(contextCorrelationId) {
		return this.tenantAgentContextMapping[contextCorrelationId] !== void 0;
	}
	getTenantSessionsMapping(contextCorrelationId) {
		let tenantSessionMapping = this.tenantAgentContextMapping[contextCorrelationId];
		if (tenantSessionMapping) return tenantSessionMapping;
		tenantSessionMapping = {
			sessionCount: 0,
			mutex: require_index.withTimeout(new require_index.Mutex(), this.tenantsModuleConfig.sessionAcquireTimeout, new __credo_ts_core.CredoError(`Error acquiring lock for tenant context ${contextCorrelationId}. Wallet initialization or shutdown took too long.`))
		};
		this.tenantAgentContextMapping[contextCorrelationId] = tenantSessionMapping;
		return tenantSessionMapping;
	}
	mutexForTenant(contextCorrelationId) {
		return this.getTenantSessionsMapping(contextCorrelationId).mutex;
	}
	async createAgentContext(tenantRecord, { provisionContext }) {
		const tenantDependencyManager = this.rootAgentContext.dependencyManager.createChild();
		const tenantConfig = this.rootAgentContext.config.extend({});
		const agentContext = new __credo_ts_core.AgentContext({
			contextCorrelationId: this.getContextCorrelationIdForTenantId(tenantRecord.id),
			dependencyManager: tenantDependencyManager,
			isRootAgentContext: false
		});
		tenantDependencyManager.registerInstance(__credo_ts_core.AgentContext, agentContext);
		tenantDependencyManager.registerInstance(__credo_ts_core.AgentConfig, tenantConfig);
		if (provisionContext) await tenantDependencyManager.provisionAgentContext(agentContext);
		await tenantDependencyManager.initializeAgentContext(agentContext);
		return agentContext;
	}
	async closeAgentContext(agentContext) {
		this.logger.debug(`Closing agent context for tenant '${agentContext.contextCorrelationId}'`);
		await agentContext.dependencyManager.closeAgentContext(agentContext);
	}
};
TenantSessionCoordinator = require_decorate.__decorate([
	(0, __credo_ts_core.injectable)(),
	require_decorateParam.__decorateParam(1, (0, __credo_ts_core.inject)(__credo_ts_core.InjectionSymbols.Logger)),
	require_decorateMetadata.__decorateMetadata("design:paramtypes", [
		typeof (_ref = typeof __credo_ts_core.AgentContext !== "undefined" && __credo_ts_core.AgentContext) === "function" ? _ref : Object,
		Object,
		typeof (_ref2 = typeof require_TenantsModuleConfig.TenantsModuleConfig !== "undefined" && require_TenantsModuleConfig.TenantsModuleConfig) === "function" ? _ref2 : Object
	])
], TenantSessionCoordinator);

//#endregion
Object.defineProperty(exports, 'TenantSessionCoordinator', {
  enumerable: true,
  get: function () {
    return TenantSessionCoordinator;
  }
});