import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, NgZone, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
import { isNullOrUndefined } from '@bixi/core/utils';
import dayjs, { Dayjs } from 'dayjs';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { interval, Subject, Subscription } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

export interface IBixiTimeProps {
  format?: string;
  relative?: boolean;
  radio?: number;
  timeout?: number;
  duration?: number;
}

const numberReg = /^[0-9]*$/;

@Component({
  selector: 'bixi-time',
  exportAs: 'bixiTime',
  host: { '[class.bixi-time]': 'true' },
  templateUrl: './time.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class BixiTimeComponent implements OnChanges, OnDestroy {
  constructor(private cdr: ChangeDetectorRef, private ngZone: NgZone) { }

  relativeTime = '';
  absoluteTime: dayjs.Dayjs | null = null;
  private destroy$ = new Subject<void>();
  @Input() time: number | string | Date = 0;
  @Input() format = 'YYYY/MM/DD HH:mm';
  @Input() relative = true; // 是否显示相对时间
  @Input() radio = 1; // 当传入是秒时，需要格式化为秒
  @Input() timeout = 24 * 60;
  @Input() duration = 1;

  timer$ = new Subscription();

  init() {
    if (!this.time) return;
    this.absoluteTime = null;
    let date: Dayjs;
    if (typeof this.time === 'number') {
      date = dayjs(Number(this.time) * this.radio);
    } else if (typeof this.time === 'string' && numberReg.test(this.time)) {
      date = dayjs(Number(this.time) * this.radio);
    } else {
      date = dayjs(this.time);
    }
    if (!date.isValid()) return;
    const currentTime = date as NzSafeAny;
    if (!currentTime.fromNow) {
      console.warn('[@bixi/core] bixi-time(need plugin): 缺少 dayjs/plugin/relativeTime');
      return;
    }

    this.relativeTime = currentTime.fromNow();
    if (dayjs().diff(date, 'second') < this.timeout) {
      this.timer$.unsubscribe();
      this.ngZone.runOutsideAngular(() => {
        // 定时刷新
        this.timer$ = interval((Number(this.duration) || 1) * 1000)
          .pipe(
            takeUntil(this.destroy$),
            filter(() => this.relativeTime !== currentTime.fromNow())
          )
          .subscribe(() => {
            this.relativeTime = currentTime.fromNow();
            this.cdr.detectChanges();
          });
      });
    }
    this.absoluteTime = date;
    this.cdr.detectChanges();
  }

  ngOnChanges(changes: SimpleChanges) {
    const { time, radio, format, relative } = changes;
    if (time) this.time = time.currentValue;
    if (radio) this.radio = radio.currentValue;
    if (format) this.format = format.currentValue;
    if (!isNullOrUndefined(relative)) this.relative = relative.currentValue;

    this.init();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
