/**
 * MIT License
 *
 * Copyright (c) 2020 ManBang Group
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

import MethodChannel from '../channel/MethodChannel'
import RenderManager from '../manager/RenderManager'
import createElement from './createElement'
import Widget from './Widget'
import VNode from './VNode'
import appContainer from './AppContainer'
import { PageRoute, PageInfo, RouteConfig, ThreshProvidersConfig } from '../types/type'
import { ToastInfo } from '../types/widget'
import threshAppContext from './ThreshAppContext'
import Util from '../shared/Util'
import bus from '../shared/bus'
import eventManager from '../manager/EventManager'
import mpWeb from '../coolink/MpWeb'

/**
 * 对外暴露所有接口的threshApp主类
 */
export class ThreshApp {
  // 代码中配置的默认展示页面
  private _defaultPageName: string
  // 代码中配置的404页面
  private _notFoundPageName: string
  // 是否正在关闭 modal
  // 如果正在关闭中，将阻止新的 modal 创建
  private _modalIsHiding: boolean = false
  // flutter version
  flutterVersion: string
  // js version
  get jsVersion (): string {
    return '1.3.2'
  }
  // 是否调试模式
  debugMode: boolean = false
  // 平台类型
  platform: 'Android' | 'iOS' | void
  // 外部环境是否准备完成
  envReady: boolean = false
  // 三方注入的路由信息
  injectRouteInfo: PageRoute
  // 三方插件
  providers: ThreshProvidersConfig = {}

  // 持有注入的路由信息
  injectRoute = injectRoute
  createElement: Function = createElement
  Widget = Widget

  get pageName (): string {
    const originPageName = appContainer.currentPageName
    const defaultPageName = (threshApp.injectRouteInfo || { pageName: '' }).pageName
    if (!originPageName) return defaultPageName
    return originPageName.split('#').shift() || defaultPageName
  }
  get referPageName (): string {
    const originReferPageName = appContainer.referPageName
    if (!originReferPageName) return ''
    return originReferPageName.split('#').shift() || ''
  }

  constructor () {
    if (mpWeb.isBrowser()) {
      return;
    }
    threshAppContext.initGlobal(this)
  }

  clear () {
    appContainer.clear()
    bus.clear()
    eventManager.resetAndRegisterBuiltInEvents()
  }

  // 上报异常到 flutter
  onError = (error: any) => {}

  /**
   * 判断当前 df 版本是否小于目标版本
   * @param flutterVersion 目标 df 版本
   */
  flutterVersionSmallerThan = Util.flutterVersionSmallerThan

