export default function({ messages, types: t }) {
  /**
   * Helper function to run a statement before an expression by replacing it with a comma expression
   * and wrapping the statement in an IIFE as the first operand.
   */
  function statementBeforeExpression(statement, expression) {
    return t.sequenceExpression([
      t.callExpression(
        t.functionExpression(null, [], t.blockStatement([statement])),
        [],
      ),
      expression,
    ]);
  }

  return {
    visitor: {
      Scope({ scope }) {
        for (const name in scope.bindings) {
          const binding = scope.bindings[name];
          if (binding.kind !== "const" && binding.kind !== "module") continue;

          for (const violation of (binding.constantViolations: Array)) {
            const throwNode = t.throwStatement(
              t.newExpression(t.identifier("Error"), [
                t.stringLiteral(messages.get("readOnly", name)),
              ]),
            );

            if (violation.isAssignmentExpression()) {
              violation
                .get("right")
                .replaceWith(
                  statementBeforeExpression(
                    throwNode,
                    violation.get("right").node,
                  ),
                );
            } else if (violation.parentPath.isUpdateExpression()) {
              violation.parentPath.replaceWith(
                statementBeforeExpression(throwNode, violation.parent),
              );
            } else if (violation.parentPath.isForXStatement()) {
              violation.parentPath.ensureBlock();
              violation.parentPath.node.body.body.unshift(throwNode);
            }
          }
        }
      },
    },
  };
}
