import { invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
import { isNonNullish } from "remeda";
import { ast, toAutoLayout, toElementStyle, ViewOps } from "../../ast.js";
import { logWarnError } from "../../logger.js";
import { stringHash } from "../../utils/index.js";
import { parseViewManualLayout } from "../../view-utils/manual-layout.js";
import { removeIndent, toSingleLine } from "./Base.js";
export function DeploymentViewParser(B) {
  return class DeploymentViewParser extends B {
    parseDeploymentView(astNode) {
      const body = astNode.body;
      invariant(body, "DynamicElementView body is not defined");
      const props = body.props.filter(this.isValid);
      const astPath = this.getAstNodePath(astNode);
      let id = astNode.name;
      if (!id) {
        id = "deployment_" + stringHash(
          this.doc.uri.toString(),
          astPath
        );
      }
      const title = toSingleLine(props.find((p) => p.key === "title")?.value) ?? null;
      const description = removeIndent(props.find((p) => p.key === "description")?.value) ?? null;
      const tags = this.convertTags(body);
      const links = this.convertLinks(body);
      ViewOps.writeId(astNode, id);
      const manualLayout = parseViewManualLayout(astNode);
      return {
        __: "deployment",
        id,
        astPath,
        title,
        description,
        tags,
        links: isNonEmptyArray(links) ? links : null,
        rules: body.rules.flatMap((n) => {
          try {
            return this.isValid(n) ? this.parseDeploymentViewRule(n) : [];
          } catch (e) {
            logWarnError(e);
            return [];
          }
        }),
        ...manualLayout && { manualLayout }
      };
    }
    parseDeploymentViewRule(astRule) {
      if (ast.isDeploymentViewRulePredicate(astRule)) {
        return this.parseDeploymentViewRulePredicate(astRule);
      }
      if (ast.isViewRuleAutoLayout(astRule)) {
        return toAutoLayout(astRule);
      }
      if (ast.isDeploymentViewRuleStyle(astRule)) {
        return this.parseDeploymentViewRuleStyle(astRule);
      }
      nonexhaustive(astRule);
    }
    parseDeploymentViewRulePredicate(astRule) {
      const exprs = [];
      let iterator = astRule.expr;
      while (iterator) {
        try {
          const expr = iterator.value;
          if (isNonNullish(expr) && this.isValid(expr)) {
            switch (true) {
              case ast.isFqnExpr(expr):
                exprs.unshift(this.parseFqnExpr(expr));
                break;
              case ast.isRelationExpr(expr):
                exprs.unshift(this.parseRelationExpr(expr));
                break;
              case ast.isRelationPredicateWhereV2(expr):
                exprs.unshift(this.parseRelationWhereExpr(expr));
                break;
              default:
                nonexhaustive(expr);
            }
          }
        } catch (e) {
          logWarnError(e);
        }
        iterator = iterator.prev;
      }
      return astRule.isInclude ? { include: exprs } : { exclude: exprs };
    }
    parseDeploymentViewRuleStyle(astRule) {
      const styleProps = astRule.props.filter(ast.isStyleProperty);
      const notationProperty = astRule.props.find(ast.isNotationProperty);
      const notation = removeIndent(notationProperty?.value);
      const targets = this.parseFqnExpressions(astRule.targets);
      return {
        targets,
        ...notation && { notation },
        style: {
          ...toElementStyle(styleProps, this.isValid)
        }
      };
    }
  };
}
