import {Pipe, PipeTransform} from '@angular/core';
import {SCRange, isInRange, SCISO8601DateRange} from '@openstapps/core';
import {NormalizedInterval, differenceInMilliseconds, interval} from 'date-fns';
import {EMPTY, Observable, SchedulerLike, asyncScheduler, concat, defer, map, of, timer} from 'rxjs';

@Pipe({
  name: 'isInRange',
  pure: true,
  standalone: true,
})
export class InRangePipe implements PipeTransform {
  transform<T>(value: T, range: SCRange<T>): boolean {
    return isInRange(value, range);
  }
}

export const MIN_DATE = new Date(0);
export const MAX_DATE = new Date(1e15);
// Maximum safe delay for JavaScript timers (~24.8 days)
export const MAX_DELAY = 2 ** 31 - 1;

@Pipe({
  name: 'rangeToDateInterval',
  pure: true,
  standalone: true,
})
export class ToDateIntervalPipe implements PipeTransform {
  transform(value: SCISO8601DateRange): NormalizedInterval {
    return interval(new Date(value.gte ?? value.gt ?? MIN_DATE), new Date(value.lte ?? value.lt ?? MAX_DATE));
  }
}

/**
 * Returns an Observable that will change its value when the current date is within the given interval.
 */
export function isWithinIntervalObservable(
  value: NormalizedInterval,
  scheduler: SchedulerLike = asyncScheduler,
): Observable<boolean> {
  return defer(() => {
    const now = scheduler.now();
    const activate = differenceInMilliseconds(value.start, now);
    const deactivate = differenceInMilliseconds(value.end, now);

    return concat(
      of(activate <= 0 && deactivate > 0),
      activate <= 0 ? EMPTY : timer(value.start, scheduler).pipe(map(() => true)),
      differenceInMilliseconds(value.end, now) >= MAX_DELAY || deactivate <= 0
        ? EMPTY
        : timer(value.end, scheduler).pipe(map(() => false)),
    );
  });
}

@Pipe({
  name: 'dfnsIntervalIsNow',
  pure: true,
  standalone: true,
})
export class IntervalIsNowPipe implements PipeTransform {
  transform(value: NormalizedInterval): Observable<boolean> {
    return isWithinIntervalObservable(value);
  }
}
