const t = require('@babel/types')

const {
  VAR_FILTER
} = require('../../constants')

const GLOBAL_METHODS = [
  'parseInt',
  'parseFloat',
  'isNaN',
  'isFinite',
  'decodeURI',
  'decodeURIComponent',
  'encodeURI',
  'encodeURIComponent'
]

const GLOBAL_OBJECTS = {
  Math: [
    'abs',
    'acos',
    'asin',
    'atan',
    'atan2',
    'ceil',
    'cos',
    'exp',
    'floor',
    'log',
    'max',
    'min',
    'pow',
    'random',
    'round',
    'sin',
    'sqrt',
    'tan'
  ],
  JSON: [
    'stringify',
    'parse'
  ]
}

const BUILT_IN_METHODS = [
  // number
  'toString',
  'toLocaleString',
  'valueOf',
  'toFixed',
  'toExponential',
  'toPrecision',
  // string
  // 'toString',
  // 'valueOf',
  'charAt',
  'charCodeAt',
  'concat',
  'indexOf',
  'lastIndexOf',
  'localeCompare',
  'match',
  'replace',
  'search',
  'slice',
  'split',
  'substring',
  'toLowerCase',
  'toLocaleLowerCase',
  'toUpperCase',
  'toLocaleUpperCase',
  'trim',
  // boolean
  // 'toString',
  // 'valueOf',
  // object
  // 'toString',
  // function
  // 'toString',
  // array
  // 'toString',
  // 'concat',
  'join',
  'pop',
  'push',
  'reverse',
  'shift',
  // 'slice',
  'sort',
  'splice',
  'unshift',
  // 'indexOf',
  // 'lastIndexOf',
  'every',
  'some',
  'forEach',
  'map',
  'filter',
  'reduce',
  'reduceRight'
]

function getGlobalMethodFilter (callExpr) {
  const callee = callExpr.callee
  if (callee) {
    const name = callee.name
    if (name && GLOBAL_METHODS.includes(name)) {
      return t.callExpression(
        t.memberExpression(
          t.identifier(VAR_FILTER),
          t.identifier(name)
        ),
        callExpr.arguments
      )
    }
  }
  return false
}

function getGlobalObjectFilter (callExpr) {
  const callee = callExpr.callee
  if (t.isMemberExpression(callee)) {
    const object = callee.object
    const property = callee.property
    const propertyName = property.name || property.value
    const methods = GLOBAL_OBJECTS[object.name]
    if (methods && methods.includes(propertyName)) {
      return t.callExpression(
        t.memberExpression(
          t.identifier(VAR_FILTER),
          t.identifier(propertyName)
        ),
        callExpr.arguments
      )
    }
  }
  return false
}

function getMemberFilter (callExpr) {
  const callee = callExpr.callee
  if (t.isMemberExpression(callee)) {
    const property = callee.property
    const propertyName = property.name || property.value
    if (BUILT_IN_METHODS.includes(propertyName)) {
      return t.callExpression(
        t.memberExpression(
          t.identifier(VAR_FILTER),
          t.identifier(propertyName)
        ),
        [
          callee.object,
          ...callExpr.arguments
        ]
      )
    }
  }
}

function processFilter (callExpr, path) {
  const globalMethodFilter = getGlobalMethodFilter(callExpr)
  if (globalMethodFilter) {
    path.replaceWith(globalMethodFilter)
    return true
  }
  const globalObjectFilter = getGlobalObjectFilter(callExpr)
  if (globalObjectFilter) {
    path.replaceWith(globalObjectFilter)
    return true
  }
  const memberFilter = getMemberFilter(callExpr)
  if (memberFilter) {
    path.replaceWith(memberFilter)
    return true
  }
  return false
}

module.exports = processFilter
