import { nonexhaustive, nonNullable } from "@likec4/core";
import { isNonNullish } from "remeda";
import { ast } from "../../ast.js";
import { logWarnError } from "../../logger.js";
import { instanceRef } from "../../utils/fqnRef.js";
import { parseWhereClause } from "../model-parser-where.js";
export function ExpressionV2Parser(B) {
  return class ExpressionV2Parser extends B {
    parseFqnRef(astNode) {
      const refValue = nonNullable(
        astNode.value.ref,
        `FqnRef is empty ${astNode.$cstNode?.range.start.line}:${astNode.$cstNode?.range.start.character}`
      );
      if (ast.isElement(refValue)) {
        const deployedInstanceAst = instanceRef(astNode);
        if (!deployedInstanceAst) {
          return {
            model: this.resolveFqn(refValue)
          };
        }
        const deployment = this.resolveFqn(deployedInstanceAst);
        const element = this.resolveFqn(refValue);
        return {
          deployment,
          element
        };
      }
      if (ast.isDeploymentElement(refValue)) {
        return {
          deployment: this.resolveFqn(refValue)
        };
      }
      nonexhaustive(refValue);
    }
    parseFqnExpr(astNode) {
      if (ast.isWildcardExpression(astNode)) {
        return {
          wildcard: true
        };
      }
      if (ast.isFqnRefExpr(astNode)) {
        return this.parseFqnRefExpr(astNode);
      }
      nonexhaustive(astNode);
    }
    parseFqnRefExpr(astNode) {
      const ref = this.parseFqnRef(astNode.ref);
      switch (true) {
        case astNode.selector === "._":
          return {
            ref,
            selector: "expanded"
          };
        case astNode.selector === ".**":
          return {
            ref,
            selector: "descendants"
          };
        case astNode.selector === ".*":
          return {
            ref,
            selector: "children"
          };
        default:
          return { ref };
      }
    }
    parseFqnExpressions(astNode) {
      const exprs = [];
      let iter = astNode;
      while (iter) {
        try {
          if (isNonNullish(iter.value) && this.isValid(iter.value)) {
            exprs.push(this.parseFqnExpr(iter.value));
          }
        } catch (e) {
          logWarnError(e);
        }
        iter = iter.prev;
      }
      return exprs.reverse();
    }
    parseRelationWhereExpr(astNode) {
      return {
        where: {
          expr: this.parseRelationExpr(astNode.subject),
          condition: astNode.where ? parseWhereClause(astNode.where) : {
            kind: { neq: "--always-true--" }
          }
        }
      };
    }
    parseRelationExpr(astNode) {
      if (ast.isRelationPredicateWhere(astNode)) {
      }
      if (ast.isDirectedRelationExpr(astNode)) {
        return {
          source: this.parseFqnExpr(astNode.source.from),
          target: this.parseFqnExpr(astNode.target),
          isBidirectional: astNode.source.isBidirectional
        };
      }
      if (ast.isInOutRelationExpr(astNode)) {
        return {
          inout: this.parseFqnExpr(astNode.inout.to)
        };
      }
      if (ast.isOutgoingRelationExpr(astNode)) {
        return {
          outgoing: this.parseFqnExpr(astNode.from)
        };
      }
      if (ast.isIncomingRelationExpr(astNode)) {
        return {
          incoming: this.parseFqnExpr(astNode.to)
        };
      }
      nonexhaustive(astNode);
    }
  };
}
