/**
 * @author Pig Fang
 * See LICENSE file in root directory for full license.
 */
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const utils = require('../utils')
const casing = require('../utils/casing')

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

/**
 * @param {import('../../typings/eslint-plugin-vue/util-types/ast').Expression} node
 * @returns {string | null}
 */
function getOptionsComponentName(node) {
  if (node.type === 'Identifier') {
    return node.name
  }
  if (node.type === 'Literal') {
    return typeof node.value === 'string' ? node.value : null
  }
  return null
}

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

module.exports = {
  meta: {
    type: 'suggestion',
    docs: {
      description:
        'enforce the casing of component name in `components` options',
      categories: undefined,
      url: 'https://eslint.vuejs.org/rules/component-options-name-casing.html'
    },
    fixable: 'code',
    hasSuggestions: true,
    schema: [{ enum: casing.allowedCaseOptions }],
    messages: {
      caseNotMatched: 'Component name "{{component}}" is not {{caseType}}.',
      possibleRenaming: 'Rename component name to be in {{caseType}}.'
    }
  },
  /** @param {RuleContext} context */
  create(context) {
    const caseType = context.options[0] || 'PascalCase'

    const canAutoFix = caseType === 'PascalCase'
    const checkCase = casing.getChecker(caseType)
    const convert = casing.getConverter(caseType)

    return utils.executeOnVue(context, (obj) => {
      const node = utils.findProperty(obj, 'components')
      if (!node || node.value.type !== 'ObjectExpression') {
        return
      }

      node.value.properties.forEach((property) => {
        if (property.type !== 'Property') {
          return
        }

        const name = getOptionsComponentName(property.key)
        if (!name || checkCase(name)) {
          return
        }

        context.report({
          node: property.key,
          messageId: 'caseNotMatched',
          data: {
            component: name,
            caseType
          },
          fix: canAutoFix
            ? (fixer) => {
                const converted = convert(name)
                return property.shorthand
                  ? fixer.replaceText(property, `${converted}: ${name}`)
                  : fixer.replaceText(property.key, converted)
              }
            : undefined,
          suggest: canAutoFix
            ? undefined
            : [
                {
                  messageId: 'possibleRenaming',
                  data: { caseType },
                  fix: (fixer) => {
                    const converted = convert(name)
                    if (caseType === 'kebab-case') {
                      return property.shorthand
                        ? fixer.replaceText(property, `'${converted}': ${name}`)
                        : fixer.replaceText(property.key, `'${converted}'`)
                    }
                    return property.shorthand
                      ? fixer.replaceText(property, `${converted}: ${name}`)
                      : fixer.replaceText(property.key, converted)
                  }
                }
              ]
        })
      })
    })
  }
}
