import { native, sys } from 'cc'

import ManifestConfig from '../config/ManifestConfig'

import CommonTips from '../uicomponent/CommonTips'

import EventManager from './EventManager'
import { Logger } from './Logger'
import VersionManager from './VersionManager'

export default class HotUpdate {
  public static Event_CheckUpdate: string = 'Event_CheckUpdate'
  public static Event_On_Progress: string = 'HotUpdate_Event_On_Progress'
  public static Event_On_NeedUpdate: string = 'HotUpdate_Event_On_NeedUpdate'
  public static Event_Finish_Update: string = 'HotUpdate_Event_Finish'
  public static Event_On_ALREADY_UP_TO_DATE: string = 'HotUpdate_Event_On_ALREADY_UP_TO_DATE'
  public static Event_On_Fail_Update: string = 'HotUpdate_Event_On_Fail_Update'
  private _am: any
  private _checkListener: null
  private storagePath: string
  private manifestUrl: string
  private localBigVersion: number
  private remoteBigVersion: number
  public needUpdate: boolean = false
  public isUpdating: boolean
  public isFinishUpdate: boolean
  public isCheck: boolean
  private key: string
  private hotupdateIndex: number

  constructor() {}

  public init(index: number, key: string = 'Code-remote-asset', manifestUrl: string) {
    if (sys.isNative) {
      this.hotupdateIndex = index
      this.key = key
      this.manifestUrl = manifestUrl
      this.storagePath = '获取this.storagePath报错了'

      Logger.log(this, 'init removeDirectory=', `${this.storagePath}_temp`)
    }
    this.needUpdate = false
    this.isUpdating = false
    this.isFinishUpdate = false
    this.isCheck = false
  }

  private jumpToPack() {
    let url: string
    if (sys.isNative) {
      if (sys.os === sys.OS.ANDROID) url = VersionManager.instance.apkStoreUrl
      else if (sys.os === sys.OS.IOS) url = VersionManager.instance.iosStoreUrl
    }
    Logger.info(this, 'jumpToPack==androidurl===', VersionManager.instance.apkStoreUrl)
    Logger.info(this, 'jumpToPack==iosStoreUrl===', VersionManager.instance.iosStoreUrl)
    Logger.info(this, 'jumpToPack=====', url)
    sys.openURL(url)
    // cc.game.end();
  }

  // 显示强制更新，即更细包面板
  private showPackUpdateDialog() {
    CommonTips.showMsg('有新的版本需要更新，下载后请先卸载，以前的版本，再安装！')
    this.jumpToPack()
    this.showPackUpdateDialog()
  }

  private checkCb(event: any) {
    Logger.log(this, `checkCb Code: =================${event.getEventCode()}`)
    switch (event.getEventCode()) {
      case native.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
        Logger.info(this, 'No local manifest file found, hot update skipped.')
        this.failUpdate()
        break
      case native.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
      case native.EventAssetsManager.ERROR_PARSE_MANIFEST:
        Logger.info(this, 'Fail to download manifest file, hot update skipped.')
        this.failUpdate()
        break
      case native.EventAssetsManager.ALREADY_UP_TO_DATE:
        Logger.info(this, 'Already up to date with the latest remote version.')
        this.alreadyUpToDate()
        break
      case native.EventAssetsManager.NEW_VERSION_FOUND:
        Logger.info(
          this,
          'new version found, please try to update.',
          this.localBigVersion,
          this.remoteBigVersion,
        )
        if (
          this.key === VersionManager.Config_Key[0] &&
          this.localBigVersion < this.remoteBigVersion
        ) {
          // 更新大版本
          Logger.info(
            this,
            'new version found, please try to update======packupdate=',
            this.localBigVersion,
            this.remoteBigVersion,
          )
          this.showPackUpdateDialog()
        } else {
          Logger.info(
            this,
            'new version found, please try to update======hotupdate=',
            this.localBigVersion,
            this.remoteBigVersion,
          )
          // this._am.update();
          this.needUpdate = true
          EventManager.instance.dispatchEvent(HotUpdate.Event_On_NeedUpdate, this.key)
        }
        break
      case native.EventAssetsManager.UPDATE_PROGRESSION:
        // var currentPercent = event.getPercent();
        // var totalPercent = event.getPercentByFile();
        // var fileprocess = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
        // var byteprocess = event.getDownloadedBytes() + ' / ' + event.getTotalBytes();
        Logger.info(
          this,
          'UPDATE_PROGRESSION2222==========',
          this.key,
          event.getDownloadedBytes(),
          event.getTotalBytes(),
        )
        if (event.getTotalBytes() > 0) {
          EventManager.instance.dispatchEvent(
            HotUpdate.Event_On_Progress,
            event.getDownloadedBytes(),
            event.getTotalBytes(),
            this.key,
          )
        }
        break
      case native.EventAssetsManager.UPDATE_FINISHED:
        Logger.info(this, 'UPDATE_FINISHED==============')
        this.finishUpdate(true)
        break
      case native.EventAssetsManager.UPDATE_FAILED:
        Logger.warn(this, 'Update failed==========', event.getMessage())
        this.failUpdate()
        break
      case native.EventAssetsManager.ERROR_UPDATING:
        const fullFilePath: string = `${this.storagePath}/${event.getAssetId()}`
        const tempFilePath: string = `${this.storagePath}_temp/${event.getAssetId()}`
        Logger.warn(this, 'fullFilePath====', fullFilePath)
        Logger.warn(this, 'tempFilePath====', tempFilePath)
        // jsb.fileUtils.removeFile(tempFilePath);
        Logger.warn(this, 'ERROR_UPDATING=============', event.getAssetId(), event.getMessage())
        this.failUpdate()
        break
      default:
      // this.failUpdate();
    }
  }

