import { DocumentState } from "langium";
import { isNullish } from "remeda";
import { DiagnosticSeverity } from "vscode-languageserver-types";
import { ast } from "../ast.js";
import { logger } from "../logger.js";
import { deployedInstanceChecks, deploymentNodeChecks, deploymentRelationChecks } from "./deployment-checks.js";
import { dynamicViewRulePredicate } from "./dynamic-view-rule.js";
import { dynamicViewStep } from "./dynamic-view-step.js";
import { elementChecks } from "./element.js";
import { iconPropertyRuleChecks, notesPropertyRuleChecks, opacityPropertyRuleChecks } from "./property-checks.js";
import { relationBodyChecks, relationChecks } from "./relation.js";
import {
  deploymentNodeKindChecks,
  elementKindChecks,
  globalPredicateChecks,
  globalsChecks,
  globalStyleIdChecks,
  modelRuleChecks,
  relationshipChecks,
  specificationRuleChecks,
  tagChecks
} from "./specification.js";
import { viewChecks } from "./view.js";
import {
  elementPredicateWithChecks,
  expandElementExprChecks,
  fqnRefExprChecks,
  incomingExpressionChecks,
  outgoingExpressionChecks,
  relationExprChecks,
  relationPredicateWithChecks
} from "./view-predicates/index.js";
function validatableAstNodeGuards(predicates) {
  return (n) => predicates.some((p) => p(n));
}
const isValidatableAstNode = validatableAstNodeGuards([
  ast.isGlobals,
  ast.isGlobalPredicateGroup,
  ast.isGlobalDynamicPredicateGroup,
  ast.isGlobalStyle,
  ast.isGlobalStyleGroup,
  ast.isDynamicViewPredicateIterator,
  ast.isElementPredicateWith,
  ast.isRelationPredicateWith,
  ast.isElementExpression,
  ast.isRelationExpression,
  ast.isDynamicViewParallelSteps,
  ast.isDynamicViewStep,
  ast.isDeploymentViewRule,
  ast.isDeploymentViewRulePredicate,
  ast.isExpressionV2,
  ast.isRelationExpr,
  ast.isFqnRefExpr,
  ast.isViewProperty,
  ast.isStyleProperty,
  ast.isPredicate,
  ast.isTags,
  ast.isViewRule,
  ast.isDynamicViewRule,
  ast.isLikeC4View,
  ast.isViewRuleStyleOrGlobalRef,
  ast.isDeployedInstance,
  ast.isDeploymentNode,
  ast.isDeploymentRelation,
  ast.isRelationshipStyleProperty,
  ast.isMetadataProperty,
  ast.isRelation,
  ast.isElementProperty,
  ast.isStringProperty,
  ast.isNavigateToProperty,
  ast.isElement,
  ast.isExtendElement,
  ast.isSpecificationElementKind,
  ast.isSpecificationRelationshipKind,
  ast.isSpecificationDeploymentNodeKind,
  ast.isSpecificationTag,
  ast.isSpecificationColor,
  ast.isSpecificationRule
]);
const findInvalidContainer = (node) => {
  let nd = node;
  while (nd && !ast.isLikeC4Grammar(nd)) {
    if (isValidatableAstNode(nd)) {
      return nd;
    }
    nd = nd.$container;
  }
  return void 0;
};
export function checksFromDiagnostics(doc) {
  const errors = doc.state >= DocumentState.Validated ? doc.diagnostics?.filter((d) => d.severity === DiagnosticSeverity.Error) ?? [] : [];
  const invalidNodes = /* @__PURE__ */ new WeakSet();
  for (const { node } of errors) {
    if (isNullish(node) || invalidNodes.has(node)) {
      continue;
    }
    invalidNodes.add(node);
    const container = findInvalidContainer(node);
    if (container) {
      invalidNodes.add(container);
    }
  }
  const isValid = (n) => !invalidNodes.has(n);
  return {
    isValid,
    invalidNodes
  };
}
export function registerValidationChecks(services) {
  logger.debug("registerValidationChecks");
  const registry = services.validation.ValidationRegistry;
  registry.register({
    DeployedInstance: deployedInstanceChecks(services),
    DeploymentNodeKind: deploymentNodeKindChecks(services),
    DeploymentNode: deploymentNodeChecks(services),
    DeploymentRelation: deploymentRelationChecks(services),
    FqnRefExpr: fqnRefExprChecks(services),
    RelationExpr: relationExprChecks(services),
    NotesProperty: notesPropertyRuleChecks(services),
    OpacityProperty: opacityPropertyRuleChecks(services),
    IconProperty: iconPropertyRuleChecks(services),
    SpecificationRule: specificationRuleChecks(services),
    Model: modelRuleChecks(services),
    Globals: globalsChecks(services),
    GlobalPredicateGroup: globalPredicateChecks(services),
    GlobalDynamicPredicateGroup: globalPredicateChecks(services),
    GlobalStyleId: globalStyleIdChecks(services),
    DynamicViewStep: dynamicViewStep(services),
    LikeC4View: viewChecks(services),
    Element: elementChecks(services),
    ElementKind: elementKindChecks(services),
    Relation: relationChecks(services),
    RelationBody: relationBodyChecks(services),
    Tag: tagChecks(services),
    DynamicViewPredicateIterator: dynamicViewRulePredicate(services),
    ElementPredicateWith: elementPredicateWithChecks(services),
    RelationPredicateWith: relationPredicateWithChecks(services),
    ExpandElementExpression: expandElementExprChecks(services),
    RelationshipKind: relationshipChecks(services),
    IncomingRelationExpression: incomingExpressionChecks(services),
    OutgoingRelationExpression: outgoingExpressionChecks(services)
  });
  const connection = services.shared.lsp.Connection;
  if (connection) {
    services.shared.workspace.DocumentBuilder.onUpdate((_, deleted) => {
      for (const uri of deleted) {
        logger.debug(`clear diagnostics for deleted ${uri.path}`);
        void connection.sendDiagnostics({
          uri: uri.toString(),
          diagnostics: []
        });
      }
    });
  }
}
