/*
 * Copyright (C) 2022 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 {Calendar} from '@awesome-cordova-plugins/calendar/ngx';
import {Injectable} from '@angular/core';
import {ICalEvent} from './ical/ical';
import moment, {duration, Moment, unitOfTime} from 'moment';
import {Dialog} from '@capacitor/dialog';
import {CalendarInfo} from './calendar-info';
import {Subject} from 'rxjs';
import {ConfigProvider} from '../config/config.provider';

const RECURRENCE_PATTERNS: Partial<Record<unitOfTime.Diff, string | undefined>> = {
  year: 'yearly',
  month: 'monthly',
  week: 'weekly',
  day: 'daily',
};

@Injectable()
export class CalendarService {
  goToDate = new Subject<number>();

  goToDateClicked = this.goToDate.asObservable();

  calendarName = 'StApps';

  // eslint-disable-next-line @typescript-eslint/no-empty-function
  constructor(
    readonly calendar: Calendar,
    private readonly configProvider: ConfigProvider,
  ) {
    this.calendarName = (this.configProvider.getValue('name') as string) ?? 'StApps';
  }

  async createCalendar(): Promise<CalendarInfo | undefined> {
    await this.calendar.createCalendar({
      calendarName: this.calendarName,
      calendarColor: '#ff8740',
    });
    return this.findCalendar(this.calendarName);
  }

  async listCalendars(): Promise<CalendarInfo[] | undefined> {
    return this.calendar.listCalendars();
  }

  async findCalendar(name: string): Promise<CalendarInfo | undefined> {
    return (await this.listCalendars())?.find((calendar: CalendarInfo) => calendar.name === name);
  }

  async purge(): Promise<CalendarInfo | undefined> {
    if (await this.findCalendar(this.calendarName)) {
      await this.calendar.deleteCalendar(this.calendarName);
    }
    return await this.createCalendar();
  }

  async syncEvents(events: ICalEvent[]) {
    const calendar = await this.purge();
    if (!calendar) {
      return Dialog.alert({
        title: 'Error',
        message: 'Could not create calendar',
      });
    }

    for (const iCalEvent of events) {
      // TODO: change to use non-interactive version after testing is complete
      const start = iCalEvent.rrule ? iCalEvent.rrule.from : iCalEvent.start;

      await this.calendar.createEventWithOptions(
        iCalEvent.recurrenceSequence
          ? `(${iCalEvent.recurrenceSequence}/${iCalEvent.recurrenceSequenceAmount}) ${iCalEvent.name}`
          : iCalEvent.name,
        iCalEvent.geo,
        iCalEvent.description,
        new Date(start),
        moment(start).add(duration(iCalEvent.duration)).toDate(),
        {
          id: `${iCalEvent.uuid}-${start}`,
          url: iCalEvent.url,
          calendarName: calendar.name,
          calendarId: calendar.id,
          ...(iCalEvent.rrule
            ? {
                recurrence: RECURRENCE_PATTERNS[iCalEvent.rrule.freq],
                recurrenceInterval: iCalEvent.rrule.interval,
                recurrenceEndDate: new Date(iCalEvent.rrule.until),
              }
            : {}),
        },
      );
    }
  }

  /**
   * Emit the calendar index corresponding to the input date.
   * @param date Moment - date the calendar should go to
   */
  emitGoToDate(date: Moment) {
    const index = date.diff(moment().startOf('day'), 'days');
    this.goToDate.next(index);
  }
}
