const path = require('path')

const isWin = /^win/.test(process.platform)

const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path)

function resolve(...args) {
  return normalizePath(path.resolve.apply(path, [__dirname, ...args]))
}

const srcPath = resolve('../../src/')
const protocolPath = resolve('../../src/core/helpers/protocol')
const coreApiPath = resolve('../../src/core/service/api')
const platformApiPath = resolve('../../src/platforms/' + process.env.UNI_PLATFORM + '/service/api')

const apis = require('../apis')

process.UNI_SERVICE_API_MANIFEST = Object.create(null)
process.UNI_SERVICE_API_PROTOCOL = Object.create(null)

function parseProtocolExport({
  file,
  exports
}) {
  const filepath = file.replace(srcPath, '')
  exports.forEach(exportName => {
    if (process.UNI_SERVICE_API_PROTOCOL[exportName]) {
      console.warn(`API[${exportName}] 冲突:`)
      console.warn(process.UNI_SERVICE_API_PROTOCOL[exportName])
      console.warn(filepath)
    } else {
      process.UNI_SERVICE_API_PROTOCOL[exportName] = filepath
    }
  })
}

function parseApiExport({
  file,
  methods,
  exports,
  isPlatform
}) {
  const deps = []
  methods && methods.forEach(method => {
    deps.push(['', method])
  })
  const filepath = file.replace(srcPath, '')
  exports.forEach(exportName => {
    if (process.UNI_SERVICE_API_MANIFEST[exportName]) {
      console.warn('\n')
      console.warn(`API[${exportName}] 冲突:`)
      console.warn(process.UNI_SERVICE_API_MANIFEST[exportName][0])
      console.warn(filepath)
      if (isPlatform) { //  优先使用 platform
        process.UNI_SERVICE_API_MANIFEST[exportName] = [filepath, deps]
        console.warn(`优先使用` + filepath)
      }
    } else {
      process.UNI_SERVICE_API_MANIFEST[exportName] = [filepath, deps]
    }
  })
}

const CONTEXTS = ['VideoContext', 'MapContext', 'EditorContext', 'CanvasContext']

function parseExports(node, t, file) {
  if (t.isFunctionDeclaration(node)) {
    return [node.id.name]
  } else if (
    t.isVariableDeclaration(node) &&
    node.declarations.length === 1
  ) {
    return [node.declarations[0].id.name]
  } else if (Array.isArray(node) && node.length) {
    return node.map(specifier => {
      return specifier.exported.name
    })
  } else {
    if (t.isClassDeclaration(node) && CONTEXTS.includes(node.id.name)) {
      // ignore
      return
    }
    console.warn('\n')
    console.warn(`${file} 解析 export 失败`, node)
  }
}

module.exports = function({
  types: t
}) {
  return {
    visitor: {
      Program: {
        enter(path, state) {
          state.file.opts.file = normalizePath(state.file.opts.filename)
          state.file.opts.isCore = state.file.opts.file.indexOf(coreApiPath) === 0
          state.file.opts.isPlatform = state.file.opts.file.indexOf(platformApiPath) === 0
          state.file.opts.isProtocol = state.file.opts.file.indexOf(protocolPath) === 0
        },
        exit(path, state) {
          const {
            exports,
            isProtocol
          } = state.file.opts
          if (exports && exports.length) {
            if (isProtocol) {
              parseProtocolExport(state.file.opts)
            } else {
              parseApiExport(state.file.opts)
            }
          }
        }
      },
      ExportNamedDeclaration(path, state) {
        const {
          file,
          isCore,
          isPlatform,
          isProtocol
        } = state.file.opts
        if (isCore || isPlatform || isProtocol) {
          const exports = parseExports(path.node.declaration || path.node.specifiers, t, file)
          if (Array.isArray(exports)) {
            (state.file.opts.exports || (state.file.opts.exports = [])).push(...exports)
          }
        }
      },
      CallExpression(path, state) {
        const {
          file,
          isCore,
          isPlatform
        } = state.file.opts

        if (
          isCore &&
          path.node.callee.name === 'invokeMethod'
        ) {
          (state.file.opts.methods || (state.file.opts.methods = new Set())).add(path.node.arguments[0].value)
        }
      }
    }
  }
}
