const schedule = require('node-schedule');
require('linqjs');
const moment = require('moment');
const path = require('path');
const Downloader = require('../downloader/Downloader').Downloader;
const fs = require('fs');
const chalk = require('chalk');
const AutoUpdate = class AutoUpdate {
    constructor({ utils, config, logger } = {}) {
        this.utils = utils;
        this.logger = logger;
        this.updateObject = null;
        this.checkInterval = 30;
        this.config = config;
        this.options = {
            directory: path.join(process.cwd(), ''),
            filename: '',
            speed: 0,
            useTiming: true,
            startTime: '00:00:01',
            endTime: '23:59:59',
            downloadTimeout: 20000,
            type: 'AutoUpdate',
            useproxy: parseInt(config.proxy.proxyEnabled),
            proxyuser: config.proxy.proxyUser,
            proxypass: config.proxy.proxyPass,
            proxyport: config.proxy.proxyPort,
            proxyhost: config.proxy.proxyHost
        };

        this.unzipDirectory = path.join(process.cwd(), 'update');

        if (!fs.existsSync(this.options.directory)) {

            fs.mkdirSync(this.options.directory);

        }
        this.downloader = new Downloader({ options: this.options });
        this.UpdateEvents = {
            onUpdateProgress: 'onUpdateProgress',
            onUpdateCompleted: 'onUpdateCompleted',
            onUpdateChecking: 'onUpdateChecking',
            onUpdateError: 'onError',
            onUpdateResult: 'onUpdateResult'
        }
    }

    processUpdate(update) {
        let nowUpdateChecking = moment();
        this.emit(this.UpdateEvents.onUpdateChecking, this.utils.appversion, nowUpdateChecking.format('YYYY-MM-DD hh:mm'));
        this.logger.AutoUpdates('Checking update');
        this.updateObject = update;
        let isArray = Array.isArray(this.updateObject);
        if (isArray) {
            this.updateObject = update.where(c => c.OsType == this.utils.osType).first()

        }
        if (!this.updateObject) {
            this.emit(this.UpdateEvents.onUpdateResult, 'there is no update for this os,exiting update');
            this.logger.AutoUpdates('there is no update for this os,exiting update');
            return;
        }
        let version = this.utils.appversion;
        if (this.updateObject.Number == version) {
            this.emit(this.UpdateEvents.onUpdateResult, version);
            this.logger.AutoUpdates(`Both server and client last versions are same ${version},skipping`);
            return;
        }

        this.clearCronjob();
        this.checkUpdate();


    }

    checkUpdate() {

        let updateDate = new moment(this.updateObject.UpdateDate).utc(true);
        let now = new moment().utc(true);
        if (now.isBefore(updateDate)) {
            this.createCronJob();
        }
        else {
            this.emit(this.UpdateEvents.onUpdateResult, 'starting Download of Autoupdate');
            this.logger.AutoUpdates(chalk.green('starting Download of Autoupdate'));
            this.clearCronjob();
            //now download the file
            this.DownloadUpdate();
        }
    }

    createCronJob() {
        //set a cron for this
        if (schedule.scheduledJobs.autoupdate) {
            this.logger.AutoUpdates('there is already cron job for autoUpdates,skipping');
            let nextRun = moment(job.nextInvocation()).utc(true).toJSON();
            this.emit(this.UpdateEvents.onUpdateResult, nextRun);
            this.logger.AutoUpdates(`next check will be at ${nextRun}`);
            return;
        }

        this.emit(this.UpdateEvents.onUpdateResult, this.updateObject.UpdateDate, this.checkInterval);
        this.logger.AutoUpdates(`Creating recurrece jobs for autoupdates starting at:${this.updateObject.UpdateDate} on every ${this.checkInterval} minute`)
        let rule = new schedule.RecurrenceRule();
        rule.minute = this.checkInterval;

        let updateDate = new moment(this.updateObject.UpdateDate).utc(true);
        let job = schedule.scheduleJob('autoupdate', {
            start: updateDate,
            rule: rule
        }, () => {

            this.logger.AutoUpdates(`running check Autoupdate download`);
            this.emit(this.UpdateEvents.onUpdateResult, `running check Autoupdate download`);
            this.checkUpdate();
        });
        let nextRun = moment(job.nextInvocation()).utc(true).toJSON();
        this.logger.AutoUpdates(`next check will be at ${nextRun}`)
    }

    async DownloadUpdate() {

        let file = this.updateObject.UpdateUrl;
        //file='http://cdn.smg.com.tr/000bdfb5-4845-4016-9ce0-48c5171977a2.mp3';
        //file='http://newmanager.smg.com.tr/downloads/vac413full.zip'
        let zipFilename = path.join(this.options.directory, file.split('/').last());

        this.downloader.downloadFile(file, true,
            async (pageStatus, pageMessage, filename) => { //callback finished
                this.logger.AutoUpdates(chalk.green(`Download of ${filename} finished, unzipping to ${zipFilename}`));
                this.logger.System(chalk.green(`Download of Autoupdate file ${filename} finished, unzipping to ${zipFilename}`));
                this.emit(this.UpdateEvents.onUpdateResult, filename, zipFilename);
                this.clearCronjob();

                try {

                    let result = await this.utils.unzipfile(zipFilename, this.unzipDirectory, true, true, true);
                    if (result.err == 0) {
                        this.logger.AutoUpdates(chalk.green(`unzipping of ${filename} completed, shutting down for update..`));
                        this.logger.System(chalk.green(`unzipping of ${filename} completed, shutting down for update..`));
                        this.emit(this.UpdateEvents.onUpdateResult, filename);
                        let nowUpdate = moment();
                        this.emit(this.UpdateEvents.onUpdateCompleted, this.utils.appversion, nowUpdate.format('YYYY-MM-DD hh:mm'));
                        process.exit(2)
                    } else {
                        this.logger.AutoUpdates(filename, result.msg);
                        this.logger.System(chalk.red(`unzipping of ${filename} failed,reason:${result.msg}`));
                        this.emit(this.UpdateEvents.onUpdateResult, filename, result.msg);
                    }

                }
                catch (e) {
                    this.logger.AutoUpdates('error:' + e)
                }
            },
            (filename, percentage, currentStr, durationStr, speed, current, length) => { //callback progress
                if ((percentage % 25) == 0) {
                    this.logger.AutoUpdates(`download progress ${filename} - ${currentStr}/${durationStr} -${percentage}% @${speed} `)
                }
                this.emit(this.UpdateEvents.onUpdateProgress, filename, percentage, currentStr, durationStr, speed, current, length);
            },
            (msg, file) => { //error
                this.logger.AutoUpdates(chalk.red(`error on downloading file ${file} , error:${msg}`));

                this.emit(this.UpdateEvents.onUpdateResult, file, msg);
                this.emit(this.UpdateEvents.onUpdateError, file, msg);
                this.createCronJob();
            }
        )
    }

    clearCronjob() {
        this.logger.AutoUpdates('Clearing old AutoUpdate jobs');
        let cnt = 0;
        for (let key in schedule.scheduledJobs) {
            if (key.startsWith('autoupdate')) {
                schedule.scheduledJobs[key].cancel();
                cnt++;
            }
        }
        this.logger.AutoUpdates(`Cleared ${cnt} AutoUpdate jobs`);
    }





};


const util = require('util'), EventEmitter = require('events').EventEmitter;
util.inherits(AutoUpdate, EventEmitter);
module.exports = AutoUpdate;
