import { interval, Subscription } from 'rxjs';
import {
  UpdateCheckConfig,
  UpdateInfo,
  UpdateType
} from '@ucap-webmessenger/native';
import axios from 'axios';
import semver from 'semver';
import path from 'path';
import fse from 'fs-extra';
import zlib from 'zlib';
import { BrowserWindow, app } from 'electron';
import { UpdaterChannel } from '@ucap-webmessenger/native-electron';
import { startWith } from 'rxjs/operators';
import log from 'electron-log';

export class RendererUpdater {
  private checkSubscription: Subscription | undefined;
  private readonly appPath: string;
  private readonly downloadPath: string;
  private readonly unzipPath: string;

  constructor(
    private window: BrowserWindow,
    private config: UpdateCheckConfig
  ) {
    this.appPath = app.getAppPath() + '/';
    const appPathFolder = this.appPath.slice(
      0,
      this.appPath.indexOf('app.asar')
    );

    this.downloadPath = path.resolve(appPathFolder, 'update.zip');
    this.unzipPath = path.resolve(appPathFolder, '_app.asar');
  }

  startCheck() {
    if (!semver.valid(this.config.currentVersion)) {
      log.error(
        `RendererUpdater::error currentVersion[${this.config.currentVersion}] is not valid`
      );
      return;
    }

    this.checkSubscription = interval(this.config.intervalHour * 60 * 60 * 1000)
      .pipe(startWith(0))
      .subscribe(i => {
        axios
          .post(this.config.feed)
          .then(res => {
            const appVersion = res.data.appVersion;
            if (!semver.valid(appVersion)) {
              log.error(
                `RendererUpdater::error appVersion[${appVersion}] is not valid`
              );
              return;
            }

            if (semver.lt(this.config.currentVersion, appVersion)) {
              this.download(appVersion, res.data.installUrl);
            }
          })
          .catch(reason => {
            log.error('RendererUpdater', reason);
          });
      });
  }

  stopCheck() {
    if (!!this.checkSubscription) {
      this.checkSubscription.unsubscribe();
    }
  }

  apply() {
    const downloadStream = fse.createReadStream(this.downloadPath);
    const unzipStream = fse.createWriteStream(this.unzipPath);
    const unzip = zlib.createGunzip();

    downloadStream
      .pipe(unzip)
      .pipe(unzipStream)
      .end(chunk => {
        try {
          fse.unlinkSync(this.appPath.slice(0, -1));
        } catch (error) {
          log.error('RendererUpdater::apply', error);
          return;
        }

        try {
          fse.renameSync(this.unzipPath, this.appPath.slice(0, -1));
        } catch (error) {
          log.error('RendererUpdater::apply', error);
          return;
        }

        app.relaunch();
      });
  }

  private download(appVersion: string, installUrl: string) {
    axios
      .get(installUrl, { responseType: 'blob' })
      .then(res => {
        fse.writeFile(this.downloadPath, res.data, err => {
          if (!!err) {
            log.error('RendererUpdater::download failed', err);
            return;
          }
          const updateInfo: UpdateInfo = {
            type: UpdateType.Differencial,
            version: appVersion,
            description: ''
          };
          this.window.webContents.send(UpdaterChannel.ExistInstant, updateInfo);
        });
      })
      .catch(reason => {
        log.error('RendererUpdater::download failed', reason);
      });
  }
}
