import type {
  EventStore as ServerEventStore, StandardEvent, LessonEvent, EventInput,
} from '../sharedworker/datastores/eventstore';
import type { Database } from './database';
import type { TimeLabelStore, TimeLabel } from './timelabelstore';
import type { ResourceStore, Resource } from './resourcestore';
import { DateTime, cmp } from '@silane/datetime';


export type Event = (
  Omit<StandardEvent, 'resources' | 'timeLabel'> |
  Omit<LessonEvent, 'resources' | 'timeLabel'>
) & {
  resources: Resource[], timeLabel: TimeLabel | null,
  eventChangeTo: { toEvent: Event | null, causeResources: Resource[] } | null,
  eventChangeFrom: { fromEvent: Event, causeResources: Resource[] } | null,
};

export function useEventStore(
  database: Database,
  timeLabelStore: TimeLabelStore, resourceStore: ResourceStore,
) {
  async function mutate(
    ops: Parameters<ServerEventStore['mutate']>[0]
  ): ReturnType<ServerEventStore['mutate']> {
    return await database.mutate('event', ops) as any;
  }

  async function query(
    param: Parameters<ServerEventStore['query']>[0]
  ): Promise<Event[]> {
    const resp = await (database.query(
      'event', param
    ) as ReturnType<ServerEventStore['query']>);

    const events: Event[] = resp.events.map(x => ({
      ...x, resources: x.resources.map(
        resourceID => resourceStore.resources.value.find(
          r => r.id === resourceID
        )
      ).filter((r): r is Resource => r != null),
      timeLabel: timeLabelStore.timeLabels.value.find(
        t => t.id === x.timeLabel
      ) ?? null, eventChangeFrom: null, eventChangeTo: null,
    }) as Event);

    for(const ec of resp.eventChanges) {
      const fromEvent = events.find(x => x.id === ec.fromEvent)!;
      const toEvent = ec.toEvent == null ? null : events.find(
        x => x.id === ec.toEvent
      )!;
      fromEvent.eventChangeTo = {
        toEvent: toEvent, causeResources: ec.causeResources.map(
          x => resourceStore.resources.value.find(r => r.id === x)
        ).filter((r): r is Resource => r != null),
      };
      if(toEvent != null) {
        toEvent.eventChangeFrom = {
          fromEvent: fromEvent, causeResources: ec.causeResources.map(
            x => resourceStore.resources.value.find(r => r.id === x)
          ).filter((r): r is Resource => r != null),
        };
      }
    }

    // 振替先、振替元のイベントとしてサーバーから返ってきて、条件外のものは結果から除外
    return events.filter(x => (
      param.id == null || x.id === param.id
    ) && (
      !param.startTime || cmp(param.startTime, x.endTime) < 0
    ) && (
      !param.endTime || cmp(x.startTime, param.endTime) < 0
    ));
  }

  return { mutate, query };
}

export type EventStore = Awaited<ReturnType<
  typeof useEventStore
>>;

export function eventToEventInput(event: {
  readonly resources: readonly Resource[],
  readonly startTime: DateTime, readonly endTime: DateTime,
  readonly timeLabel: TimeLabel | null, readonly memo: string,
  readonly color: string,
} & ({ readonly type: 'standard' } | {
  readonly type: 'lesson', subject: string, frame: string,
})): EventInput {
  const ret = {
    resources: event.resources.map(x => x.id),
    startTime: event.startTime, endTime: event.endTime,
    timeLabel: event.timeLabel?.id ?? null, memo: event.memo,
    color: event.color,
  }
  if(event.type === 'standard') {
    return { ...ret, type: 'standard' };
  } else if(event.type === 'lesson') {
    return {
      ...ret, type: 'lesson', subject: event.subject, frame: event.frame,
    };
  } else {
    throw new Error('Unrecognized event type');
  }
}
