import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';

class Controller extends Section {
    constructor($element, $, vnWeekDays) {
        super($element, $);
        this.weekDays = [];
        this.weekdayNames = vnWeekDays.locales;
        this.entryDirections = [
            {code: 'in', description: this.$t('In')},
            {code: 'middle', description: this.$t('Intermediate')},
            {code: 'out', description: this.$t('Out')}
        ];
    }

    $postLink() {
        const timestamp = this.$params.timestamp;
        let initialDate = new Date();

        if (timestamp) {
            initialDate = new Date(timestamp * 1000);
            this.$.calendar.defaultDate = initialDate;
        }

        this.date = initialDate;
    }

    get worker() {
        return this._worker;
    }

    set worker(value) {
        this._worker = value;
    }

    /**
     * The current selected date
     */
    get date() {
        return this._date;
    }

    set date(value) {
        this._date = value;
        value.setHours(0, 0, 0, 0);

        let weekOffset = value.getDay() - 1;
        if (weekOffset < 0) weekOffset = 6;

        let started = new Date(value.getTime());
        started.setDate(started.getDate() - weekOffset);
        this.started = started;

        let ended = new Date(started.getTime());
        ended.setHours(23, 59, 59, 59);
        ended.setDate(ended.getDate() + 6);
        this.ended = ended;

        this.weekDays = [];
        let dayIndex = new Date(started.getTime());

        while (dayIndex < ended) {
            this.weekDays.push({
                dated: new Date(dayIndex.getTime())
            });
            dayIndex.setDate(dayIndex.getDate() + 1);
        }

        this.fetchHours();
    }

    /**
     * Worker hours data
     */
    get hours() {
        return this._hours;
    }

    set hours(value) {
        this._hours = value;

        for (const weekDay of this.weekDays) {
            if (value) {
                let day = weekDay.dated.getDay();
                weekDay.hours = value
                    .filter(hour => new Date(hour.timed).getDay() == day)
                    .sort((a, b) => new Date(a.timed) - new Date(b.timed));
            } else
                weekDay.hours = null;
        }
    }

    fetchHours() {
        const params = {workerFk: this.$params.id};
        const filter = {
            where: {and: [
                {timed: {gte: this.started}},
                {timed: {lte: this.ended}}
            ]}
        };
        this.$.model.applyFilter(filter, params).then(() => {
            this.getWorkedHours(this.started, this.ended);
            this.getAbsences();
        });
    }

    hasEvents(day) {
        return day >= this.started && day < this.ended;
    }

    getAbsences() {
        const fullYear = this.started.getFullYear();
        let params = {
            workerFk: this.$params.id,
            businessFk: null,
            year: fullYear
        };

        return this.$http.get(`Calendars/absences`, {params})
            .then(res => this.onData(res.data));
    }

    onData(data) {
        const events = {};

        const addEvent = (day, event) => {
            events[new Date(day).getTime()] = event;
        };

        if (data.holidays) {
            data.holidays.forEach(holiday => {
                const holidayDetail = holiday.detail && holiday.detail.description;
                const holidayType = holiday.type && holiday.type.name;
                const holidayName = holidayDetail || holidayType;

                addEvent(holiday.dated, {
                    name: holidayName,
                    color: '#ff0'
                });
            });
        }
        if (data.absences) {
            data.absences.forEach(absence => {
                const type = absence.absenceType;
                addEvent(absence.dated, {
                    name: type.name,
                    color: type.rgb
                });
            });
        }

        this.weekDays.forEach(day => {
            const timestamp = day.dated.getTime();
            if (events[timestamp])
                day.event = events[timestamp];
        });
    }

    getWorkedHours(from, to) {
        this.weekTotalHours = null;
        let weekTotalHours = 0;
        let params = {
            id: this.$params.id,
            from: from,
            to: to
        };
        const query = `Workers/${this.$params.id}/getWorkedHours`;
        return this.$http.get(query, {params}).then(res => {
            const workDays = res.data;
            const map = new Map();

            for (const workDay of workDays) {
                workDay.dated = new Date(workDay.dated);
                map.set(workDay.dated, workDay);
                weekTotalHours += workDay.workedHours;
            }

            for (const weekDay of this.weekDays) {
                const workDay = workDays.find(day => {
                    let from = new Date(day.dated);
                    from.setHours(0, 0, 0, 0);

                    let to = new Date(day.dated);
                    to.setHours(23, 59, 59, 59);

                    return weekDay.dated >= from && weekDay.dated <= to;
                });

                if (workDay) {
                    weekDay.expectedHours = workDay.expectedHours;
                    weekDay.workedHours = workDay.workedHours;
                }
            }
            this.weekTotalHours = weekTotalHours;
        });
    }

