"use strict";

exports.__esModule = true;
exports.default = _default;

var _pluginSyntaxDecorators = _interopRequireDefault(require("@babel/plugin-syntax-decorators"));

var _core = require("@babel/core");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var buildClassDecorator = (0, _core.template)("\n  DECORATOR(CLASS_REF = INNER) || CLASS_REF;\n");
var buildClassPrototype = (0, _core.template)("\n  CLASS_REF.prototype;\n");
var buildGetDescriptor = (0, _core.template)("\n    Object.getOwnPropertyDescriptor(TARGET, PROPERTY);\n");
var buildGetObjectInitializer = (0, _core.template)("\n    (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY), (TEMP = TEMP ? TEMP.value : undefined), {\n        enumerable: true,\n        configurable: true,\n        writable: true,\n        initializer: function(){\n            return TEMP;\n        }\n    })\n");

function _default() {
  var WARNING_CALLS = new WeakSet();

  function applyEnsureOrdering(path) {
    var decorators = (path.isClass() ? [path].concat(path.get("body.body")) : path.get("properties")).reduce(function (acc, prop) {
      return acc.concat(prop.node.decorators || []);
    }, []);
    var identDecorators = decorators.filter(function (decorator) {
      return !_core.types.isIdentifier(decorator.expression);
    });
    if (identDecorators.length === 0) return;
    return _core.types.sequenceExpression(identDecorators.map(function (decorator) {
      var expression = decorator.expression;
      var id = decorator.expression = path.scope.generateDeclaredUidIdentifier("dec");
      return _core.types.assignmentExpression("=", id, expression);
    }).concat([path.node]));
  }

  function applyClassDecorators(classPath) {
    if (!hasClassDecorators(classPath.node)) return;
    var decorators = classPath.node.decorators || [];
    classPath.node.decorators = null;
    var name = classPath.scope.generateDeclaredUidIdentifier("class");
    return decorators.map(function (dec) {
      return dec.expression;
    }).reverse().reduce(function (acc, decorator) {
      return buildClassDecorator({
        CLASS_REF: _core.types.cloneNode(name),
        DECORATOR: _core.types.cloneNode(decorator),
        INNER: acc
      }).expression;
    }, classPath.node);
  }

  function hasClassDecorators(classNode) {
    return !!(classNode.decorators && classNode.decorators.length);
  }

  function applyMethodDecorators(path, state) {
    if (!hasMethodDecorators(path.node.body.body)) return;
    return applyTargetDecorators(path, state, path.node.body.body);
  }

  function hasMethodDecorators(body) {
    return body.some(function (node) {
      return node.decorators && node.decorators.length;
    });
  }

  function applyObjectDecorators(path, state) {
    if (!hasMethodDecorators(path.node.properties)) return;
    return applyTargetDecorators(path, state, path.node.properties);
  }

  function applyTargetDecorators(path, state, decoratedProps) {
    var name = path.scope.generateDeclaredUidIdentifier(path.isClass() ? "class" : "obj");
    var exprs = decoratedProps.reduce(function (acc, node) {
      var decorators = node.decorators || [];
      node.decorators = null;
      if (decorators.length === 0) return acc;

      if (node.computed) {
        throw path.buildCodeFrameError("Computed method/property decorators are not yet supported.");
      }

      var property = _core.types.isLiteral(node.key) ? node.key : _core.types.stringLiteral(node.key.name);
      var target = path.isClass() && !node.static ? buildClassPrototype({
        CLASS_REF: name
      }).expression : name;

      if (_core.types.isClassProperty(node, {
        static: false
      })) {
        var descriptor = path.scope.generateDeclaredUidIdentifier("descriptor");
        var initializer = node.value ? _core.types.functionExpression(null, [], _core.types.blockStatement([_core.types.returnStatement(node.value)])) : _core.types.nullLiteral();
        node.value = _core.types.callExpression(state.addHelper("initializerWarningHelper"), [descriptor, _core.types.thisExpression()]);
        WARNING_CALLS.add(node.value);
        acc = acc.concat([_core.types.assignmentExpression("=", descriptor, _core.types.callExpression(state.addHelper("applyDecoratedDescriptor"), [_core.types.cloneNode(target), _core.types.cloneNode(property), _core.types.arrayExpression(decorators.map(function (dec) {
          return _core.types.cloneNode(dec.expression);
        })), _core.types.objectExpression([_core.types.objectProperty(_core.types.identifier("enumerable"), _core.types.booleanLiteral(true)), _core.types.objectProperty(_core.types.identifier("initializer"), initializer)])]))]);
      } else {
        acc = acc.concat(_core.types.callExpression(state.addHelper("applyDecoratedDescriptor"), [_core.types.cloneNode(target), _core.types.cloneNode(property), _core.types.arrayExpression(decorators.map(function (dec) {
          return _core.types.cloneNode(dec.expression);
        })), _core.types.isObjectProperty(node) || _core.types.isClassProperty(node, {
          static: true
        }) ? buildGetObjectInitializer({
          TEMP: path.scope.generateDeclaredUidIdentifier("init"),
          TARGET: _core.types.cloneNode(target),
          PROPERTY: _core.types.cloneNode(property)
        }).expression : buildGetDescriptor({
          TARGET: _core.types.cloneNode(target),
          PROPERTY: _core.types.cloneNode(property)
        }).expression, _core.types.cloneNode(target)]));
      }

      return acc;
    }, []);
    return _core.types.sequenceExpression([_core.types.assignmentExpression("=", _core.types.cloneNode(name), path.node), _core.types.sequenceExpression(exprs), _core.types.cloneNode(name)]);
  }

  return {
    inherits: _pluginSyntaxDecorators.default,
    visitor: {
      ClassDeclaration: function ClassDeclaration(path) {
        var node = path.node;

        if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) {
          return;
        }

        var ref = node.id ? _core.types.cloneNode(node.id) : path.scope.generateUidIdentifier("class");

        var letDeclaration = _core.types.variableDeclaration("let", [_core.types.variableDeclarator(ref, _core.types.toExpression(node))]);

        if (path.parentPath.isExportDefaultDeclaration()) {
          path.parentPath.replaceWithMultiple([letDeclaration, _core.types.exportNamedDeclaration(null, [_core.types.exportSpecifier(_core.types.cloneNode(ref), _core.types.identifier("default"))])]);
        } else {
          path.replaceWith(letDeclaration);
        }
      },
      ClassExpression: function ClassExpression(path, state) {
        var decoratedClass = applyEnsureOrdering(path) || applyClassDecorators(path, state) || applyMethodDecorators(path, state);
        if (decoratedClass) path.replaceWith(decoratedClass);
      },
      ObjectExpression: function ObjectExpression(path, state) {
        var decoratedObject = applyEnsureOrdering(path) || applyObjectDecorators(path, state);
        if (decoratedObject) path.replaceWith(decoratedObject);
      },
      AssignmentExpression: function AssignmentExpression(path, state) {
        if (!WARNING_CALLS.has(path.node.right)) return;
        path.replaceWith(_core.types.callExpression(state.addHelper("initializerDefineProperty"), [_core.types.cloneNode(path.get("left.object").node), _core.types.stringLiteral(path.get("left.property").node.name), _core.types.cloneNode(path.get("right.arguments")[0].node), _core.types.cloneNode(path.get("right.arguments")[1].node)]));
      }
    }
  };
}