import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
import UserError from 'core/lib/user-error';

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 = Date.vnNew();

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

        this.date = initialDate;

        this.getMailStates(this.date);
    }

    get isHr() {
        return this.aclService.hasAny(['hr']);
    }

    get isHimSelf() {
        const userId = window.localStorage.currentUserWorkerId;
        return userId == this.$params.id;
    }

    get worker() {
        return this._worker;
    }

    get weekNumber() {
        return this.getWeekNumber(this.date);
    }

    set weekNumber(value) {
        this._weekNumber = value;
    }

    set worker(value) {
        this._worker = value;
        this.fetchHours();
        if (this.date)
            this.getWeekData();
    }

    /**
     * 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;
        }
    }

    /**
     * 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);
        }

        if (this.worker) {
            this.fetchHours();
            this.getWeekData();
        }
    }

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

    get weekTotalHours() {
        return this._weekTotalHours;
    }

    getWeekData() {
        const filter = {
            where: {
                workerFk: this.$params.id,
                year: this._date.getFullYear(),
                week: this.getWeekNumber(this._date)
            },
        };
        this.$http.get('WorkerTimeControlMails', {filter})
            .then(res => {
                if (!res.data.length) {
                    this.state = null;
                    return;
                }
                const [mail] = res.data;
                this.state = mail.state;
                this.reason = mail.reason;
            });
        this.canBeResend();
    }

    canBeResend() {
        this.canResend = false;
        const filter = {
            where: {
                year: this._date.getFullYear(),
                week: this.getWeekNumber(this._date)
            },
            limit: 1
        };
        this.$http.get('WorkerTimeControlMails', {filter})
            .then(res => {
                if (res.data.length)
                    this.canResend = true;
            });
    }

    fetchHours() {
        if (!this.worker || !this.date) return;

        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();
        });
    }

    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;
        });
    }

    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));
    }

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

    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];
        });
    }

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

        let today = Date.vnNew();
        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.`;
            }
        }
    }

    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();
                    this.getMailStates(this.date);
                });
        } 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.getMailStates(this.date);
            this.vnApp.showSuccess(this.$t('Entry removed'));
        });
    }

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

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

    getWeekNumber(date) {
        const tempDate = new Date(date);
        let dayOfWeek = tempDate.getDay();
        dayOfWeek = (dayOfWeek === 0) ? 7 : dayOfWeek;
        const firstDayOfWeek = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() - (dayOfWeek - 1));
        const firstDayOfYear = new Date(tempDate.getFullYear(), 0, 1);
        const differenceInMilliseconds = firstDayOfWeek.getTime() - firstDayOfYear.getTime();
        const weekNumber = Math.floor(differenceInMilliseconds / (1000 * 60 * 60 * 24 * 7)) + 1;
        return weekNumber;
    }

    isSatisfied() {
        this.updateWorkerTimeControlMail('CONFIRMED');
    }

    isUnsatisfied() {
        if (!this.reason) throw new UserError(`You must indicate a reason`);
        this.updateWorkerTimeControlMail('REVISE', this.reason);
    }

    updateWorkerTimeControlMail(state, reason) {
        const params = {
            workerId: this.worker.id,
            year: this.date.getFullYear(),
            week: this.weekNumber,
            state
        };

        if (reason)
            params.reason = reason;

        const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
        this.$http.post(query, params).then(() => {
            this.getMailStates(this.date);
            this.getWeekData();
            this.vnApp.showSuccess(this.$t('Data saved!'));
        });
    }

    state(state, reason) {
        this.state = state;
        this.reason = reason;
        this.repaint();
    }

    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())
                .then(() => this.getMailStates(this.date));
        } catch (e) {
            this.vnApp.showError(this.$t(e.message));
        }
    }

    resendEmail() {
        const params = {
            recipient: this.worker.user.emailUser.email,
            week: this.weekNumber,
            year: this.date.getFullYear(),
            workerId: this.worker.id,
            state: 'SENDED'
        };
        this.$http.post(`WorkerTimeControls/weekly-hour-hecord-email`, params)
            .then(() => {
                this.getMailStates(this.date);
                this.vnApp.showSuccess(this.$t('Email sended'));
            });
    }

    getTime(timeString) {
        const [hours, minutes, seconds] = timeString.split(':');
        return [parseInt(hours), parseInt(minutes), parseInt(seconds)];
    }

    getMailStates(date) {
        const params = {
            month: date.getMonth() + 1,
            year: date.getFullYear()
        };
        const query = `WorkerTimeControls/${this.$params.id}/getMailStates`;
        this.$http.get(query, {params})
            .then(res => {
                this.workerTimeControlMails = res.data;
                this.repaint();
            });
    }

    formatWeek($element) {
        const weekNumberHTML = $element.firstElementChild;
        const weekNumberValue = weekNumberHTML.innerHTML;

        if (!this.workerTimeControlMails) return;
        const workerTimeControlMail = this.workerTimeControlMails.find(
            workerTimeControlMail => workerTimeControlMail.week == weekNumberValue
        );

        if (!workerTimeControlMail) return;
        const state = workerTimeControlMail.state;

        if (state == 'CONFIRMED') {
            weekNumberHTML.classList.remove('revise');
            weekNumberHTML.classList.remove('sended');

            weekNumberHTML.classList.add('confirmed');
            weekNumberHTML.setAttribute('title', 'Conforme');
        }
        if (state == 'REVISE') {
            weekNumberHTML.classList.remove('confirmed');
            weekNumberHTML.classList.remove('sended');

            weekNumberHTML.classList.add('revise');
            weekNumberHTML.setAttribute('title', 'No conforme');
        }
        if (state == 'SENDED') {
            weekNumberHTML.classList.add('sended');
            weekNumberHTML.setAttribute('title', 'Pendiente');
        }
    }

    repaint() {
        let calendars = this.element.querySelectorAll('vn-calendar');
        for (let calendar of calendars)
            calendar.$ctrl.repaint();
    }
}

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

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