import './style.less'

import React, { Component } from 'react'

import bard from '@lls/bard'
import UserAgent from '@lls/client-js/es/UserAgent'

import PageFail from '../page-fail'
import PageLoading from '../page-loading'
import parseParams from '../parse-params'
import {
  getIsPadDisplay,
  isFunction,
  isObject,
  loadPrimaryFont,
  preloadImage,
  toArray
} from '../utils'

export interface Auth {
  appId: string
  deviceId: string
  sDeviceId: string
  token: string
  userLogin: string
}

function setGangFour({ deviceId, sDeviceId, token, appId, userLogin }: Auth) {
  localStorage.setItem('TOKEN', JSON.stringify(token))
  localStorage.setItem('APP_ID', JSON.stringify(appId))
  localStorage.setItem('DEVICE_ID', JSON.stringify(deviceId))
  localStorage.setItem('S_DEVICE_ID', JSON.stringify(sDeviceId))
  localStorage.setItem('USER_LOGIN', JSON.stringify(userLogin))
}

function getVisibilityName() {
  let hidden = ''
  let visibilityChange = ''
  if (typeof document.hidden !== 'undefined') {
    hidden = 'hidden'
    visibilityChange = 'visibilitychange'
  } else if (typeof (document as any).msHidden !== 'undefined') {
    hidden = 'msHidden'
    visibilityChange = 'msvisibilitychange'
  } else if (typeof (document as any).webkitHidden !== 'undefined') {
    hidden = 'webkitHidden'
    visibilityChange = 'webkitvisibilitychange'
  }
  return { hidden, visibilityChange }
}

const { hidden, visibilityChange } = getVisibilityName()

export default function PageWrapper(target: any): any {
  // TODO: fix type
  let usePrimaryFont = false
  let notJudgePad = false
  let useAuthInApp = false
  let preloadSync = false
  let preload: Array<string> = []

  function getAppAuth() {
    const { deviceId, sDeviceId, token, appId } = parseParams()
    if (deviceId && sDeviceId && token && appId) {
      const result = { deviceId, sDeviceId, token, appId, userLogin: '' }
      setGangFour(result)
      return Promise.resolve(result)
    } else if (UserAgent.isHybrid) {
      return bard
        .invoke('getAuthData')
        .then(({ deviceId, sDeviceId, token, appId, userLogin }: Auth) => {
          const result = { deviceId, sDeviceId, token, appId, userLogin }
          setGangFour(result)
          return result
        })
    } else {
      return Promise.resolve()
    }
  }

  const pageHoc = (OriginComponent: any) => {
    const hasFetch = isFunction(OriginComponent.fetch)
    class PhPageWrapper extends Component<any> {
      public state = {
        loading: hasFetch || useAuthInApp || !!preload.length,
        auth: null,
        data: null,
        error: null
      }

      public isPad = getIsPadDisplay()

      public componentWillMount() {
        if (usePrimaryFont) loadPrimaryFont()

        const { loading: showLoading } = this.state
        if (!showLoading) return

        const { isPad } = this
        const loading = PageLoading({ isPad })
        loading.show()

        let auth: null | Auth = null
        let data: any = null

        const authPromise = useAuthInApp ? getAppAuth() : Promise.resolve(null)
        let loadImage = preloadImage(preload).catch(_ => _)
        let imgPromise = preloadSync ? loadImage : Promise.resolve(null)

        Promise.all([authPromise, imgPromise])
          .then(([res]) => {
            if (res) auth = res
            if (hasFetch) {
              return OriginComponent.fetch({ ...this.props, auth: res }).then(
                (d: any) => (data = d)
              )
            }
          })
          .then(() => {
            this.setState({ data, auth, loading: false })
            loading.hide()
          })
          .catch((err: any) => {
            this.setState({ error: err || true, loading: false })
            loading.hide()
          })
      }

      public handleVChange() {
        const videos = toArray(document.querySelectorAll('video'))
        const audios = toArray(document.querySelectorAll('audio'))
        if ((document as any)[hidden]) {
          videos.forEach(video => {
            video.pause()
          })
          audios.forEach(audio => {
            audio.pause()
          })
        }
      }

      public componentDidMount() {
        document.addEventListener(visibilityChange, this.handleVChange)

        if (!notJudgePad) {
          if (this.isPad) document.body.classList.add('ph-pad-body')
          window.addEventListener('orientationchange', this.onOrientationChange)
        }
      }

      public componentWillUnmount() {
        document.addEventListener(visibilityChange, this.handleVChange)
        if (!notJudgePad) {
          window.removeEventListener(
            'orientationchange',
            this.onOrientationChange
          )
        }
      }

      public onOrientationChange = () => {
        window.location.reload()
      }

      public render() {
        const { loading, data, error, auth } = this.state
        const { jest } = this.props
        if (jest) {
          const { jest, ...props } = this.props
          return <OriginComponent {...props} />
        }
        if (loading) return null
        if (error) return <PageFail isPad={this.isPad} />
        const props: any = { data, ...this.props, auth }
        if (!notJudgePad) props.isPad = this.isPad

        return <OriginComponent {...props} />
      }
    }

    return PhPageWrapper
  }

  if (target && target.prototype instanceof Component) {
    return pageHoc(target)
  } else {
    if (isObject(target)) {
      usePrimaryFont = !!target.usePrimaryFont
      notJudgePad = !!target.notJudgePad
      useAuthInApp = !!target.useAuthInApp
      preload = target.preload || []
      preloadSync = target.preloadSync
    }
    return pageHoc
  }
}
