import { AstUtils, GrammarUtils } from "langium";
import { isString } from "remeda";
import { ast, isParsedLikeC4LangiumDocument } from "../ast.js";
const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
const { getDocument } = AstUtils;
export class LikeC4ModelLocator {
  constructor(services) {
    this.services = services;
    this.fqnIndex = services.likec4.FqnIndex;
    this.deploymentsIndex = services.likec4.DeploymentsIndex;
    this.langiumDocuments = services.shared.workspace.LangiumDocuments;
  }
  fqnIndex;
  deploymentsIndex;
  langiumDocuments;
  documents() {
    return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
  }
  getParsedElement(astNodeOrFqn) {
    if (isString(astNodeOrFqn)) {
      const fqn2 = astNodeOrFqn;
      const entry = this.fqnIndex.byFqn(astNodeOrFqn).head();
      if (!entry) {
        return null;
      }
      const doc2 = this.langiumDocuments.getDocument(entry.documentUri);
      if (!doc2 || !isParsedLikeC4LangiumDocument(doc2)) {
        return null;
      }
      return doc2.c4Elements.find((e) => e.id === fqn2) ?? null;
    }
    const fqn = this.fqnIndex.getFqn(astNodeOrFqn);
    if (!fqn) return null;
    const doc = getDocument(astNodeOrFqn);
    if (!isParsedLikeC4LangiumDocument(doc)) {
      return null;
    }
    return doc.c4Elements.find((e) => e.id === fqn) ?? null;
  }
  locateElement(fqn, _prop) {
    const entry = this.fqnIndex.byFqn(fqn).head();
    const docsegment = entry?.nameSegment ?? entry?.selectionSegment;
    if (!entry || !docsegment) {
      return null;
    }
    return {
      uri: entry.documentUri.toString(),
      range: docsegment.range
    };
  }
  locateDeploymentElement(fqn, _prop) {
    const entry = this.deploymentsIndex.byFqn(fqn).head();
    const docsegment = entry?.nameSegment ?? entry?.selectionSegment;
    if (!entry || !docsegment) {
      return null;
    }
    return {
      uri: entry.documentUri.toString(),
      range: docsegment.range
    };
  }
  locateRelation(relationId) {
    for (const doc of this.documents()) {
      const relation = doc.c4Relations.find((r) => r.id === relationId) ?? doc.c4DeploymentRelations.find((r) => r.id === relationId);
      if (!relation) {
        continue;
      }
      const node = this.services.workspace.AstNodeLocator.getAstNode(
        doc.parseResult.value,
        relation.astPath
      );
      if (!ast.isRelation(node) && !ast.isDeploymentRelation(node)) {
        continue;
      }
      let targetNode = node.title ? findNodeForProperty(node.$cstNode, "title") : void 0;
      targetNode ??= node.kind ? findNodeForProperty(node.$cstNode, "kind") : void 0;
      targetNode ??= findNodeForProperty(node.$cstNode, "target");
      targetNode ??= node.$cstNode;
      if (!targetNode) {
        continue;
      }
      return {
        uri: doc.uri.toString(),
        range: targetNode.range
      };
    }
    return null;
  }
  locateViewAst(viewId) {
    for (const doc of this.documents()) {
      const view = doc.c4Views.find((r) => r.id === viewId);
      if (!view) {
        continue;
      }
      const viewAst = this.services.workspace.AstNodeLocator.getAstNode(
        doc.parseResult.value,
        view.astPath
      );
      if (ast.isLikeC4View(viewAst)) {
        return {
          doc,
          view,
          viewAst
        };
      }
    }
    return null;
  }
  locateView(viewId) {
    const res = this.locateViewAst(viewId);
    if (!res) {
      return null;
    }
    const node = res.viewAst;
    let targetNode = node.name ? findNodeForProperty(node.$cstNode, "name") : void 0;
    targetNode ??= findNodeForKeyword(node.$cstNode, "view");
    targetNode ??= node.$cstNode;
    if (!targetNode) {
      return null;
    }
    return {
      uri: res.doc.uri.toString(),
      range: targetNode.range
    };
  }
}
