const fs = require('fs')
const fsExtra = require('fs-extra')
const path = require('path')
const merge = require('merge')

const {
  normalizePath,
  getFlexDirection
} = require('@dcloudio/uni-cli-shared')
const {
  getTheme,
  hasTheme,
  parseTheme
} = require('@dcloudio/uni-cli-shared/lib/theme')
const {
  compileI18nJsonStr
} = require('@dcloudio/uni-i18n')
const {
  initI18nOptions
} = require('@dcloudio/uni-cli-shared/lib/i18n')
const {
  hasOwn,
  parseStyle
} = require('../../util')

const wxPageOrientationMapping = {
  auto: [
    'portrait-primary',
    'portrait-secondary',
    'landscape-primary',
    'landscape-secondary'
  ],
  portrait: ['portrait-primary', 'portrait-secondary'],
  landscape: ['landscape-primary', 'landscape-secondary']
}

function parseConfig (appJson) {
  return {
    name: 'app-config.js',
    content: `__registerConfig(${JSON.stringify(appJson)});`
  }
}

const _toString = Object.prototype.toString

function isPlainObject (obj) {
  return _toString.call(obj) === '[object Object]'
}

function normalizeNetworkTimeout (appJson) {
  if (!isPlainObject(appJson.networkTimeout)) {
    appJson.networkTimeout = {
      request: 60000,
      connectSocket: 60000,
      uploadFile: 60000,
      downloadFile: 60000
    }
  } else {
    if (typeof appJson.networkTimeout.request === 'undefined') {
      appJson.networkTimeout.request = 60000
    }
    if (typeof appJson.networkTimeout.connectSocket === 'undefined') {
      appJson.networkTimeout.connectSocket = 60000
    }
    if (typeof appJson.networkTimeout.uploadFile === 'undefined') {
      appJson.networkTimeout.uploadFile = 60000
    }
    if (typeof appJson.networkTimeout.downloadFile === 'undefined') {
      appJson.networkTimeout.downloadFile = 60000
    }
  }
}

function updateFileFlag (appJson) {
  // 已经不再根据文件识别,理论可废弃此处的逻辑
  if (process.env.UNI_USING_V3 || process.env.UNI_USING_V3_NATIVE) {
    return
  }
  const nvueCompilerFilePath = path.resolve(
    process.env.UNI_OUTPUT_DIR,
    '__uniappnvuecompiler.js'
  )
  const nvueCompilerExists = fs.existsSync(nvueCompilerFilePath)

  if (appJson.nvueCompiler === 'uni-app') {
    if (!nvueCompilerExists) {
      fsExtra.outputFile(nvueCompilerFilePath, '')
    }
  } else {
    if (nvueCompilerExists) {
      fs.unlinkSync(nvueCompilerFilePath)
    }
  }

  const rendererFilePath = path.resolve(
    process.env.UNI_OUTPUT_DIR,
    '__uniapprenderer.js'
  )
  const rendererExists = fs.existsSync(rendererFilePath)

  if (appJson.renderer === 'native') {
    if (!rendererExists) {
      fsExtra.outputFile(rendererFilePath, '')
    }
  } else {
    if (rendererExists) {
      fs.unlinkSync(rendererFilePath)
    }
  }
}

function _initTheme (appJson, userManifestJson) {
  const manifestJson = userManifestJson[process.env.UNI_PLATFORM] || {}
  appJson.darkmode = manifestJson.darkmode || false
  const themeLocation = manifestJson.themeLocation || 'theme.json'
  if (themeLocation && hasTheme(themeLocation)) {
    appJson.themeConfig = getTheme()
  }
  return appJson
}