  public checkUpdate() {
    if (this.isUpdating || this.isCheck) {
      Logger.log(this, 'Checking or updating ...')
      return
    }
    const hotupdateUrlKey: string = VersionManager.Config_Url_Key[this.hotupdateIndex]
    Logger.log(this, 'checkoutUpdate=====', this.manifestUrl, hotupdateUrlKey)
    if (!this._am) {
      this._am = new native.AssetsManager(
        '',
        this.storagePath,
        this.versionCompareHandle.bind(this),
      )
    }
    // this._am.setMaxConcurrentTask(1);
    const manifestStr: string = ManifestConfig.getManifestStr(hotupdateUrlKey)
    Logger.log(this, 'checkUpdate=======manifestStr=======', manifestStr)
    const manifest = new native.Manifest(manifestStr, this.storagePath)
    this._am.setVerifyCallback((filePath, asset) => {
      return true
      // var md5 = calculateMD5(filePath);
      // if (md5 === asset.md5)
      //     return true;
      // else
      //     return false;
    })
    this._am.setEventCallback(this.checkCb.bind(this))
    // 设置事件回调
    this.isCheck = true
    this._am.loadLocalManifest(manifest, this.storagePath)
    this._am.checkUpdate()
  }

  /**
   * @param versionA 本地版本 1.0.0
   * @param versionB 服务器版本 1.0.1
   */
  private versionCompareHandle(versionA: string, versionB: string) {
    const vA = versionA.split('.')
    const vB = versionB.split('.')
    Logger.log(this, 'versionCompareHandle======', this.key, VersionManager.Config_Key[0])
    if (this.key === VersionManager.Config_Key[0]) {
      Logger.log(this, 'versionCompareHandle22===', versionA, versionB)
      VersionManager.instance.nowVersion = versionA
      VersionManager.instance.targetVersion = versionB
    }
    this.localBigVersion = Number.parseInt(vA[0])
    this.remoteBigVersion = Number.parseInt(vB[0])
    for (let i = 0; i < vA.length; ++i) {
      const a = Number.parseInt(vA[i])
      const b = Number.parseInt(vB[i] || '0')
      if (a !== b) return a - b
    }
    if (vB.length > vA.length) return -1
    else return 0
  }

  public startUpdate() {
    if (this.isUpdating) return
    const localManifest = this._am.getLocalManifest()
    const remoteManifest = this._am.getRemoteManifest()
    Logger.log(this, 'startUpdate111===', localManifest.getVersionFileUrl())
    Logger.log(this, 'startUpdate2222===', localManifest.getManifestFileUrl())
    Logger.log(this, 'startUpdate3333===', remoteManifest.getVersionFileUrl())
    Logger.log(this, 'startUpdate4444===', remoteManifest.getManifestFileUrl())
    this.isUpdating = true
    EventManager.instance.dispatchEvent(HotUpdate.Event_On_Progress, 0, 100, this.key)
    this._am.update()
  }

  public disposeUpdate() {
    if (this._am) {
      this._am.setVerifyCallback(null)
      this._am.setEventCallback(null)
    }
    this._am = null
    this._checkListener = null
    this.isUpdating = false
    this.needUpdate = false
  }

  private failUpdate() {
    this.disposeUpdate()
    this.isCheck = false
    EventManager.instance.dispatchEvent(HotUpdate.Event_On_Fail_Update, this.key)
  }

  private alreadyUpToDate() {
    this.disposeUpdate()
    this.isFinishUpdate = true
    EventManager.instance.dispatchEvent(HotUpdate.Event_On_ALREADY_UP_TO_DATE, this.key)
  }

  private finishUpdate(needRestart: boolean) {
    Logger.info(this, '更新完成=====', needRestart)
    this.disposeUpdate()
    this.isFinishUpdate = true
    EventManager.instance.dispatchEvent(HotUpdate.Event_Finish_Update, this.key, needRestart)
  }
}