    getFinishTime() {
        if (!this.weekDays) return;

        let today = new Date();
        today.setHours(0, 0, 0, 0);

        let todayInWeek = this.weekDays.find(day => day.dated.getTime() === today.getTime());

        if (todayInWeek && todayInWeek.hours && todayInWeek.hours.length) {
            const remainingTime = todayInWeek.workedHours ?
                ((todayInWeek.expectedHours - todayInWeek.workedHours) * 1000) : null;
            const lastKnownEntry = todayInWeek.hours[todayInWeek.hours.length - 1];
            const lastKnownTime = new Date(lastKnownEntry.timed).getTime();
            const finishTimeStamp = lastKnownTime && remainingTime ? lastKnownTime + remainingTime : null;

            if (finishTimeStamp) {
                let finishDate = new Date(finishTimeStamp);
                let hour = finishDate.getHours();
                let minute = finishDate.getMinutes();

                if (hour < 10) hour = `0${hour}`;
                if (minute < 10) minute = `0${minute}`;

                return `${hour}:${minute} h.`;
            }
        }
    }

    set weekTotalHours(totalHours) {
        this._weekTotalHours = this.formatHours(totalHours);
    }

    get weekTotalHours() {
        return this._weekTotalHours;
    }

    formatHours(timestamp = 0) {
        let hour = Math.floor(timestamp / 3600);
        let min = Math.floor(timestamp / 60 - 60 * hour);

        if (hour < 10) hour = `0${hour}`;
        if (min < 10) min = `0${min}`;

        return `${hour}:${min}`;
    }

    showAddTimeDialog(weekday) {
        const timed = new Date(weekday.dated.getTime());
        timed.setHours(0, 0, 0, 0);

        this.newTimeEntry = {
            workerFk: this.$params.id,
            timed: timed
        };
        this.selectedWeekday = weekday;
        this.$.addTimeDialog.show();
    }

    addTime() {
        try {
            const entry = this.newTimeEntry;
            if (!entry.direction)
                throw new Error(`The entry type can't be empty`);

            const query = `WorkerTimeControls/${this.worker.id}/addTimeEntry`;
            this.$http.post(query, entry)
                .then(() => this.fetchHours());
        } catch (e) {
            this.vnApp.showError(this.$t(e.message));
            return false;
        }

        return true;
    }

    showDeleteDialog($event, hour) {
        $event.preventDefault();

        this.timeEntryToDelete = hour;
        this.$.deleteEntryDialog.show();
    }

    deleteTimeEntry() {
        const entryId = this.timeEntryToDelete.id;

        this.$http.post(`WorkerTimeControls/${entryId}/deleteTimeEntry`).then(() => {
            this.fetchHours();
            this.vnApp.showSuccess(this.$t('Entry removed'));
        });
    }

    edit($event, hour) {
        if ($event.defaultPrevented) return;

        this.selectedRow = hour;
        this.$.editEntry.show($event);
    }

    getWeekNumber(currentDate) {
        const startDate = new Date(currentDate.getFullYear(), 0, 1);
        let days = Math.floor((currentDate - startDate) /
        (24 * 60 * 60 * 1000));
        return Math.ceil(days / 7);
    }

    isSatisfied() {
        const weekNumber = this.getWeekNumber(this.date);
        const params = {
            workerId: this.worker.id,
            year: this.date.getFullYear(),
            week: weekNumber,
            state: 'CONFIRMED'
        };
        const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
        this.$http.post(query, params).then(() => {
            this.vnApp.showSuccess(this.$t('Data saved!'));
        });
    }

    isUnsatisfied() {
        const weekNumber = this.getWeekNumber(this.date);
        const params = {
            workerId: this.worker.id,
            year: this.date.getFullYear(),
            week: weekNumber,
            state: 'REVISE',
            reason: this.reason
        };
        const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
        this.$http.post(query, params).then(() => {
            this.vnApp.showSuccess(this.$t('Data saved!'));
        });
    }

    save() {
        try {
            const entry = this.selectedRow;
            if (!entry.direction)
                throw new Error(`The entry type can't be empty`);

            const query = `WorkerTimeControls/${entry.id}/updateTimeEntry`;
            this.$http.post(query, {direction: entry.direction})
                .then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
                .then(() => this.$.editEntry.hide())
                .then(() => this.fetchHours());
        } catch (e) {
            this.vnApp.showError(this.$t(e.message));
        }
    }
}

Controller.$inject = ['$element', '$scope', 'vnWeekDays'];

ngModule.vnComponent('vnWorkerTimeControl', {
    template: require('./index.html'),
    controller: Controller,
    bindings: {
        worker: '<'
    }
});