  /**
   * 注册页面
   * @param {String} pageName
   * @param {Function} pageBuilder
   * @param {boolean} isDefault
   */
  registerPage (pageName: string, pageBuilder: Function, config: RouteConfig = {
    isDefault: false,
    isNotFound: false
  }) {
    appContainer.addRoute(pageName, pageBuilder)
    if (config.isDefault) this._defaultPageName = pageName
    if (config.isNotFound) this._notFoundPageName = pageName
  }
  /**
   * @override
   * 表示相关环境已准备完成执行的回调，如 flutter 环境
   * 可以在这个函数中做一些配置
   */
  ready = () => {}
  /**
   * js主动显示页面
   * 存在 injectRouteInfo 时显示 injectRouteInfo 对应的页面
   * 不存在时显示 _defaultPageName 页面
   * 都不存在时不显示
   */
  runApp () {
    this.pushPage()
  }
  /**
   * 通知 flutter 进入下一级页面
   * @param {String} pageName
   * @param {Object} params 页面参数
   */
  pushPage (pageName?: string, params: object = {}, checkSamePage: boolean = true) {
    let pageInfo = RenderManager.currentPage;
    if (checkSamePage) {
      if (pageInfo) {
        if (pageInfo.pageName == pageName) {
          return;
        }
      }
    }
    //
    const pageCreateTimestamp = Date.now()
    const pageRes: PageInfo = this._findPage(pageName || '', params)
    if (pageRes.pageData) {
      RenderManager.pushPage(pageRes.pageData, pageRes.pageName, pageCreateTimestamp)
      Util.log('Push page: ' + pageRes.pageName)
    }
  }
  /**
   * 通知 flutter 进入下一级页面
   * @param {String} pageName
   * @param {Object} params 页面参数
   */
   pushReplacement (pageName?: string, params: object = {}) {
    const pageCreateTimestamp = Date.now()
    const pageRes: PageInfo = this._findPage(pageName || '', params)
    if (pageRes.pageData) {
      RenderManager.pushReplacement(pageRes.pageData, pageRes.pageName, pageCreateTimestamp)
      Util.log('Push page: ' + pageRes.pageName)
    }
  }
  /**
   * 推出页面或关闭当前窗口
   */
  async popPage () {
    if (appContainer.isEmpty) return
    return await RenderManager.popPage()
  }
  /**
   * 当前页面是否可以pop
   */
  canPop (): boolean {
    return appContainer.canPop
  }
  /**
   * 显示模态框
   * @param {VNode} modal
   * @param {String} title
   * @param {boolean} popup
   */
  showModal (modal: VNode, {
    title,
    popup
  }: {
    title?: string,
    popup?: boolean
  } = {}) {
    if (!this._modalIsHiding) {
      RenderManager.showModal(modal, appContainer.formatModalName(title), !!popup)
    }
  }
  /**
   * 隐藏模态框
   */
  async hideModal () {
    this._modalIsHiding = true
    await RenderManager.hideModal()
    this._modalIsHiding = false
  }
  /**
   * 显示 toast
   * toast 不具备生命周期
   * @param {VNode} toast
   * @param {ToastInfo} info 该参数字段见下，可选
   * @param {String} name 名称，用来控制隐藏，如果不设置将会设置为自动隐藏
   * @param {int} stayTime toast 保持时间，默认 2000ms
   * @param {int} duration toast 动画时长，默认 200ms
   * @param {boolean} mask 是否需要透明遮罩，默认 false，设置为 true 则背景不可点击
   * @param {Object|Array<Object>} position 动画位置信息，可以是一个 Object 也可以是包含两个 Object 的数组（第一个元素是起始位置，第二个是结束位置），Object 有 left right top bottom width height 6个 double 属性，存在 width 时会忽略 left right，不设置默认为 { bottom: 50, right: 50, left: 50 }
   * @param {double|Array<double>} opacity 动画透明度，可以是一个 double 也可以是包含两个 double 的数组（第一个元素是起始透明度，第二个是结束透明度），不设置默认为 [0.0, 1.0]
   */
  showToast (toast: VNode, info?: ToastInfo) {
    RenderManager.showToast(toast, info)
  }
  /**
   * 隐藏 toast
   * @param {String} name
   */
  hideToast (name: string) {
    RenderManager.hideToast(name)
  }
  /**
   * 设置资源的路径 
   */
   setResourcePath(path: string) {
    RenderManager.setResourcePath(path)
   }
  /**
   * 在关闭容器之前
   * 通过该方法可以停止页面上永久渲染组件的渲染，如 Refresh / gif 等
   * 防止关闭容器后因为持续渲染导致 crash
   * PS: 如果通过 threshApp.popPage() 关闭容器则不需要主动调用该方法，popPage 内部会判断调用时机
   */
  stopInfinitRender () {
    RenderManager.stopAlwaysRender()
  }
  /**
   * 上报当前正在显示页面的数据
   * @param networkTime 接口耗时
   */
  pageDidShow (networkTime: number = 0) {
    MethodChannel.pageDidShow(networkTime)
  }
  /**
   * 加载 providers
   */
  useProviders (providers: ThreshProvidersConfig) {
    this.providers = providers
  }
  /** 
   * native 打印方法
   */
  print (args: any) {
    MethodChannel.print(args)
  }
  /**
   * 重载 js 并执行
   */
  // reload () {
  //   MethodChannel.reload()
  // }
  /**
   * 在已注册的页面找到目标页面
   * 页面优先级如下：
   * 1. 已指定页面则显示指定页面，不存在则显示404页面，未设置404页面则会向flutter发出pageNotFound的通知并执行flutter中的处理方法
   * 2. 未指定页面则按如下顺序显示：注入路由页面 > 默认页面 > 404页面 > flutter pageNotFound
   */
  _findPage (pageName: string, params: object = {}): PageInfo {
    let pageBuilder: Function
    let pageData: any
    if (!pageName) {
      if (this.injectRouteInfo) {
        pageName = this.injectRouteInfo.pageName
        params = this.injectRouteInfo.params
      } else if (this._defaultPageName) {
        pageName = this._defaultPageName
      } else if (this._notFoundPageName) {
        pageName = this._notFoundPageName
      }
    } else {
      if (!appContainer.hasRoute(pageName) && this._notFoundPageName) {
        pageName = this._notFoundPageName
      }
    }
    if (!appContainer.hasRoute(pageName)) {
      RenderManager.pageNotFound(pageName)
    } else {
      const builder: Function = appContainer.getRoute(pageName) as Function
      pageBuilder = builder()
      if (pageBuilder) {
        pageData = createElement(pageBuilder, params)
        if (!(pageData instanceof VNode)) throw new Error(`page ${pageName} is not a Widget`)
      } else {
        RenderManager.pageNotFound(pageName)
      }
    }
    
    return {
      pageData,
      pageName
    }
  }
}

/**
* 第三方注入路由
* 该方法需要暴露在全局下
* 调试是可以使用该方法快速进入某个页面
* @param {String} pageName 
* @param {Object} params
*/
export function injectRoute (route: PageRoute) {
  if (!route.pageName) return
  threshApp.injectRouteInfo = route
}

const threshApp: ThreshApp = new ThreshApp()
export default threshApp
