/*
 * Copyright (C) 2023 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import type {nominatim_object} from 'opening_hours';
import {from, Observable, map, expand, of, delay} from 'rxjs';
import {lazy} from './rxjs/lazy';
import {differenceInMilliseconds, isAfter, subHours} from 'date-fns';

export const OPENING_HOURS_REFERENCE = {
  address: {
    country_code: 'de',
    state: 'Hessen',
  },
  lon: 8.667_97,
  lat: 50.129_16,
} satisfies nominatim_object;

export interface OpeningHoursInfo {
  isOpen: boolean;
  changesSoon: boolean;
  isUnknown: boolean;
  nextChange?: Date;
  comment?: string;
}

/**
 * Opening Hours is a pretty huge CommonJS module,
 * so we lazy-load it
 */
const OpeningHours = lazy(() => import('opening_hours').then(it => it.default));

/**
 * Create an observable from opening hours
 * @param openingHours The opening hours string
 * @param soonThresholdHours The number of hours before which a change is marked as "soon"
 */
export function fromOpeningHours(openingHours: string, soonThresholdHours = 1): Observable<OpeningHoursInfo> {
  return from(OpeningHours).pipe(
    map(OpeningHours => new OpeningHours(openingHours, OPENING_HOURS_REFERENCE)),
    expand(it => {
      const now = new Date();
      const nextChange = it.getNextChange(now);
      const changesSoon = nextChange ? isAfter(now, subHours(nextChange, soonThresholdHours)) : false;

      const changeTime = nextChange && !changesSoon ? subHours(nextChange, soonThresholdHours) : nextChange;
      if (!changeTime) return of();

      // Safari has issues with this. The value comes out to about 24 days
      const maxSafeValue = 0x7f_ff_ff_ff;
      const timeDifference = differenceInMilliseconds(changeTime, Date.now());

      return of(it).pipe(delay(timeDifference >= maxSafeValue ? maxSafeValue : changeTime));
    }),
    map(it => {
      const now = new Date();
      const nextChange = it.getNextChange(now);

      return {
        isOpen: it.getState(now),
        isUnknown: it.getUnknown(now),
        changesSoon: nextChange ? isAfter(now, subHours(nextChange, soonThresholdHours)) : false,
        comment: it.getComment(now),
        nextChange,
      };
    }),
  );
}
