const moment = require('moment');
const Downloader = require('./Downloader').Downloader;
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const prettyBytes = require('pretty-bytes');
const DiskUsage = require('../utils/DiskUsage');
const chalk = require('chalk');
const _ = require('lodash');
const _fs = require('fs');

const DownloadManager = class DownloadManager {
    constructor({options = {}, config, utils, logger} = {}) {
        this.eventTypes = {
            NotAGoodTimeToDownload: 'Not a good time to download',
            QueueIsEmpty: 'Queue is Empty',
            DownloadCompleted: 'Download Completed',
            Error: 'Error',
            Progress: 'Progress',
            NoDiskSpace: 'There is no disk space',
            SetAnnounceDownloadedStatus: 'SetAnnounceDownloadedStatus',
            SetScheduleDownloadedStatus: 'SetScheduleDownloadedStatus'
        };
        this.config = config;
        this.utils = utils;
        this.options = {
            directory: '',
            filename: '',
            speed: 0,
            useTiming: true,
            startTime: '00:00:01',
            endTime: '23:59:59',
            downloadTimeout: 60000,
            type: 'DownloadManager',
            useproxy: parseInt(config.proxy.proxyEnabled),
            proxyuser: config.proxy.proxyUser,
            proxypass: config.proxy.proxyPass,
            proxyport: config.proxy.proxyPort,
            proxyhost: config.proxy.proxyHost
        };
        this.options = Object.assign(this.options, options);

        this.diskUsage = new DiskUsage();
        // this.Queue = require('queue-fifo');
        // this.queue = new this.Queue();
        this.checkNewFileTimeoutPeriod = 2000;
        this.DownloadUrl = this.config.Settings.downloadURL;
        if (!this.DownloadUrl.endsWith('/')) this.DownloadUrl = this.DownloadUrl + '/';
        this.CurrentFile = null;
        //this.SavePath = '';
        this.oldPercentage = -1;
        this.fileSize = 0;
        this.oneMegaByte = 1024 * 1024;
        this.minDiskSpace = 20 * this.oneMegaByte; // 20 megabytes
        this.maxTryLimit = 3;
        this.tryCount = 0;
        this.nextDownloadInterval = 500;
        this.twoMinutes = 1000 * 60 * 2; // 2 minutes
        this.logger = logger;
        this.downloader = new Downloader({options: this.options});
        this.downloadArr = [];
        this._fs = _fs;
        this.startTime = this.options.startTime;
        this.endTime = this.options.endTime;
        this.useTiming = this.options.useTiming;
        this.timeOut = 5000;
        this.AnnounceDownloader = false;
        this.totalDownloaded = 0;
        this.totalFiles = 0;
        this.FilesSizeDownloaded = 0;

    }


    checkDownload() {
        if (!this.checkTime()) {
            this.logger.DownloadManager(`Not a good time to download, time must be between ${this.startTime} and ${this.endTime}`);
            this.emit(this.eventTypes.NotAGoodTimeToDownload);
            setTimeout(() => {
                this.checkDownload();
            }, this.checkNewFileTimeoutPeriod);
            return;
        }

        if (this.queIsEmpty()) {
            this.logger.DownloadManager('There are no files in download queue.');
            this.emit(this.eventTypes.QueueIsEmpty);
            return;
        }
        if (!this.checkFreeSpace()) {
            this.logger.DownloadManager(chalk.bgRed('not enough free space on disk, skipping any download'));
            setTimeout(() => {
                this.checkDownload();
            }, this.checkNewFileTimeoutPeriod);
        }
        // if there's error in 4 downloads back to back, increase nextDownloadInterval
        // if (this.tryCount > 3) {
        //     this.nextDownloadInterval = this.nextDownloadInterval * 5;
        //     if (this.nextDownloadInterval > this.oneMinute) {
        //         this.nextDownloadInterval = this.oneMinute;
        //     }
        //     this.logger.DownloadManager(`next download try in: ${this.nextDownloadInterval}`);
        //     setTimeout(() => {
        //         this.downloadNext();
        //     }, this.nextDownloadInterval);
        //     return;
        // }
        else {
            this.downloadNext();
        }

    }

    checkFreeSpace() {
        let freeSpace = this.diskUsage.freeSpaceSync();
        this.logger.DownloadManager('free space on disk: ' + prettyBytes(freeSpace));
        if (freeSpace < this.minDiskSpace) {
            this.logger.DownloadManager('Not enough disk space! available: ' + prettyBytes(freeSpace) + ' needed ' + prettyBytes(this.minDiskSpace));
            return false;
        } else {
            return true;
        }
    }

    downloadNext() {
        //let file = this.queue.dequeue();
        let file = this.downloadArr.shift();
        if (file == undefined) {
            // shift returns undefined if download arr's length is 0
            this.emit(this.eventTypes.QueueIsEmpty);
            return;
        }
        this.CurrentFile = file.split('/').pop();
        this.logger.DownloadManager('adding file: ' + file);
        this.logger.DownloadManager('que size after dequeue ' + this.downloadArr.length); //this.queue.size()
        this.totalDownloaded += 1;
        if (this.totalFiles == 0) {
            this.totalFiles = this.downloadArr.length;
        }
        this.logger.DownloadManager(`downloading ${this.totalDownloaded}/${this.totalFiles} , ${prettyBytes(this.FilesSizeDownloaded)}/${prettyBytes(this.FilesSize)}`);
        this.currentFileSize = 0;
        this.downloader.downloadFile(file, false, (pageStatus, pageMessage, filename, diskSize, resSize, filePath) => {
                //download finished, now move to next
                if (diskSize != resSize) {
                    this.tryCount++;
                    // if try count reaches 3 or more, try next in 2 minutes
                    if(this.tryCount >= 3){
                        this.nextDownloadInterval = this.nextDownloadInterval * 10;
                        if(this.nextDownloadInterval > this.twoMinutes) {
                            this.nextDownloadInterval = this.twoMinutes;
                        }
                    }
                    try {
                        this.logger.DownloadManager(`Download error: page status: ${pageStatus}, disk size: ${diskSize}, should be ${resSize}, file: ${filePath}. Deleting and re-adding it to queue`);
                        if (this._fs.existsSync(filePath)) {
                            this._fs.unlinkSync(filePath);
                            // if page status is not 404 aka not found, push it to the end of download array.
                            if(pageStatus != 404) {
                                this.downloadArr.push(filename);
                            }
                            this.emit(this.eventTypes.Error, filePath, diskSize, resSize);
                        }
                    } catch (e) {
                        this.logger.DownloadManager(`Error deleting false size file, err: ${e}, filepath: ${filePath}`)
                    }
                } else if(pageStatus == 200) {
                    this.nextDownloadInterval = 500;
                    this.tryCount = 0;
                }
                this.emit(this.eventTypes.DownloadCompleted, pageStatus, pageMessage, filename);

                this.logger.DownloadManager(chalk.green('finished: ' + filename + ' page status: ' + pageStatus));
                this.pageStatus = pageStatus;
                setTimeout(() => {
                    this.FilesSizeDownloaded += this.currentFileSize;
                    process.nextTick(() => this.checkDownload());
                }, this.nextDownloadInterval);
                return;
            },
            (filename, percentage, currentStr, durationStr, speed, current, length) => {
                //show progress here, emit event
                this.emit(this.eventTypes.Progress, filename, percentage, currentStr, durationStr, speed, current, length, this.totalDownloaded, this.totalFiles, this.FilesSizeDownloaded, this.FilesSize);
                //this.logger.DownloadManager(filename + ': ' + percentage + '% ' + curent + 'mb / ' + length + ' mb @' + speed);
                this.currentFileSize = length;

            }, (msg, file) => {
                this.tryCount++;
                // if try count reaches 3 or more, try next in 2 minutes
                if(this.tryCount >= 3) {
                    this.nextDownloadInterval = this.nextDownloadInterval * 10;
                    if(this.nextDownloadInterval > this.twoMinutes) {
                        this.nextDownloadInterval = this.twoMinutes;
                    }
                }
                this.emit(this.eventTypes.Error, file);

                let q = _.castArray({FileName: file});
                this.utils.deleteFilesArray(q, this.options.directory);
                // set downloaded status in db
                if(this.options.directory.endsWith('files')) {
                    this.emit(this.eventTypes.SetScheduleDownloadedStatus, false, file)
                }

                if(this.options.directory.endsWith('announces')) {
                    this.emit(this.eventTypes.SetAnnounceDownloadedStatus, false, file)
                }

                //push undownloaded file to array
                if (msg != 404) {
                    this.addFileToQue(file);
                    this.logger.DownloadManager(chalk.red('Error: ' + msg + ' File: ' + file + ' re-adding it to download que'));
                } else {
                    this.logger.DownloadManager(chalk.red('Error: ' + msg + ' File: ' + file));
                }
                setTimeout(() => {
                    //+this.logger.DownloadManager('checking next download');
                    process.nextTick(() => this.checkDownload());
                }, this.nextDownloadInterval);
                return;
            }
        );
    }

    queIsEmpty() {
        if (this.downloadArr.length == 0) {
            //this.emit(this.eventTypes.QueueIsEmpty);
            return true;
        } else {
            return false;
        }
    }

    setPath(newPath) {
        this.SavePath = newPath;
        this.downloader.options.directory = this.SavePath;
    }

    checkTime() {
        // moment('2016-10-30').isBetween('2016-10-30', '2016-10-30', null, '[]'); //true
        let now = new moment().utc(true);

        let nowJson = now.toJSON().substr(0, 11);
        //let nowString = now.utc(true).toJSON().substr(0,19);
        let startTime = moment(nowJson + this.startTime).utc(true);
        let endTime = moment(nowJson + this.endTime).utc(true);
        let q = now.isBetween(startTime, endTime, null, "[]");

        return q;
    }

    start() {
        setTimeout(() => {
            this.checkDownload();
        }, this.checkNewFileTimeoutPeriod);
    }

    addArrayToQue(array) {
        // array.forEach(file => {
        //     let fname = file.FileName;
        //     this.addFileToQue(fname);
        //     //this.totalFiles+=1;
        // });
        array.forEach(file => {
            let fname = file.FileName;
            let downloadLink = this.DownloadUrl + fname;
            this.downloadArr.push(downloadLink);
        });
        this.checkDownload();
        this.totalFiles = this.downloadArr.length;
    }

    addFileToQue(file) {
        // let downloadLink = this.DownloadUrl + file;
        // this.queue.enqueue(downloadLink);
        // this.totalFiles = this.queue.size()
        // //this.totalFiles+=1;
        let dlLink = this.DownloadUrl + file;
        this.downloadArr.push(dlLink);
        this.totalFiles = this.downloadArr.length;
    }

    clearQue() {
        // await this.queue.clear();
        // this.emit(this.eventTypes.QueueIsEmpty, "");
        // this.totalDownloaded = 0;
        // this.totalFiles = this.queue.size();
        // if (this.totalFiles == 0) {
        //     this.FilesSize = 0;
        //     this.FilesSizeDownloaded = 0;
        // }
        this.downloadArr = [];
        this.totalDownloaded = 0;
        this.totalFiles = this.downloadArr.length;
        if (this.totalFiles == 0) {
            this.FilesSize = 0;
            this.FilesSizeDownloaded = 0;
        }
        this.emit(this.eventTypes.QueueIsEmpty);
    }

    setTotalSize(FilesSize) {
        this.FilesSize = FilesSize;
    }
};

util.inherits(DownloadManager, EventEmitter);
module.exports = DownloadManager;
