const fs = require('fs')
const path = require('path')

const {
  hasOwn,
  getPlatforms,
  getH5Options,
  getFlexDirection,
  getNetworkTimeout,
  normalizePath
} = require('@dcloudio/uni-cli-shared')

const {
  addPageUsingComponents
} = require('@dcloudio/uni-cli-shared/lib/pages')

const compilerVersion = require('@dcloudio/webpack-uni-pages-loader/package.json')['uni-app'].compilerVersion

const PLATFORMS = getPlatforms()

const removePlatformStyle = function (style) {
  Object.keys(style).forEach(name => {
    if (PLATFORMS.includes(name)) {
      delete style[name]
    }
  })
}

const getPageComponents = function (inputDir, pagesJson) {
  const firstPagePath = pagesJson.pages[0].path
  const pages = pagesJson.pages

  // 解析分包
  if (pagesJson.subPackages && pagesJson.subPackages.length) {
    pagesJson.subPackages.forEach(({
      root,
      pages: subPages
    }) => {
      if (root && subPages.length) {
        subPages.forEach(subPage => {
          subPage.path = normalizePath(path.join(root, subPage.path))
          pages.push(subPage)
        })
      }
    })
  }

  const tabBarList = (pagesJson.tabBar && pagesJson.tabBar.list) || []
  tabBarList.forEach(item => { // 添加全部属性，方便 Vue 响应式
    item.text = item.text || ''
    item.iconPath = item.iconPath || ''
    item.selectedIconPath = item.selectedIconPath || ''
    item.redDot = false
    item.badge = ''
  })

  if (tabBarList.length) { // 添加全部属性，方便 Vue 响应式
    pagesJson.tabBar.color = pagesJson.tabBar.color || '#999'
    pagesJson.tabBar.selectedColor = pagesJson.tabBar.selectedColor || '#007aff'
    pagesJson.tabBar.backgroundColor = pagesJson.tabBar.backgroundColor || ''
    pagesJson.tabBar.borderStyle = pagesJson.tabBar.borderStyle || 'black'
  }

  const globalStyle = Object.assign({}, pagesJson.globalStyle || {})

  Object.assign(
    globalStyle,
    globalStyle['app-plus'] || {},
    globalStyle.h5 || {}
  )

  if (process.env.UNI_SUB_PLATFORM) {
    Object.assign(globalStyle, globalStyle[process.env.UNI_SUB_PLATFORM] || {})
  }

  process.UNI_H5_PAGES_JSON = {
    pages: {},
    globalStyle
  }

  removePlatformStyle(process.UNI_H5_PAGES_JSON.globalStyle)

  return pages.map(page => {
    const name = page.path.replace(/\//g, '-')
    const pagePath = normalizePath(path.resolve(inputDir, page.path))
    const props = page.style || {}
    const isEntry = firstPagePath === page.path
    const tabBarIndex = tabBarList.findIndex(tabBarPage => tabBarPage.pagePath === page.path)
    const isTabBar = tabBarIndex !== -1

    let isNVue = false
    if (process.env.UNI_USING_NVUE_COMPILER) {
      if (!fs.existsSync(pagePath + '.vue') && fs.existsSync(pagePath + '.nvue')) {
        isNVue = true
      }
    }
    // 解析 titleNView，pullToRefresh
    const h5Options = Object.assign({}, props['app-plus'] || {}, props.h5 || {})

    if (process.env.UNI_SUB_PLATFORM) {
      Object.assign(h5Options, props[process.env.UNI_SUB_PLATFORM] || {})
      Object.assign(props, props[process.env.UNI_SUB_PLATFORM] || {})
    }

    removePlatformStyle(h5Options)

    if (hasOwn(h5Options, 'titleNView')) {
      props.titleNView = h5Options.titleNView
    }
    if (hasOwn(h5Options, 'pullToRefresh')) {
      props.pullToRefresh = h5Options.pullToRefresh
    }

    let windowTop = 44
    const pageStyle = Object.assign({}, globalStyle, props)
    const titleNViewTypeList = {
      none: 'default',
      auto: 'transparent',
      always: 'float'
    }
    let titleNView = pageStyle.titleNView
    titleNView = Object.assign({}, {
      type: pageStyle.navigationStyle === 'custom' ? 'none' : 'default'
    }, pageStyle.transparentTitle in titleNViewTypeList ? {
      type: titleNViewTypeList[pageStyle.transparentTitle],
      backgroundColor: 'rgba(0,0,0,0)'
    } : null, typeof titleNView === 'object' ? titleNView : (typeof titleNView === 'boolean' ? {
      type: titleNView ? 'default' : 'none'
    } : null))
    if (titleNView.type === 'none' || titleNView.type === 'transparent') {
      windowTop = 0
    }

    // 删除 app-plus 平台配置
    delete props['app-plus']
    delete props.h5

    if (process.env.UNI_SUB_PLATFORM) {
      delete props[process.env.UNI_SUB_PLATFORM]
    }

    process.UNI_H5_PAGES_JSON.pages[page.path] = props

    // 缓存usingComponents
    addPageUsingComponents(page.path, props.usingComponents)

    return {
      name,
      route: page.path,
      path: pagePath,
      props,
      isNVue,
      isEntry,
      isTabBar,
      tabBarIndex,
      isQuit: isEntry || isTabBar,
      windowTop,
      topWindow: pageStyle.topWindow,
      leftWindow: pageStyle.leftWindow,
      rightWindow: pageStyle.rightWindow,
      maxWidth: pageStyle.maxWidth
    }
  }).filter(pageComponents => !!pageComponents)
}

const genRegisterPageVueComponentsCode = function (pageComponents) {
  return pageComponents
    .map(({
      name,
      path,
      isNVue,
      isQuit,
      isEntry,
      isTabBar
    }) => {
      const ext = isNVue ? '.nvue' : '.vue'

      return `Vue.component('${name}', resolve=>{
const component = {
  component:require.ensure([], () => resolve(require(${JSON.stringify(path)}+'${ext}')), '${name}'),
  delay:__uniConfig['async'].delay,
  timeout: __uniConfig['async'].timeout
}
if(__uniConfig['async']['loading']){
  component.loading={
    name:'SystemAsyncLoading',
    render(createElement){
      return createElement(__uniConfig['async']['loading'])
    }
  }
}
if(__uniConfig['async']['error']){
  component.error={
    name:'SystemAsyncError',
    render(createElement){
      return createElement(__uniConfig['async']['error'])
    }
  }
}
return component
})`
    })
    .join('\n')
}

const genPageRoutes = function (pageComponents) {
  let id = 1
  return pageComponents
    .map(({
      name,
      route,
      props,
      isNVue,
      isQuit,
      isEntry,
      isTabBar,
      windowTop,
      tabBarIndex,
      topWindow,
      leftWindow,
      rightWindow,
      maxWidth
    }) => {
      return `
{
path: '/${isEntry ? '' : route}',${isEntry ? '\nalias:\'/' + route + '\',' : ''}
component: {
  render (createElement) {
    return createElement(
      'Page',
      {
        props: Object.assign({
          ${isQuit ? 'isQuit:true,\n' : ''}${isEntry ? 'isEntry:true,\n' : ''}${isTabBar ? 'isTabBar:true,\n' : ''}
          ${topWindow === false ? 'topWindow:false,\n' : ''}${leftWindow === false ? 'leftWindow:false,\n' : ''}${rightWindow === false ? 'rightWindow:false,\n' : ''}
          ${isTabBar ? ('tabBarIndex:' + tabBarIndex) : ''}
        },__uniConfig.globalStyle,${JSON.stringify(props)})
      },
      [
        createElement('${name}', {
          slot: 'page'
        })
      ]
    )
  }
},
meta:{${isQuit ? '\nid:' + (id++) + ',' : ''}
  name:'${name}',
  isNVue:${isNVue},maxWidth:${maxWidth || 0},${topWindow === false ? 'topWindow:false,\n' : ''}${leftWindow === false ? 'leftWindow:false,\n' : ''}${rightWindow === false ? 'rightWindow:false,\n' : ''}
  pagePath:'${route}'${isQuit ? ',\nisQuit:true' : ''}${isEntry ? ',\nisEntry:true' : ''}${isTabBar ? ',\nisTabBar:true' : ''}${tabBarIndex !== -1 ? (',\ntabBarIndex:' + tabBarIndex) : ''},
  windowTop:${windowTop}
}
}`
    })
}

const genSystemRoutes = function () {
  return [
    `
{
path: '/choose-location',
component: {
  render (createElement) {
    return createElement(
      'Page',
      {
        props:{
          navigationStyle:'custom'
        }
      },
      [
        createElement('system-choose-location', {
          slot: 'page'
        })
      ]
    )
  }
},
meta:{
  name:'choose-location',
  pagePath:'/choose-location'
}
}
    `,
    `
{
path: '/open-location',
component: {
  render (createElement) {
    return createElement(
      'Page',
      {
        props:{
          navigationStyle:'custom'
        }
      },
      [
        createElement('system-open-location', {
          slot: 'page'
        })
      ]
    )
  }
},
meta:{
  name:'open-location',
  pagePath:'/open-location'
}
}
    `
  ]
}

function filterPages (pagesJson, includes) {
  const pages = []
  let subPackages = pagesJson.subPackages || []
  if (!Array.isArray(subPackages)) {
    subPackages = []
  }
  includes.forEach(includePagePath => {
    let page = pagesJson.pages.find(page => page.path === includePagePath)
    if (!page) {
      for (let i = 0; i < subPackages.length; i++) {
        const {
          root,
          pages: subPages
        } = subPackages[i]
        page = subPages.find(subPage => normalizePath(path.join(root, subPage.path)) === includePagePath)
        if (page) {
          break
        }
      }
    }
    if (!page) {
      console.error(`${includePagePath} is not found`)
    }
    pages.push(page)
  })
  pagesJson.pages = pages
}

function genLayoutComponentsCode (pagesJson) {
  const code = []
  const {
    topWindow,
    leftWindow,
    rightWindow
  } = pagesJson
  if (topWindow && topWindow.path) {
    code.push(
      `import TopWindow from './${topWindow.path}';
${topWindow.style ? ('TopWindow.style=' + JSON.stringify(topWindow.style)) : ''}
Vue.component('VUniTopWindow',TopWindow);`
    )
  }
  if (leftWindow && leftWindow.path) {
    code.push(
      `import LeftWindow from './${leftWindow.path}';
${leftWindow.style ? ('LeftWindow.style=' + JSON.stringify(leftWindow.style)) : ''}
Vue.component('VUniLeftWindow',LeftWindow);`
    )
  }

  if (rightWindow && rightWindow.path) {
    code.push(
      `
import RightWindow from './${rightWindow.path}';
${rightWindow.style ? ('RightWindow.style=' + JSON.stringify(rightWindow.style)) : ''}
Vue.component('VUniRightWindow',RightWindow);`
    )
  }
  return code.join('\n')
}

module.exports = function (pagesJson, manifestJson, loader) {
  const inputDir = process.env.UNI_INPUT_DIR

  global.uniPlugin.configurePages.forEach(configurePages => {
    configurePages(pagesJson, manifestJson, loader)
  })

  if (loader.resourceQuery) {
    const loaderUtils = require('loader-utils')
    const params = loaderUtils.parseQuery(loader.resourceQuery)
    if (params.pages) {
      try {
        const pages = JSON.parse(params.pages)
        if (Array.isArray(pages)) {
          filterPages(pagesJson, pages)
        }
      } catch (e) {}
    }
  }

  const pageComponents = getPageComponents(inputDir, pagesJson)

  pagesJson.globalStyle = process.UNI_H5_PAGES_JSON.globalStyle
  delete pagesJson.pages
  delete pagesJson.subPackages

  const h5 = getH5Options(manifestJson)

  const networkTimeoutConfig = getNetworkTimeout(manifestJson)

  const sdkConfigs = h5.sdkConfigs || {}

  const qqMapKey = sdkConfigs.maps && sdkConfigs.maps.qqmap && sdkConfigs.maps.qqmap.key
  const googleMapKey = sdkConfigs.maps && sdkConfigs.maps.google && sdkConfigs.maps.google.key
  const aMapKey = sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.key
  const aMapSecurityJsCode =
        sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.securityJsCode
  const aMapServiceHost =
        sdkConfigs.maps && sdkConfigs.maps.amap && sdkConfigs.maps.amap.serviceHost

  let locale = manifestJson.locale
  locale = locale && locale.toUpperCase() !== 'AUTO' ? locale : ''

  return `
import Vue from 'vue'
${genLayoutComponentsCode(pagesJson)}
const locales = ${fs.existsSync(path.resolve(process.env.UNI_INPUT_DIR, 'locale')) ? 'require.context(\'./locale\', false, /.json$/)' : '{keys(){return []}}'}
global['____${h5.appid}____'] = true;
delete global['____${h5.appid}____'];
global.__uniConfig = ${JSON.stringify(pagesJson)};
global.__uniConfig.compilerVersion = '${compilerVersion}';
global.__uniConfig.uniPlatform = '${process.env.UNI_PLATFORM}';
global.__uniConfig.appId = '${process.env.UNI_APP_ID}';
global.__uniConfig.appName = '${process.env.UNI_APP_NAME}';
global.__uniConfig.appVersion = '${process.env.UNI_APP_VERSION_NAME}';
global.__uniConfig.appVersionCode = '${process.env.UNI_APP_VERSION_CODE}';
global.__uniConfig.router = ${JSON.stringify(h5.router)};
global.__uniConfig.publicPath = ${JSON.stringify(h5.publicPath)};
global.__uniConfig['async'] = ${JSON.stringify(h5.async)};
global.__uniConfig.debug = ${manifestJson.debug === true};
global.__uniConfig.networkTimeout = ${JSON.stringify(networkTimeoutConfig)};
global.__uniConfig.sdkConfigs = ${JSON.stringify(sdkConfigs)};
global.__uniConfig.qqMapKey = ${JSON.stringify(qqMapKey)};
global.__uniConfig.googleMapKey = ${JSON.stringify(googleMapKey)};
global.__uniConfig.aMapKey = ${JSON.stringify(aMapKey)};
global.__uniConfig.aMapSecurityJsCode = ${JSON.stringify(aMapSecurityJsCode)};
global.__uniConfig.aMapServiceHost = ${JSON.stringify(aMapServiceHost)};
global.__uniConfig.locale = ${JSON.stringify(locale)};
global.__uniConfig.fallbackLocale = ${JSON.stringify(manifestJson.fallbackLocale)};
global.__uniConfig.locales = locales.keys().reduce((res,key)=>{const locale=key.replace(/\\.\\/(uni-app.)?(.*).json/,'$2');const messages = locales(key);Object.assign(res[locale]||(res[locale]={}),messages.common||messages);return res},{});
global.__uniConfig.nvue = ${JSON.stringify({ 'flex-direction': getFlexDirection(manifestJson['app-plus']) })}
global.__uniConfig.__webpack_chunk_load__ = __webpack_chunk_load__
${genRegisterPageVueComponentsCode(pageComponents)}
global.__uniRoutes=[${genPageRoutes(pageComponents).concat(genSystemRoutes()).join(',')}]
global.UniApp && new global.UniApp();
`
}
