import { isAstNode as e, isAstNodeDescription as t, isLinkingError as n } from "../syntax-tree.js";
import { findRootNode as r, streamAst as i, streamReferences as a } from "../utils/ast-utils.js";
import { cancellation_exports as o } from "../utils/cancellation.js";
import { interruptAndCheck as s } from "../utils/promise-utils.js";
import { DocumentState as c } from "../workspace/documents.js";
//#region ../../node_modules/langium/lib/references/linker.js
var l = Symbol("RefResolving"), u = class {
	constructor(e) {
		this.reflection = e.shared.AstReflection, this.langiumDocuments = () => e.shared.workspace.LangiumDocuments, this.scopeProvider = e.references.ScopeProvider, this.astNodeLocator = e.workspace.AstNodeLocator, this.profiler = e.shared.profilers.LangiumProfiler, this.languageId = e.LanguageMetaData.languageId;
	}
	async link(e, t = o.CancellationToken.None) {
		if (this.profiler?.isActive("linking")) {
			let n = this.profiler.createTask("linking", this.languageId);
			n.start();
			try {
				for (let r of i(e.parseResult.value)) await s(t), a(r).forEach((t) => {
					let i = `${r.$type}:${t.property}`;
					n.startSubTask(i);
					try {
						this.doLink(t, e);
					} finally {
						n.stopSubTask(i);
					}
				});
			} finally {
				n.stop();
			}
		} else for (let n of i(e.parseResult.value)) await s(t), a(n).forEach((t) => this.doLink(t, e));
	}
	doLink(e, t) {
		let r = e.reference;
		if ("_ref" in r && r._ref === void 0) {
			r._ref = l;
			try {
				let t = this.getCandidate(e);
				n(t) ? r._ref = t : (r._nodeDescription = t, r._ref = this.loadAstNode(t) ?? this.createLinkingError(e, t));
			} catch (t) {
				console.error(`An error occurred while resolving reference to '${r.$refText}':`, t);
				let n = t.message ?? String(t);
				r._ref = {
					info: e,
					message: `An error occurred while resolving reference to '${r.$refText}': ${n}`
				};
			}
			t.references.push(r);
		} else if ("_items" in r && r._items === void 0) {
			r._items = l;
			try {
				let t = this.getCandidates(e), i = [];
				if (n(t)) r._linkingError = t;
				else for (let e of t) {
					let t = this.loadAstNode(e);
					t && i.push({
						ref: t,
						$nodeDescription: e
					});
				}
				r._items = i;
			} catch (t) {
				r._linkingError = {
					info: e,
					message: `An error occurred while resolving reference to '${r.$refText}': ${t}`
				}, r._items = [];
			}
			t.references.push(r);
		}
	}
	unlink(e) {
		for (let t of e.references) "_ref" in t ? (t._ref = void 0, delete t._nodeDescription) : "_items" in t && (t._items = void 0, delete t._linkingError);
		e.references = [];
	}
	getCandidate(e) {
		return this.scopeProvider.getScope(e).getElement(e.reference.$refText) ?? this.createLinkingError(e);
	}
	getCandidates(e) {
		let t = this.scopeProvider.getScope(e).getElements(e.reference.$refText).distinct((e) => `${e.documentUri}#${e.path}`).toArray();
		return t.length > 0 ? t : this.createLinkingError(e);
	}
	buildReference(i, a, o, s) {
		let u = this, d = {
			$refNode: o,
			$refText: s,
			_ref: void 0,
			get ref() {
				if (e(this._ref)) return this._ref;
				if (t(this._nodeDescription)) this._ref = u.loadAstNode(this._nodeDescription) ?? u.createLinkingError({
					reference: d,
					container: i,
					property: a
				}, this._nodeDescription);
				else if (this._ref === void 0) {
					this._ref = l;
					let e = r(i).$document, t = u.getLinkedNode({
						reference: d,
						container: i,
						property: a
					});
					if (t.error && e && e.state < c.ComputedScopes) {
						this._ref = void 0;
						return;
					}
					this._ref = t.node ?? t.error, this._nodeDescription = t.descr, e?.references.push(this);
				} else this._ref === l && u.throwCyclicReferenceError(i, a, s);
				return e(this._ref) ? this._ref : void 0;
			},
			get $nodeDescription() {
				return this._nodeDescription;
			},
			get error() {
				return n(this._ref) ? this._ref : void 0;
			}
		};
		return d;
	}
	buildMultiReference(e, t, i, a) {
		let o = this, s = {
			$refNode: i,
			$refText: a,
			_items: void 0,
			get items() {
				if (Array.isArray(this._items)) return this._items;
				if (this._items === void 0) {
					this._items = l;
					let i = r(e).$document, a = o.getCandidates({
						reference: s,
						container: e,
						property: t
					}), c = [];
					if (n(a)) this._linkingError = a;
					else for (let e of a) {
						let t = o.loadAstNode(e);
						t && c.push({
							ref: t,
							$nodeDescription: e
						});
					}
					this._items = c, i?.references.push(this);
				} else this._items === l && o.throwCyclicReferenceError(e, t, a);
				return Array.isArray(this._items) ? this._items : [];
			},
			get error() {
				if (this._linkingError) return this._linkingError;
				if (!(this.items.length > 0)) return this._linkingError = o.createLinkingError({
					reference: s,
					container: e,
					property: t
				});
			}
		};
		return s;
	}
	throwCyclicReferenceError(e, t, n) {
		throw Error(`Cyclic reference resolution detected: ${this.astNodeLocator.getAstNodePath(e)}/${t} (symbol '${n}')`);
	}
	getLinkedNode(e) {
		try {
			let t = this.getCandidate(e);
			if (n(t)) return { error: t };
			let r = this.loadAstNode(t);
			return r ? {
				node: r,
				descr: t
			} : {
				descr: t,
				error: this.createLinkingError(e, t)
			};
		} catch (t) {
			console.error(`An error occurred while resolving reference to '${e.reference.$refText}':`, t);
			let n = t.message ?? String(t);
			return { error: {
				info: e,
				message: `An error occurred while resolving reference to '${e.reference.$refText}': ${n}`
			} };
		}
	}
	loadAstNode(e) {
		if (e.node) return e.node;
		let t = this.langiumDocuments().getDocument(e.documentUri);
		if (t) return this.astNodeLocator.getAstNode(t.parseResult.value, e.path);
	}
	createLinkingError(e, t) {
		let n = r(e.container).$document;
		return n && n.state < c.ComputedScopes && console.warn(`Attempted reference resolution before document reached ComputedScopes state (${n.uri}).`), {
			info: e,
			message: `Could not resolve reference to ${this.reflection.getReferenceType(e)} named '${e.reference.$refText}'.`,
			targetDescription: t
		};
	}
};
//#endregion
export { u as DefaultLinker };
