import {
  Injectable,
  Pipe,
  PipeTransform,
  OnDestroy,
  ChangeDetectorRef
} from '@angular/core';
import { Subscription } from 'rxjs';
import { ObjectUtil } from '@ucap-webmessenger/core';

import { DateService, DateOptions } from '../services/date.service';

@Injectable()
@Pipe({
  name: 'ucapDate',
  pure: false // required to update the value when the promise is resolved
})
export class DatePipe implements PipeTransform, OnDestroy {
  value = '';
  lastDate: any;
  lastFormat: string;
  lastOptions: DateOptions;
  changedLocaleSubscription: Subscription;
  changedTimezoneSubscription: Subscription;
  changedDefaultTimezoneSubscription: Subscription;

  constructor(
    private dateService: DateService,
    private changeDetectorRef: ChangeDetectorRef
  ) {}

  ngOnDestroy(): void {
    this._dispose();
  }

  transform(date: any, format: string = 'L', options?: DateOptions): any {
    if (!date || 0 === date.length) {
      return '';
    }

    if (
      ObjectUtil.equals(date, this.lastDate) &&
      ObjectUtil.equals(format, this.lastFormat) &&
      ObjectUtil.equals(options, this.lastOptions)
    ) {
      return this.value;
    }

    // store the target, in case it changes
    this.lastDate = date;
    // store the key, in case it changes
    this.lastFormat = format;
    // store the key, in case it changes
    this.lastOptions = options;

    // set the value
    this.updateValue(date, format, options);

    // if there is a subscription to onLangChange, clean it
    this._dispose();

    // subscribe to onLangChange event, in case the language changes
    if (!this.changedTimezoneSubscription) {
      this.changedTimezoneSubscription = this.dateService.changedTimezone.subscribe(
        () => {
          if (this.lastDate) {
            this.lastDate = null; // we want to make sure it doesn't return the same value until it's been updated
            this.updateValue(date, format, options);
          }
        }
      );
    }

    // subscribe to onDefaultLangChange event, in case the default language changes
    if (!this.changedDefaultTimezoneSubscription) {
      this.changedDefaultTimezoneSubscription = this.dateService.changedDefaultTimezone.subscribe(
        () => {
          if (this.lastDate) {
            this.lastDate = null; // we want to make sure it doesn't return the same value until it's been updated
            this.updateValue(date, format, options);
          }
        }
      );
    }

    // subscribe to onDefaultLangChange event, in case the default language changes
    if (!this.changedLocaleSubscription) {
      this.changedLocaleSubscription = this.dateService.changedLocale.subscribe(
        () => {
          if (this.lastDate) {
            this.lastDate = null; // we want to make sure it doesn't return the same value until it's been updated
            this.updateValue(date, format, options);
          }
        }
      );
    }

    return this.value;
  }

  updateValue(date: any, format: string = 'L', options?: DateOptions): void {
    try {
      const value = this.dateService.get(date, format, options);

      if (value === 'Invalid date') {
        this.value = date;
      } else {
        this.value = value;
      }
    } catch (error) {
      this.value = date;
    }
    this.lastDate = date;
    this.lastFormat = format;
    this.lastOptions = options;
    this.changeDetectorRef.markForCheck();
  }

  private _dispose(): void {
    if (!!this.changedTimezoneSubscription) {
      this.changedTimezoneSubscription.unsubscribe();
      this.changedTimezoneSubscription = undefined;
    }
    if (!!this.changedDefaultTimezoneSubscription) {
      this.changedDefaultTimezoneSubscription.unsubscribe();
      this.changedDefaultTimezoneSubscription = undefined;
    }
    if (!!this.changedLocaleSubscription) {
      this.changedLocaleSubscription.unsubscribe();
      this.changedLocaleSubscription = undefined;
    }
  }
}
