import { Component, forwardRef, Input, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { LocalizationService } from '@shared/services';
import { TranslateService } from '@ngx-translate/core';
import { DATE_FORMAT } from '@shared/helpers';
import * as moment from 'moment';
import { Calendar } from 'primeng/calendar';
import { parseUserInputDate } from './calendar-input.methods';
import { timer, interval, Subscription } from 'rxjs';

@Component({
    selector: 'calendar-input',
    templateUrl: 'calendar-input.component.html',
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => CalendarInputComponent), // replace name as appropriate
            multi: true,
        },
    ],
})
export class CalendarInputComponent implements ControlValueAccessor {
    @ViewChild('calendar', { static: true })
    calendar: Calendar;
    timer: Subscription; 
    interval: Subscription;
    value: Date;

    writeValue(value: Date) {
        if (value?.toString() === 'Invalid Date') {
            return;
        }

        this.value = value;
        this.updateStr();
    }

    onTouched = () => {};
    onChanged = (_: Date) => {};

    registerOnChange(fn: (value: Date) => void) {
        this.onChanged = fn;
    }

    registerOnTouched(fn: () => void) {
        this.onTouched = fn;
    }

    @Input() error: string;

    isShow = false;

    dateStr: string;

    todayString: string;
    tomorrowString: string;
    currentWeek: string;

    @Input() label: string;

    @Input() minDate: Date;

    @Input() maxDate: Date;

    @Input() disabledDates: Date[];

    placeholder = 'SHARED.DATE_FORMAT';

    @Input() readonly = false;

    months: MonthName[] = [];
    days: DayName[] = [];
    ready$: Promise<any>;

    constructor(
        private translate: TranslateService,
        public localizationService: LocalizationService,
    ) {
        this.ready$ = this.loadTranslations();
    }

    ngOnInit(){
        this.minDate = new Date(this.minDate);
        this.maxDate = new Date(this.maxDate);
        var dateNow = new Date();
        var dateTomorrow = new Date();
        dateTomorrow.setHours(0,0,0,0);
        dateTomorrow.setDate(dateTomorrow.getDate() + 1);
        var durationDelay = dateTomorrow.getTime()-dateNow.getTime();
        this.timer = timer(durationDelay).subscribe(() => 
        {
            this.minDate.setDate(this.minDate.getDate()+1);
            this.maxDate.setDate(this.maxDate.getDate()+1);            
        });
        this.interval = interval(1000*60*60*24).subscribe();
    }

    ngOnDestroy() {
        this.timer.unsubscribe();
        this.interval.unsubscribe();
    }

    openCalendar(event: any) {
        this.calendar.showOverlay();
        this.calendar.cd.detectChanges();
        event.stopPropagation();
    }

    inputKeyup() {
        this.manualInputHandler();
    }

    private manualInputHandler() {
        const date = parseUserInputDate(this.dateStr, DATE_FORMAT);
        this.onDateChange(date);
    }

    onDateChange(value?: Date) {
        if (!value) return;
        if (!moment(value).isValid()) return;

        this.error = null;
        this.value = value;
        this.calendar?.hideOverlay();
        this.updateStr();
        this.onChanged(this.value);
    }

    onDateChangeByButton(value) {
        this.onDateChange(value);
    }

    clearInput() {
        this.dateStr = '';
        this.value = null;
        this.onChanged(this.value);
    }

    private getDateString(value: Date): string {
        if (moment(value).isSame(moment(), 'day')) return this.todayString;

        if (moment(value).isSame(moment().add(1, 'd'), 'day'))
            return this.tomorrowString;

        return moment(value).format(DATE_FORMAT);
    }

    async updateStr() {
        await this.ready$;
        if (!this.value) return;
        this.dateStr = this.getDateString(this.value);
    }

    getDate(dateRelativeToday: number) {
        const value = new Date();
        value.setDate(new Date().getDate() + dateRelativeToday);
        return value;
    }

    getMonthName(dateRelativeToday: number) {
        const value = new Date();
        value.setDate(new Date().getDate() + dateRelativeToday);
        const month = this.months.find((m) => m.month === value.getMonth());
        return month ? month.name : '';
    }

    getDayName(dateRelativeToday: number) {
        try {
            const value = new Date();
            value.setDate(new Date().getDate() + dateRelativeToday);
            return this.days.find((d) => d.day === value.getDay()).name;
        } catch {
            return '';
        }
    }

    isSelectedDigit(dateRelativeToday: number) {
        const value = new Date();
        value.setDate(new Date().getDate() + dateRelativeToday);
        if (this.value) {
            if (
                this.value.getDate() == value.getDate() &&
                this.value.getMonth() == value.getMonth() &&
                this.value.getFullYear() == value.getFullYear()
            ) {
                return true;
            }
        }
        return false;
    }

    setDateString(dateStr: string) {
        this.dateStr = dateStr;

        this.onChanged(new Date(dateStr));
    }

    private async loadTranslations() {
        await this.translate
            .get([
                'OF-MONTH.1',
                'OF-MONTH.2',
                'OF-MONTH.3',
                'OF-MONTH.4',
                'OF-MONTH.5',
                'OF-MONTH.6',
                'OF-MONTH.7',
                'OF-MONTH.8',
                'OF-MONTH.9',
                'OF-MONTH.10',
                'OF-MONTH.11',
                'OF-MONTH.12',
                'DAYS-SHORT.0',
                'DAYS-SHORT.1',
                'DAYS-SHORT.2',
                'DAYS-SHORT.3',
                'DAYS-SHORT.4',
                'DAYS-SHORT.5',
                'DAYS-SHORT.6',
            ])
            .toPromise()
            .then((values) => {
                this.months = [
                    { month: 0, name: values['OF-MONTH.1'] },
                    { month: 1, name: values['OF-MONTH.2'] },
                    { month: 2, name: values['OF-MONTH.3'] },
                    { month: 3, name: values['OF-MONTH.4'] },
                    { month: 4, name: values['OF-MONTH.5'] },
                    { month: 5, name: values['OF-MONTH.6'] },
                    { month: 6, name: values['OF-MONTH.7'] },
                    { month: 7, name: values['OF-MONTH.8'] },
                    { month: 8, name: values['OF-MONTH.9'] },
                    { month: 9, name: values['OF-MONTH.10'] },
                    { month: 10, name: values['OF-MONTH.11'] },
                    { month: 11, name: values['OF-MONTH.12'] },
                ];

                this.days = [
                    { day: 0, name: values['DAYS-SHORT.0'] },
                    { day: 1, name: values['DAYS-SHORT.1'] },
                    { day: 2, name: values['DAYS-SHORT.2'] },
                    { day: 3, name: values['DAYS-SHORT.3'] },
                    { day: 4, name: values['DAYS-SHORT.4'] },
                    { day: 5, name: values['DAYS-SHORT.5'] },
                    { day: 6, name: values['DAYS-SHORT.6'] },
                ];
            });

        this.todayString = await this.translate.get('SHARED.TODAY').toPromise();
        this.tomorrowString = await this.translate
            .get('SHARED.TOMORROW')
            .toPromise();
    }
}

export interface MonthName {
    month?;
    name?;
}

export interface DayName {
    day?;
    name?;
}
