import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import type { Boat } from '~/utils/boat.types';
import type { Timestamp } from '@quasar/quasar-ui-qcalendar';
import { today } from '@quasar/quasar-ui-qcalendar';
import type { Interval } from '~/utils/schedule.types';
import { AppwriteIds, databases } from '~/utils/appwrite';
import { ID, Query } from 'appwrite';
import { useReservationStore } from './reservation';
import type { LoadingTypes } from '~/utils/misc';
import { useRealtimeStore } from './realtime';

export const useIntervalStore = defineStore('interval', () => {
  const intervals = ref(new Map<string, Interval>());
  const dateStatus = ref(new Map<string, LoadingTypes>());

  const selectedDate = ref<string>(today());

  const reservationStore = useReservationStore();
  const realtimeStore = useRealtimeStore();

  realtimeStore.register(
    `databases.${AppwriteIds.databaseId}.collections.${AppwriteIds.collection.interval}.documents`,
    (response) => {
      const payload = response.payload as unknown as Interval;
      if (!payload.$id) return;
      if (
        response.events.includes('databases.*.collections.*.documents.*.delete')
      ) {
        intervals.value.delete(payload.$id);
      } else {
        intervals.value.set(payload.$id, payload);
      }
    }
  );

  const getIntervals = (date: Timestamp | string, boat?: Boat) => {
    const searchDate = typeof date === 'string' ? date : date.date;
    const dayStart = new Date(searchDate + 'T00:00');
    const dayEnd = new Date(searchDate + 'T23:59');
    if (dateStatus.value.get(searchDate) === undefined) {
      dateStatus.value.set(searchDate, 'pending');
      fetchIntervals(searchDate);
    }
    return computed(() => {
      return Array.from(intervals.value.values()).filter((interval) => {
        const intervalStart = new Date(interval.start);
        const intervalEnd = new Date(interval.end);

        const isWithinDay = intervalStart < dayEnd && intervalEnd > dayStart;
        const matchesBoat = boat ? boat.$id === interval.resource : true;
        return isWithinDay && matchesBoat;
      });
    });
  };

  const getAvailableIntervals = (date: Timestamp | string, boat?: Boat) => {
    return computed(() =>
      getIntervals(date, boat).value.filter((interval) => {
        return !reservationStore.isResourceTimeOverlapped(
          interval.resource,
          new Date(interval.start),
          new Date(interval.end)
        );
      })
    );
  };

  async function fetchInterval(id: string): Promise<Interval> {
    return (await databases.getDocument(
      AppwriteIds.databaseId,
      AppwriteIds.collection.interval,
      id
    )) as Interval;
  }

  async function fetchIntervals(dateString: string) {
    try {
      const response = await databases.listDocuments(
        AppwriteIds.databaseId,
        AppwriteIds.collection.interval,
        [
          Query.greaterThanEqual(
            'end',
            new Date(dateString + 'T00:00').toISOString()
          ),
          Query.lessThanEqual(
            'start',
            new Date(dateString + 'T23:59').toISOString()
          ),
          Query.limit(50),
        ]
      );
      response.documents.forEach((d) =>
        intervals.value.set(d.$id, d as unknown as Interval)
      );
      dateStatus.value.set(dateString, 'loaded');
      console.info(`Loaded ${response.documents.length} intervals from server`);
    } catch (error) {
      console.error('Failed to fetch intervals', error);
      dateStatus.value.set(dateString, 'error');
    }
  }

  const createInterval = async (interval: Interval) => {
    try {
      const response = await databases.createDocument(
        AppwriteIds.databaseId,
        AppwriteIds.collection.interval,
        ID.unique(),
        interval
      );
      intervals.value.set(response.$id, response as unknown as Interval);
    } catch (e) {
      console.error('Error creating Interval: ' + e);
    }
  };

  const updateInterval = async (interval: Interval) => {
    try {
      if (interval.$id) {
        const response = await databases.updateDocument(
          AppwriteIds.databaseId,
          AppwriteIds.collection.interval,
          interval.$id,
          { ...interval, $id: undefined }
        );
        intervals.value.set(response.$id, response as unknown as Interval);
        console.info(`Saved Interval: ${interval.$id}`);
      } else {
        console.error('Update interval called without an ID');
      }
    } catch (e) {
      console.error('Error updating Interval: ' + e);
    }
  };

  const deleteInterval = async (id: string) => {
    try {
      await databases.deleteDocument(
        AppwriteIds.databaseId,
        AppwriteIds.collection.interval,
        id
      );
      intervals.value.delete(id);
      console.info(`Deleted interval: ${id}`);
    } catch (e) {
      console.error('Error deleting Interval: ' + e);
    }
  };

  return {
    getIntervals,
    getAvailableIntervals,
    fetchIntervals,
    fetchInterval,
    createInterval,
    updateInterval,
    deleteInterval,
    selectedDate,
    intervals,
  };
});
