import { Directive, Input, HostBinding, OnDestroy, OnInit, HostListener } from '@angular/core';
import { DateAdapter } from '@ui/common';
import { Subscription, ReplaySubject, combineLatest, BehaviorSubject } from 'rxjs';
import { Datepicker } from '../datepicker';

@Directive({ selector: '[appUiDatepickerCell]' })
export class UiDatepickerCellDirective implements OnInit, OnDestroy {
  dateSubject = new BehaviorSubject<Date>(undefined);

  @Input('appUiDatepickerCell')
  set date(value: Date) {
    this.dateSubject.next(value);
  }
  get date() {
    return this.dateSubject.value;
  }

  @HostBinding('class.cell-in-displayed-month')
  inDisplayedMonth: boolean;

  @HostBinding('class.cell-not-in-displayed-month')
  notInDisplayedMonth: boolean;

  @HostBinding('class.cell-selected')
  selected: boolean;

  @HostBinding('class.cell-in-range')
  inRange: boolean;

  @HostBinding('class.cell-not-in-range')
  notInRange: boolean;

  @HostBinding('class.cell-is-today')
  isToday: boolean;

  @HostBinding('class.cell-is-disabled-day-of-week')
  isDisabledDayOfWeek: boolean;

  private subscription = new Subscription();

  constructor(private datepicker: Datepicker, private dateAdapter: DateAdapter) {}

  ngOnInit(): void {
    this.initDisplayed$();
    this.initSelected$();
    this.initRange$();
    this.initToday$();
    this.initDisabledDayOfWeek$();
  }

  initDisplayed$() {
    const sub = combineLatest([this.dateSubject, this.datepicker.displayed$]).subscribe(([date, displayed]) => {
      this.inDisplayedMonth = this.dateAdapter.compareYearAndMonth(date, displayed) === 0;
      this.notInDisplayedMonth = !this.inDisplayedMonth;
    });

    this.subscription.add(sub);
  }

  initSelected$() {
    const sub = combineLatest([this.dateSubject, this.datepicker.selected$]).subscribe(([date, selected]) => {
      this.selected = this.dateAdapter.compareDate(date, selected) === 0;
    });

    this.subscription.add(sub);
  }

  initRange$() {
    const sub = combineLatest([this.dateSubject, this.datepicker.min$, this.datepicker.max$]).subscribe(([date, min, max]) => {
      const minInRange = !!min ? this.dateAdapter.compareDate(date, min) > 0 : true;
      const maxInRange = !!max ? this.dateAdapter.compareDate(max, date) > 0 : true;

      this.inRange = minInRange && maxInRange;
      this.notInRange = !this.inRange;
    });

    this.subscription.add(sub);
  }

  initToday$() {
    const sub = this.dateSubject.subscribe((date) => {
      this.isToday = this.dateAdapter.compareDate(date, this.dateAdapter.today()) === 0;
    });
    this.subscription.add(sub);
  }

  initDisabledDayOfWeek$() {
    const sub = combineLatest([this.dateSubject, this.datepicker.disabledDaysOfWeek$]).subscribe(([date, disabledDayOfWeek]) => {
      this.isDisabledDayOfWeek = disabledDayOfWeek && disabledDayOfWeek.includes(this.dateAdapter.getDayOfWeek(date));
    });

    this.subscription.add(sub);
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
    this.dateSubject.complete();
  }

  @HostListener('click')
  select() {
    if (this.inRange && this.inDisplayedMonth && !this.isDisabledDayOfWeek) {
      this.datepicker.setSelected(this.date);
    }
  }
}
