/**
 * @fileoverview 处理插件
 */
const path = require('path')
const through2 = require('through2')

const config = require('./config')

config.plugins.sort((a, b) => {
  // editable 置于最后面
  if (a === 'editable') return 1
  if (b === 'editable') return -1
  // markdown 置于最前面
  if (a === 'markdown') return -1
  if (b === 'markdown') return 1
  // 剩余任意顺序
  return 0
})

// 提取和替换标签名选择器（组件中仅支持 class 选择器）
const tagSelector = {}
let tagI = 0
if (config.externStyle) {
  config.externStyle = config.externStyle.replace(/[^,\s}]+(?=[^}]*{)/g, $ => {
    if (!/[a-zA-Z_]/.test($[0])) return $
    if (tagSelector[$]) return '.' + tagSelector[$]
    tagSelector[$] = '_' + tagI++
    return '.' + tagSelector[$]
  })
}

module.exports = {
  /**
   * @description 构建插件
   * @param {string} platform 使用平台
   */
  build (platform) {
    const builds = {} // 构建模块
    let pluginImports = '' // 插件引入
    let plugins = '' // 插件列表
    let voidTags = '' // 增加的自闭合标签
    let wxml = '' // 要引入到 node.wxml 中的内容
    let js = '' // 要引入到 node.js 中的内容
    let wxss = config.externStyle // 要引入到 node.wxss 中的内容
    const json = {} // 要引入到 node.json 中的内容

    // 收集插件中要写入模板文件的内容
    for (let i = 0; i < config.plugins.length; i++) {
      const plugin = config.plugins[i]
      let build = {}
      try {
        // 专用 build
        if (platform === 'uni-app') {
          build = require(`../plugins/${plugin}/uni-app/build.js`)
        } else {
          build = require(`../plugins/${plugin}/miniprogram/build.js`)
        }
      } catch (e) { }
      try {
        // 通用 build
        build = Object.assign(require(`../plugins/${plugin}/build.js`), build)
      } catch (e) { }
      // 可以在当前平台使用
      if (!build.platform || build.platform.includes(platform)) {
        builds[plugin] = build
        if (platform === 'uni-app') {
          plugins += plugin.replace(/-([a-z])/g, ($, $1) => $1.toUpperCase()) + ','
          pluginImports += `import ${plugin.replace(/-([a-z])/g, ($, $1) => $1.toUpperCase())} from './${plugin}/${build.main ? build.main : 'index.js'}'\n`
        } else {
          plugins += `require('./${plugin}/${build.main ? build.main : 'index.js'}'),`
        }
        if (build.template) {
          wxml += build.template.replace('wx:if', 'wx:elif').replace('v-if', 'v-else-if')
        }
        if (build.methods) {
          for (const method in build.methods) {
            js += build.methods[method].toString() + ','
          }
        }
        if (build.usingComponents) {
          Object.assign(json, build.usingComponents)
        }
        if (build.style) {
          wxss += build.style
        }
      }
    }

    // 加入其他自定义标签
    for (const item of config.customElements) {
      if (platform === 'uni-app') {
        if (item.platforms) {
          wxml += '<!-- #ifdef ' + item.platforms.join(' || ').toUpperCase() + ' -->'
        }
        voidTags += item.name + ','
        wxml += '<' + item.name + ' v-else-if="n.name==\'' + item.name + '\'" :class="n.attrs.class" :style="n.attrs.style"'
        if (item.attrs) {
          for (const attr of item.attrs) {
            wxml += ' :' + attr + '="n.attrs'
            if (attr.includes('-')) {
              wxml += '[\'' + attr + '\']"'
            } else {
              wxml += '.' + attr + '"'
            }
          }
        }
        wxml += ' />'
        if (item.platforms) {
          wxml += '<!-- #endif -->'
        }
      } else if (!item.platforms || item.platforms.join(',').toLowerCase().includes(platform)) {
        voidTags += item.name + ','
        wxml += '<' + item.name + ' wx:elif="{{n.name==\'' + item.name + '\'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}"'
        if (item.attrs) {
          for (const attr of item.attrs) {
            wxml += ' ' + attr + '="{{n.attrs'
            if (attr.includes('-')) {
              wxml += '[\'' + attr + '\']}}"'
            } else {
              wxml += '.' + attr + '}}"'
            }
          }
        }
        wxml += ' />'
      }
    }

    return through2.obj(function (file, _, callback) {
      if (file.isBuffer()) {
        // src 目录
        if (file.base.includes('src')) {
          let content = file.contents.toString()
          if (file.basename === 'index.js' || file.basename === 'mp-html.vue') {
            // 注册插件列表
            if (platform === 'uni-app') {
              content = content.replace(/const\s*plugins\s*=\s*\[\]/, `${pluginImports}const plugins=[${plugins}]`)
            } else {
              content = content.replace(/plugins\s*=\s*\[\]/, `plugins=[${plugins}]`)
            }
          } else if (file.basename === 'parser.js') {
            // 设置标签名选择器
            content = content.replace(/tagSelector\s*=\s*{}/, `tagSelector=${JSON.stringify(tagSelector)}`)
            // 设置自闭合标签
              .replace(/voidTags\s*:\s*makeMap\('/, 'voidTags: makeMap(\'' + voidTags)
          } else if (file.basename === 'node.wxml') {
            // 引入模板
            content = content.replace(/<!--\s*insert\s*-->/, wxml)
          } else if (file.basename === 'node.js') {
            // 引入方法
            content = content.replace(/methods\s*:\s*{/, 'methods:{' + js)
          } else if (file.basename === 'node.wxss') {
            // 引入样式
            content = wxss + content
          } else if (file.basename === 'node.json') {
            // 引入组件声明
            const comps = JSON.stringify(json).slice(1, -1)
            if (comps) {
              content = content.replace(/"usingComponents"\s*:\s*{/, '"usingComponents":{' + comps + ',')
            }
          } else if (file.basename === 'node.vue') {
            // 引入 vue
            content = content.replace(/<!--\s*insert\s*-->/, wxml)
              .replace(/methods\s*:\s*{/, 'methods:{' + js)
              .replace('<style>', '<style>' + wxss.replace(/\.[a-zA-Z_][^)}]*?[{,]/g, '/deep/ $&')).replace(/,url/g, ', url')
            let importComp = ''
            let comps = ''
            for (let item in json) {
              const val = json[item]
              // 插件无法通过这种方式引入
              if (val.includes('plugin://')) continue
              item = item.replace(/-([a-z])/g, (_, $1) => $1.toUpperCase())
              importComp += 'import ' + item + " from '" + val + "'\n"
              comps += item + ',\n'
            }
            content = content.replace('<script>', '<script>\n' + importComp)
              .replace(/components\s*:\s*{/, 'components: {\n' + comps)
          } else if (file.basename === 'local.html' && wxss) {
            // 引入样式
            content = '<style>' + wxss + '</style>' + content
          }
          file.contents = Buffer.from(content)
          for (const item in builds) {
            if (builds[item].handler) {
              builds[item].handler(file, platform)
            }
          }
        } else {
          // plugins 目录
          const name = file.relative.split(path.sep)[0]
          const build = builds[name]
          // 本平台不支持使用
          if (!build || file.extname === '.md' || file.basename === 'build.js') {
            callback()
            return
          }
          // import
          if (build.import) {
            if (typeof build.import === 'string') {
              if (file.relative.includes(build.import)) {
                file.import = true
              }
            } else {
              for (let i = 0; i < build.import.length; i++) {
                if (file.relative.includes(build.import[i])) {
                  file.import = true
                  break
                }
              }
            }
          }
          if (build.handler) {
            build.handler(file, platform)
          }
        }
      }
      this.push(file)
      callback()
    })
  },
  /**
   * @description 引入样式文件到 node.wxss 中
   */
  importCss () {
    let css = ''
    return through2.obj(function (file, _, callback) {
      if (file.isBuffer()) {
        let content = file.contents.toString()
        // 要被引入的文件
        if (file.import) {
          css += content
          callback()
          return
        }
        // 引入到对应位置
        if (file.basename === 'node.wxss') {
          content = css + content
        } else if (file.basename === 'node.vue') {
          content = content.replace('<style>', '<style>' + css.replace(/\.[a-z_][^)}]+?[{,]/g, '/deep/ $&')).replace(/,url/g, ', url')
        } else if (file.basename === 'local.html' && css) {
          content = '<style>' + css + '</style>' + content
        }
        file.contents = Buffer.from(content)
      }
      this.push(file)
      callback()
    })
  }
}