module.exports = function (pagesJson, userManifestJson, isAppView) {
  const {
    app
  } = require('../mp')(pagesJson, userManifestJson)

  const manifest = {
    name: 'manifest'
  }

  const appJson = app.content

  _initTheme(appJson, userManifestJson)

  const {
    navigationBarTextStyle = 'white',
    navigationBarBackgroundColor = '#000000'
  } = parseTheme(appJson.window) || {}

  const TABBAR_HEIGHT = 50

  let manifestJson = JSON.parse(
    fs.readFileSync(path.resolve(__dirname, './manifest.json'), 'utf8')
  )

  // 状态栏
  manifestJson.plus.statusbar = {
    immersed: 'supportedDevice',
    style: navigationBarTextStyle === 'black' ? 'dark' : 'light',
    background: navigationBarBackgroundColor
  }
  // 用户配置覆盖默认配置
  manifestJson = merge.recursive(
    true,
    manifestJson, {
      id: userManifestJson.appid || '',
      name: userManifestJson.name || '',
      description: userManifestJson.description || '',
      version: {
        name: userManifestJson.versionName,
        code: userManifestJson.versionCode
      },
      locale: userManifestJson.locale,
      uniStatistics: userManifestJson.uniStatistics
    }, {
      plus: userManifestJson['app-plus']
    }
  )

  initUniStatistics(manifestJson)

  const splashscreenOptions =
    userManifestJson['app-plus'] && userManifestJson['app-plus'].splashscreen

  const hasAlwaysShowBeforeRender =
    splashscreenOptions &&
    hasOwn(splashscreenOptions, 'alwaysShowBeforeRender')

  // 转换为老版本配置
  if (manifestJson.plus.modules) {
    manifestJson.permissions = manifestJson.plus.modules
    delete manifestJson.plus.modules
  }

  const distribute = manifestJson.plus.distribute

  if (distribute) {
    if (distribute.android) {
      manifestJson.plus.distribute.google = distribute.android
      delete manifestJson.plus.distribute.android
    }
    if (distribute.ios) {
      manifestJson.plus.distribute.apple = distribute.ios
      delete manifestJson.plus.distribute.ios
    }
    if (distribute.sdkConfigs) {
      manifestJson.plus.distribute.plugins = distribute.sdkConfigs
      delete manifestJson.plus.distribute.sdkConfigs
    }
    if (manifestJson.plus.darkmode) {
      if (!(distribute.google || (distribute.google = {})).defaultNightMode) {
        distribute.google.defaultNightMode = 'auto'
      }

      if (!(distribute.apple || (distribute.apple = {})).UIUserInterfaceStyle) {
        distribute.apple.UIUserInterfaceStyle = 'Automatic'
      }
    }
  }

  // 屏幕启动方向
  if (manifestJson.plus.screenOrientation) {
    // app平台优先使用 manifest 配置
    manifestJson.screenOrientation = manifestJson.plus.screenOrientation
    delete manifestJson.plus.screenOrientation
  } else if (appJson.window && appJson.window.pageOrientation) {
    // 兼容微信小程序
    const pageOrientationValue =
      wxPageOrientationMapping[appJson.window.pageOrientation]
    if (pageOrientationValue) {
      manifestJson.screenOrientation = pageOrientationValue
    }
  }

  // 全屏配置
  manifestJson.fullscreen = manifestJson.plus.fullscreen

  // 地图坐标系
  if (manifestJson.permissions && manifestJson.permissions.Maps) {
    manifestJson.permissions.Maps.coordType = 'gcj02'
  }

  if (!manifestJson.permissions) {
    manifestJson.permissions = {}
  }

  const nvuePages = process.env.UNI_USING_V3_NATIVE
    ? pagesJson.pages
    : pagesJson.nvue && pagesJson.nvue.pages

  if (nvuePages && nvuePages.length) {
    const pages = {}
    nvuePages.forEach(({
      path,
      style
    }) => {
      pages[path] = {
        window: parseStyle(style),
        nvue: true
      }
    })

    appJson.nvue = {
      pages
    }

    if (process.env.UNI_USING_V3_NATIVE) {
      appJson.nvue.entryPagePath = nvuePages[0]
    } else if (pagesJson.nvue.entryPagePath) {
      appJson.nvue.entryPagePath = pagesJson.nvue.entryPagePath
    }

    // nvue 权限
    manifestJson.permissions.UniNView = {
      description: 'UniNView原生渲染'
    }
  } else if (process.env.UNI_USING_V8) {
    // nvue 权限
    manifestJson.permissions.UniNView = {
      description: 'UniNView原生渲染'
    }
  }

  // 启动页面配置
  if (process.env.NODE_ENV === 'development') {
    const condition = pagesJson.condition
    if (condition && Array.isArray(condition.list) && condition.list.length) {
      const list = condition.list
      let current = parseInt(condition.current) || 0
      if (current < 0) {
        current = 0
      }
      if (current >= list.length) {
        current = 0
      }
      manifestJson.plus.arguments = JSON.stringify(list[current])
    }
  }

  // 允许内联播放视频
  manifestJson.plus.allowsInlineMediaPlayback = true

  const addRenderAlways = function () {
    // "render": "always"
    if (!manifestJson.plus.launchwebview) {
      manifestJson.plus.launchwebview = {
        render: 'always'
      }
    } else if (!manifestJson.plus.launchwebview.render) {
      manifestJson.plus.launchwebview.render = 'always'
    }
  }

  if (appJson.tabBar && appJson.tabBar.list && appJson.tabBar.list.length) {
    // 安全区配置 仅包含 tabBar 的时候才配置
    if (!manifestJson.plus.safearea) {
      manifestJson.plus.safearea = {
        background: parseTheme(appJson.tabBar).backgroundColor || '#FFFFFF',
        bottom: {
          offset: 'auto'
        }
      }
    }
    if (!process.env.UNI_USING_COMPONENTS) {
      // 非自定义组件模式下，仍旧添加 render always
      addRenderAlways()
    }
  } else {
    addRenderAlways()
  }

  let flexDir = false

  if (process.env.UNI_USING_NVUE_COMPILER) {
    appJson.nvueCompiler = 'uni-app'
    flexDir = getFlexDirection(manifestJson.plus)
  } else {
    appJson.nvueCompiler = 'weex'
  }

  appJson.nvueStyleCompiler = process.env.UNI_USING_NVUE_STYLE_COMPILER
    ? 'uni-app'
    : 'weex'

  if (manifestJson.plus.renderer === 'native') {
    appJson.renderer = 'native'
  } else {
    appJson.renderer = 'auto'
  }

  updateFileFlag(appJson)

  appJson.splashscreen = {
    alwaysShowBeforeRender: false, // 是否启用白屏检测 关闭 splash
    autoclose: false // 是否 uni-app 框架关闭 splash
  }

  // 强制白屏检测
  if (manifestJson.plus.splashscreen) {
    if (
      !hasAlwaysShowBeforeRender &&
      manifestJson.plus.splashscreen.autoclose === false
    ) {
      // 兼容旧版本仅配置了 autoclose 为 false
      manifestJson.plus.splashscreen.alwaysShowBeforeRender = false
    }
    if (manifestJson.plus.splashscreen.alwaysShowBeforeRender) {
      // 白屏检测
      if (!manifestJson.plus.splashscreen.target) {
        manifestJson.plus.splashscreen.target = 'id:1'
      }
      manifestJson.plus.splashscreen.autoclose = true
      manifestJson.plus.splashscreen.delay = 0

      appJson.splashscreen.alwaysShowBeforeRender = true
    } else {
      // 不启用白屏检测
      delete manifestJson.plus.splashscreen.target

      if (manifestJson.plus.splashscreen.autoclose) {
        // 启用 uni-app 框架关闭 splash
        manifestJson.plus.splashscreen.autoclose = false // 原 5+ autoclose 改为 false
        appJson.splashscreen.autoclose = true
      }
    }
    delete manifestJson.plus.splashscreen.alwaysShowBeforeRender
  }

  appJson.appname = manifestJson.name

  if (!manifestJson.plus.distribute) {
    manifestJson.plus.distribute = {
      plugins: {}
    }
  }

  if (!manifestJson.plus.distribute.plugins) {
    manifestJson.plus.distribute.plugins = {}
  }

  // 录音支持 mp3
  manifestJson.plus.distribute.plugins.audio = {
    mp3: {
      description: 'Android平台录音支持MP3格式文件'
    }
  }

  // 有效值为 close,none
  if (!['close', 'none'].includes(manifestJson.plus.popGesture + '')) {
    manifestJson.plus.popGesture = 'close'
  }

  // 检查原生混淆选项
  const confusion = manifestJson.plus.confusion
  if (confusion && confusion.resources) {
    const resources = {}
    const nvuePages = (appJson.nvue && appJson.nvue.pages) || {}
    for (const key in confusion.resources) {
      if (path.extname(key) === '.js') {
        // 支持 js 混淆，过滤掉
        // 静态 js 文件
        if (
          key.indexOf('hybrid/html') === 0 ||
          key.indexOf('static/') === 0 ||
          key.indexOf('/static/') !== -1
        ) {
          resources[key] = confusion.resources[key]
        }
        continue
      }
      if (!/\.nvue$/.test(key)) {
        throw new Error(`原生混淆仅支持 nvue 页面，错误的页面路径：${key}`)
      } else {
        resources[key.replace(/\.nvue$/, '.js')] = confusion.resources[key]
      }
      if (
        !Object.keys(nvuePages).find(path => {
          const subNVues = nvuePages[path].window.subNVues || []
          // TODO
          return (
            path.replace(/\.html$/, '.nvue') === key ||
            path.replace(/\.html$/, '.nvue') + '.nvue' === key ||
            subNVues.find(({
              path
            }) => path === key.replace(/\.nvue$/, ''))
          )
        }) &&
        !pagesJson.pages.find(({
          style = {}
        }) => {
          style = Object.assign(style, style['app-plus'])
          const subNVues = style.subNVues || []
          return subNVues.find(
            ({
              path
            }) => path === key.replace(/\.nvue$/, '')
          )
        })
      ) {
        throw new Error(`原生混淆页面未在项目内使用，错误的页面路径：${key}`)
      }
    }
    confusion.resources = resources
  }

  // uni-app
  const uniApp = require('../../../package.json')['uni-app']
  manifestJson.plus['uni-app'] = uniApp
  // 控制页类型
  const control =
    process.env.UNI_USING_V3 || process.env.UNI_USING_V3_NATIVE
      ? 'uni-v3'
      : process.env.UNI_USING_V8
        ? 'v8'
        : 'webview'

  manifestJson.plus['uni-app'].control = control
  manifestJson.plus['uni-app'].nvueCompiler = appJson.nvueCompiler
  // v3 + native 时强制 auto
  manifestJson.plus['uni-app'].renderer = process.env.UNI_USING_V3_NATIVE
    ? 'auto'
    : appJson.renderer

  if (flexDir) {
    manifestJson.plus['uni-app'].nvue = {
      'flex-direction': flexDir
    }
  }

  // 检查 webview 版本 || 下载 X5 后启动
  const plusWebview = manifestJson.plus.webView
  if (plusWebview) {
    manifestJson.plus['uni-app'].webView = plusWebview
    delete manifestJson.plus.webView
  }

  // 记录编译器版本号
  appJson.compilerVersion = uniApp.compilerVersion

  if (process.env.UNI_USING_V8) {
    let entryPagePath = appJson.pages[0]
    let conditionPagePath = false
    if (manifestJson.plus.arguments) {
      try {
        const args = JSON.parse(manifestJson.plus.arguments)
        if (args && (args.path || args.pathName)) {
          entryPagePath = conditionPagePath = args.path || args.pathName
        }
      } catch (e) { }
    }

    let isNVueEntryPage = appJson.nvue && appJson.nvue.entryPagePath
    conditionPagePath =
      process.env.UNI_CLI_LAUNCH_PAGE_PATH || conditionPagePath
    if (conditionPagePath && appJson.nvue) {
      isNVueEntryPage = `${conditionPagePath}.html` in appJson.nvue.pages
    }
    manifestJson.plus.useragent.value = 'uni-app'
    manifestJson.launch_path = '__uniappview.html'
    Object.assign(manifestJson.plus.launchwebview, {
      id: '1',
      kernel: 'WKWebview',
      'uni-app': 'auto'
    })
    if (process.env.UNI_USING_NATIVE) {
      appJson.entryPagePath = appJson.nvue.entryPagePath
      // networkTimeout
      normalizeNetworkTimeout(appJson)
      appJson.page = Object.create(null)
      appJson.pages = Object.keys(appJson.nvue.pages).map(pagePath => {
        const newPagePath = pagePath.replace('.html', '')
        appJson.page[newPagePath] = {
          window: appJson.nvue.pages[pagePath].window,
          nvue: true
        }
        return newPagePath
      })

      delete appJson.nvue

      delete manifestJson.plus.launchwebview.kernel
      manifestJson.launch_path = ''
      Object.assign(manifestJson.plus.launchwebview, {
        uniNView: {
          path: appJson.entryPagePath
        }
      })
    } else if (isNVueEntryPage) {
      // 非纯 nvue 项目首页为 nvue 页面
      manifestJson.plus.launchwebview.id = '2'
      manifestJson.plus.launchwebview.render = 'always'
    }
    // 带 tab
    if (
      pagesJson.tabBar &&
      pagesJson.tabBar.list &&
      pagesJson.tabBar.list.length
    ) {
      const tabBar = (manifestJson.plus.tabBar = Object.assign({},
        parseTheme(pagesJson.tabBar)
      ))
      const borderStyles = {
        black: 'rgba(0,0,0,0.4)',
        white: 'rgba(255,255,255,0.4)'
      }
      let borderStyle = tabBar.borderStyle
      if (!borderStyle) {
        borderStyle = 'black'
      }
      if (borderStyle in borderStyles) {
        tabBar.borderStyle = borderStyles[borderStyle]
      }
      if (!tabBar.selectedColor) {
        tabBar.selectedColor = '#0062cc'
      }
      tabBar.height = `${parseFloat(tabBar.height) || TABBAR_HEIGHT}px`
      // 非纯 nvue 项目首页为 nvue 页面
      if (!process.env.UNI_USING_NATIVE && isNVueEntryPage) {
        manifestJson.plus.launchwebview.id = '2'
      } else {
        // 首页是 tabBar 页面
        const item = tabBar.list.find(
          page =>
            page.pagePath ===
            (process.env.UNI_USING_NATIVE
              ? appJson.entryPagePath
              : entryPagePath)
        )
        if (item) {
          tabBar.child = ['lauchwebview']
          tabBar.selected = tabBar.list.indexOf(item)
        }
      }

      const i18nOptions = initI18nOptions(
        process.env.UNI_PLATFORM,
        process.env.UNI_INPUT_DIR,
        true,
        true
      )
      if (i18nOptions) {
        manifestJson = JSON.parse(
          compileI18nJsonStr(JSON.stringify(manifestJson), i18nOptions)
        )
        manifestJson.fallbackLocale = i18nOptions.locale
      }
    }
  }

  if (!process.env.UNI_USING_COMPONENTS) {
    manifestJson.plus.launchwebview.kernel = 'UIWebview'
  }

  manifest.content = manifestJson

  const subPackages = []
  // 分包合并
  if (appJson.subPackages && appJson.subPackages.length) {
    appJson.subPackages.forEach(subPackage => {
      if (subPackage.pages && subPackage.pages.length) {
        subPackage.pages.forEach(page => {
          appJson.pages.push(normalizePath(path.join(subPackage.root, page)))
        })
        subPackages.push({
          root: subPackage.root
        })
      }
    })
  }

  delete appJson.subPackages

  // TODO 处理纯原生
  if (process.env.UNI_USING_NATIVE) {
    manifest.name = 'manifest.json'
    manifest.content = JSON.stringify(manifest.content)
    return [manifest, parseConfig(appJson)]
  }

  if (process.env.UNI_USING_V3 || process.env.UNI_USING_V3_NATIVE) {
    if (process.env.UNI_USING_V3 && process.env.UNI_OPT_SUBPACKAGES) {
      appJson.subPackages = subPackages
    }
    return require('./index.v3')(
      appJson,
      manifestJson, {
        manifest,
        pagesJson,
        normalizeNetworkTimeout
      },
      isAppView
    )
  }
  return [app, manifest]
}

function initUniStatistics (manifestJson) {
  // 根节点配置了统计
  if (manifestJson.uniStatistics) {
    manifestJson.plus.uniStatistics = merge.recursive(
      true,
      manifestJson.uniStatistics,
      manifestJson.plus.uniStatistics
    )
    delete manifestJson.uniStatistics
  }
  if (!process.env.UNI_CLOUD_PROVIDER) {
    return
  }
  let spaces = []
  try {
    spaces = JSON.parse(process.env.UNI_CLOUD_PROVIDER)
  } catch (e) { }
  if (!Array.isArray(spaces) || !spaces.length) {
    return
  }
  const space = spaces[0]
  if (!space) {
    return
  }
  const uniStatistics = manifestJson.plus && manifestJson.plus.uniStatistics
  if (!uniStatistics) {
    return
  }
  if (uniStatistics.version === 2 || uniStatistics.version === '2') {
    if (uniStatistics.uniCloud && uniStatistics.uniCloud.spaceId) {
      return
    }
    uniStatistics.uniCloud = {
      provider: space.provider,
      spaceId: space.spaceId,
      clientSecret: space.clientSecret,
      endpoint: space.endpoint
    }
  }
}
