import {
  createAction,
  createSelector,
  type PayloadAction,
} from "@reduxjs/toolkit";
import ical from "node-ical";

import type { IcalConfig } from "../../settings";
import type { RemoteTimeBlock } from "../../time-block-types";
import type { WithIcalConfig } from "../../types";
import { createAppSlice } from "../create-app-slice";

export type RawIcal = { icalConfig: IcalConfig; text: string };
export type SerializedRemoteTimeBlock = Omit<RemoteTimeBlock, "startTime"> & {
  startTime: string;
};

export interface IcalState {
  icalEvents: Array<WithIcalConfig<ical.VEvent>>;
  plainTextIcals: Array<RawIcal>;
  serializedRemoteTimeBlocks: Array<SerializedRemoteTimeBlock>;
}

export function isVEvent(event: ical.CalendarComponent): event is ical.VEvent {
  return event.type === "VEVENT";
}

export const initialIcalState: IcalState = {
  icalEvents: [],
  plainTextIcals: [],
  serializedRemoteTimeBlocks: [],
};

export const icalSlice = createAppSlice({
  name: "ical",
  initialState: initialIcalState,
  reducers: (create) => ({
    icalsFetched: create.reducer(
      (state, action: PayloadAction<Array<RawIcal>>) => {
        state.plainTextIcals = action.payload;
      },
    ),
    remoteTimeBlocksUpdated: create.reducer(
      (state, action: PayloadAction<Array<SerializedRemoteTimeBlock>>) => {
        state.serializedRemoteTimeBlocks = action.payload;
      },
    ),
  }),
  selectors: {
    selectSerializedRemoteTimeBlocks: (state) =>
      state.serializedRemoteTimeBlocks,
    selectPlainTextIcals: (state) => state.plainTextIcals,
  },
});

export const icalRefreshRequested = createAction("ical/icalRefreshRequested");

export const { remoteTimeBlocksUpdated, icalsFetched } = icalSlice.actions;
export const { selectPlainTextIcals } = icalSlice.selectors;
const { selectSerializedRemoteTimeBlocks } = icalSlice.selectors;

export const selectAllIcalEventsWithIcalConfigs = createSelector(
  selectPlainTextIcals,
  (rawIcals) =>
    rawIcals.flatMap(
      ({ icalConfig, text }): Array<WithIcalConfig<ical.VEvent>> => {
        const parsed = ical.parseICS(text);

        return Object.values(parsed)
          .filter(isVEvent)
          .map((icalEvent) => ({
            ...icalEvent,
            calendar: icalConfig,
          }));
      },
    ),
);

export const selectRemoteTimeBlocks = createSelector(
  selectSerializedRemoteTimeBlocks,
  (serializedRemoteTimeBlocks) =>
    serializedRemoteTimeBlocks.map((it) => ({
      ...it,
      startTime: window.moment(it.startTime),
    })),
);
