/**
 * @fileoverview Enforce event handler naming conventions in JSX
 * @author Jake Marsh
 */

'use strict';

const docsUrl = require('../util/docsUrl');

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
  meta: {
    docs: {
      description: 'Enforce event handler naming conventions in JSX',
      category: 'Stylistic Issues',
      recommended: false,
      url: docsUrl('jsx-handler-names')
    },

    schema: [{
      anyOf: [
        {
          type: 'object',
          properties: {
            eventHandlerPrefix: {type: 'string'},
            eventHandlerPropPrefix: {type: 'string'},
            checkLocalVariables: {type: 'boolean'}
          },
          additionalProperties: false
        }, {
          type: 'object',
          properties: {
            eventHandlerPrefix: {type: 'string'},
            eventHandlerPropPrefix: {
              type: 'boolean',
              enum: [false]
            },
            checkLocalVariables: {type: 'boolean'}
          },
          additionalProperties: false
        }, {
          type: 'object',
          properties: {
            eventHandlerPrefix: {
              type: 'boolean',
              enum: [false]
            },
            eventHandlerPropPrefix: {type: 'string'},
            checkLocalVariables: {type: 'boolean'}
          },
          additionalProperties: false
        }, {
          type: 'object',
          properties: {
            checkLocalVariables: {type: 'boolean'}
          },
          additionalProperties: false
        }
      ]
    }]
  },

  create(context) {
    function isPrefixDisabled(prefix) {
      return prefix === false;
    }

    const configuration = context.options[0] || {};

    const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix) ?
      null :
      configuration.eventHandlerPrefix || 'handle';
    const eventHandlerPropPrefix = isPrefixDisabled(configuration.eventHandlerPropPrefix) ?
      null :
      configuration.eventHandlerPropPrefix || 'on';

    const EVENT_HANDLER_REGEX = !eventHandlerPrefix ?
      null :
      new RegExp(`^((props\\.${eventHandlerPropPrefix || ''})|((.*\\.)?${eventHandlerPrefix}))[A-Z].*$`);
    const PROP_EVENT_HANDLER_REGEX = !eventHandlerPropPrefix ?
      null :
      new RegExp(`^(${eventHandlerPropPrefix}[A-Z].*|ref)$`);

    const checkLocal = !!configuration.checkLocalVariables;

    return {
      JSXAttribute(node) {
        if (!node.value || !node.value.expression || (!checkLocal && !node.value.expression.object)) {
          return;
        }

        const propKey = typeof node.name === 'object' ? node.name.name : node.name;
        const propValue = context.getSourceCode().getText(node.value.expression).replace(/^this\.|.*::/, '');

        if (propKey === 'ref') {
          return;
        }

        const propIsEventHandler = PROP_EVENT_HANDLER_REGEX && PROP_EVENT_HANDLER_REGEX.test(propKey);
        const propFnIsNamedCorrectly = EVENT_HANDLER_REGEX && EVENT_HANDLER_REGEX.test(propValue);

        if (
          propIsEventHandler &&
          propFnIsNamedCorrectly !== null &&
          !propFnIsNamedCorrectly
        ) {
          context.report({
            node,
            message: `Handler function for ${propKey} prop key must begin with '${eventHandlerPrefix}'`
          });
        } else if (
          propFnIsNamedCorrectly &&
          propIsEventHandler !== null &&
          !propIsEventHandler
        ) {
          context.report({
            node,
            message: `Prop key for ${propValue} must begin with '${eventHandlerPropPrefix}'`
          });
        }
      }
    };
  }
};
