const
    //moment = require('moment'),
    util = require('util'),
    EventEmitter = require('events').EventEmitter,
    //Logger = require('../logs/clsLogger').clsLogger,
    chalk = require('chalk');

require('linqjs');

const announcesDB = class announcesDB {
    constructor({db, moment, config, utils, logger} = {}) {
        this.db = db;
        this.moment = moment;
        this.config = config;
        this._sequelize = this.db._sequelize;
        this.Announces = this.db.Announces;
        this.Announces_Details = this.db.Announces_Details;
        this.DownloadExceptions = this.db.DownloadExceptions;
        this.logger = logger;
        this.sutils = utils;
        this.checkAnnouncesTimer = 5000;
        this.eventTypes = {
            AnnounceInserted: 'AnnounceInserted'
        };

        //PlayerDB Events
        // this.db.on(this.db.eventTypes.Announces_Details_Changed, arr);
        // this.db.on(this.db.eventTypes.Schedules_Details_Changed, arr);
        // this.db.on(this.db.eventTypes.DownloadExceptions_Changed, arr);
    }

    async getAnnounces() {
        this.logger.PlayerEventsAnnounces('getAnnounces method starting');
        let now = this.moment().utc(true);
       let endTime = '23:59:59';
        let nowJson = now.toJSON().substr(0, 11);
        let availablePlayTime = await this.db.getAvailablePlayTime();

        try {
            let announces = await (await this.Announces.findAll({
                include: ['Announces_Details'],
                where: { deviceID: this.config.Settings.deviceID }
            })).select((c) => {
                let st=this.moment(nowJson + c.startTime).utc(true);

                if(c.isOpeningAnnounce==1 )st=this.moment(nowJson + availablePlayTime.startTime.toJSON().substr(11,8)).utc(true).add(c.delayTime,'minutes');
                if( c.isClosingAnnounce==1)st=this.moment(nowJson + availablePlayTime.endTime.toJSON().substr(11,8)).utc(true).add(c.delayTime,'minutes');

                return {
                    announceID: c.announceID,
                    MusicVolume: c.MusicVolume,
                    Volume: c.Volume,
                    announceName: c.announceName,
                    daysEffected: c.daysEffected,
                    delayTime: c.delayTime,
                    endDate: this.moment(c.endDate + 'T' + endTime).utc(true),
                    isActive: c.isActive,
                    isClosingAnnounce: c.isClosingAnnounce,
                    isOpeningAnnounce: c.isOpeningAnnounce,
                    orderID: c.orderID,
                    startDate: this.moment(c.startDate + 'T' + c.startTime).utc(true),
                    startTime: st,
                    endTime: this.moment(nowJson + endTime).utc(true),
                    Announces_Details: c.Announces_Details.select((d) => d.dataValues)
                }
            });

            if(announces.length > 0){

                return announces;
            }
            else{
                return [];
            }
        } catch (e) {
            this.logger.DownloadAnnounces('Error fetching announces from signage.db: ' + e.message);
            return [];
        }
    }

    async getAnnouncesToPlay() {
        this.logger.PlayerEventsAnnounces('getAnnouncesToPlay method starting');
        let now = this.moment().utc(true);
        let allAnnounces = await this.getAnnounces();
        if(!allAnnounces) { return null; }
        let today = Math.pow(2, now.weekday());
        let announcesToPlay = await allAnnounces.where(c => c.isActive && now.isBetween(c.startDate, c.endDate, null, '[]') && now.isSame(c.startTime, 'minute') && (c.daysEffected & today) == today);
        if(!announcesToPlay) return null;
        this.logger.PlayerEventsAnnounces(`getAnnouncesToPlay has ${announcesToPlay.length} announces for this minute`);
        return announcesToPlay;
    }

    async getAnnouncesToDownload(announces) {
        if (announces.length == 0 && announces == null) return [];
        let arr = [];
        try {
            await announces.forEach(c => c.Announces_Details.forEach(d => {
                arr.push(d);
            }));
            arr = await arr.orderBy(c => c.orderID).groupBy(c => c.FileName).select(c => c.first());
            return arr;
        } catch (e) {
            this.logger.DownloadAnnounces('Error getting distinct announce files with details: ' + e.message);
            return [];
        }
    }

    clearAnnounces() {
        try {
            this.Announces.destroy({
                where: {deviceID: {gt: 0}}
            }).then(this.Announces_Details.destroy({
                where: {deviceID: {gt: 0}}
            }));
        } catch (e) {
            this.logger.DownloadAnnounces('Error destroying announces db: ' + e.message);
        }
    }

    async InsertAnnounces(data) {
        this.logger.DownloadAnnounces(chalk.blue('we have ' + data.length + ' new announces to add'));
        let announces = data.select((c) => {
            return {
                MusicVolume: c.an.MusicVolume,
                Volume: c.an.Volume,
                announceID: c.an.announceID,
                announceName: c.an.announceName,
                createdDate: c.an.createdDate.substring(0, 19),
                daysEffected: c.an.daysEffected,
                delayTime: c.an.delayTime,
                deviceID: c.an.deviceID,
                endDate: c.an.endDate.substring(0, 10),
                endTime: c.an.endTime.substring(11, 19),
                isActive: c.an.isActive,
                isClosingAnnounce: c.an.isClosingAnnounce,
                isLooping: c.an.isLooping,
                isOpeningAnnounce: c.an.isOpeningAnnounce,
                lastUpdateDate: c.an.lastUpdateDate.substring(0, 19),
                orderID: c.an.orderID,
                startDate: c.an.startDate.substring(0, 10),
                startTime: c.an.startTime.substring(11, 19)
            }
        });

        //each announce have one or more details. so we have to extract them from data.
        let dat = [];
        let cnt = 1;
        data.forEach((c) => c.and.forEach((d) => {
            d.ID = cnt++;
            dat.push(d)
        }));

        let details = dat.select((c) => {
            return {
                announceID: c.scheduleID,
                FileID: c.FileID,
                ID: c.ID,
                FileName: c.file,
                OriginalName: c.ofile,
                orderID: c.order,
                FileSize: c.size,
                isDownloaded: this.db._downloadStatusenum.NotDownloaded
            }
        });
        data = null;
        dat = null;
        this.logger.DownloadAnnounces('deleting old announces and details');
        await this.Announces.destroy({where: {deviceID: {gt: 0}}});
        await this.Announces_Details.destroy({where: {announceID: {gt: 0}}});

        this.logger.DownloadAnnounces('inserting new announces and details');
        if (announces.length > 0) await this.Announces.bulkCreate(announces);
        if (details.length > 0) await this.Announces_Details.bulkCreate(details);
        if(announces.length > 0 || details.length > 0) {
            this.emit(this.eventTypes.AnnounceInserted);
        }
        this.logger.DownloadAnnounces('inserting new announces and details completed');
    }

    async getAnnouncesLastUpdate() {
        try {
            let updateDate = await this.Announces.findAll({
                attributes: [[this._sequelize.fn('MAX', this._sequelize.col('lastUpdateDate')), 'lastUpdateDate']]
            });
            return updateDate[0].lastUpdateDate;
        } catch (e) {
            this.logger.DownloadAnnounces('Error getting last update date from db: ' + e.message);
            return null;
        }
    }

    //update downloaded status of files on db
    async updateAnnounceFileDownloadedStatus(status, filename) {
        try {
            await this.Announces_Details.update({isDownloaded: status}, {where: {FileName: filename}});
        } catch (e) {
            this.logger.DownloadAnnounces(chalk.bold.red('Error updating download status of: ' + filename + ' error: ' + e.message));
        }
        //alternate method with await to js
        /*
        let err, res;
        [err, res] =  await to(this.Announces_Details.update({isDownloaded: status}, {where: {FileName: filename}}));
         */

    }
};

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