'use strict';
const kebabCase = require('kebab-case');
const rules = require('./rules');

const forbiddenLibs = ['lodash', 'lodash/fp', 'lodash-es'];

function getAssignmentLeftHandSide(node) {
  // For VariableDeclarator nodes, the left hand side is called `id`
  // The `x` on `var x = 3;
  if (node.type === 'VariableDeclarator') {
    return node.id;
  }
  // For AssignmentExpression nodes, the left hand side is called `left`
  // The `x` on `x = 3;
  if (node.type === 'AssignmentExpression') {
    return node.left;
  }
  return null;
}

for (const rule in rules) {
  const alternative = rules[rule].alternative;
  const ruleName = rules[rule].ruleName || kebabCase(rule);
  const forbiddenImports = {
    [`lodash/${rule}`]: 1,
    [`lodash/fp/${rule}`]: 1,
    [`lodash-es/${rule}`]: 1,
    [`lodash.${rule.toLowerCase()}`]: 1,
  };

  module.exports[ruleName] = {
    create(context) {
      return {
        CallExpression(node) {
          const callee = node.callee;
          const objectName = callee.name || (callee.object && callee.object.name) || (callee.object && callee.object.callee && callee.object.callee.name);

          if (objectName === 'require' && node.arguments.length === 1) {
            const requiredModuleName = node.arguments[0].value;
            const { parent } = node;
            if (forbiddenLibs.includes(requiredModuleName)) {
              const leftHandSide = getAssignmentLeftHandSide(parent);
              // ex: const { indexOf } = require('lodash');
              // ex: ({ indexOf } = require('lodash'));
              if (leftHandSide && leftHandSide.type === 'ObjectPattern') {
                leftHandSide.properties.forEach(property => {
                  if (property.key.name === rule) {
                    context.report({
                      node,
                      message: `{ ${rule} } = require('${requiredModuleName}') detected. Consider using the native ${alternative}`
                    });
                  }
                });
              }
            } else if (forbiddenImports.hasOwnProperty(requiredModuleName)) {
              // ex: const indexOf = require('lodash.indexof');
              // ex: const indexOf = require('lodash/indexOf');
              context.report({
                node,
                message: `require('${requiredModuleName}') detected. Consider using the native ${alternative}`
              });
            }
          } else if ((objectName === '_' || objectName === 'lodash' || objectName === 'underscore') && callee.property && callee.property.name === rule) {
            context.report({
              node,
              message: `Consider using the native ${alternative}`
            });
          }
        },
        ImportDeclaration(node) {
          if (forbiddenLibs.includes(node.source.value)) {
            // ex: import { indexOf } from 'lodash';
            // ex: import { indexOf as x } from 'lodash';
            node.specifiers.forEach(specifier => {
              if (specifier.type === 'ImportSpecifier' && specifier.imported.name === rule) {
                context.report({
                  node,
                  message: `Import { ${rule} } from '${node.source.value}' detected. Consider using the native ${alternative}`
                });
              }
            });
          } else if (forbiddenImports.hasOwnProperty(node.source.value)) {
            // ex: import indexOf from 'lodash/indexOf';
            // ex: import indexOf from 'lodash.indexof';
            context.report({
              node,
              message: `Import from '${node.source.value}' detected. Consider using the native ${alternative}`
            });
          }
        }
      };
    }
  };
}
