import { defineStore } from 'pinia';
import { Ref, computed, ref } from 'vue';
import { Boat } from './boat';
import { Timestamp } from '@quasar/quasar-ui-qcalendar';

import { IntervalTemplate, Interval, IntervalRecord } from './schedule.types';
import { AppwriteIds, databases } from 'src/boot/appwrite';
import { ID, Models, Query } from 'appwrite';
import { arrayToTimeTuples } from 'src/utils/schedule';

export const useScheduleStore = defineStore('schedule', () => {
  // TODO: Implement functions to dynamically pull this data.
  const intervals = ref<Map<string, Interval>>(new Map());
  const intervalDates = ref<IntervalRecord>({});
  const intervalTemplates = ref<IntervalTemplate[]>([]);

  const getIntervals = (date: Timestamp | string, boat?: Boat): Interval[] => {
    const searchDate = typeof date === 'string' ? date : date.date;
    const dayStart = new Date(searchDate + 'T00:00');
    const dayEnd = new Date(searchDate + 'T23:59');
    if (!intervalDates.value[searchDate]) {
      intervalDates.value[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.boatId : true;
        return isWithinDay && matchesBoat;
      });
    }).value;
  };

  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), // We are asuming that we won't have more than 50 intervals per day.
        ]
      );
      response.documents.forEach((d) =>
        intervals.value.set(d.$id, d as Interval)
      );
      intervalDates.value[dateString] = 'loaded';
      console.info(`Loaded ${response.documents.length} intervals from server`);
    } catch (error) {
      console.error('Failed to fetch intervals', error);
      intervalDates.value[dateString] = 'error';
    }
  }

  const getIntervalTemplates = (): Ref<IntervalTemplate[]> => {
    // Should subscribe to get new intervaltemplates when they are created
    if (!intervalTemplates.value) fetchIntervalTemplates();
    return intervalTemplates;
  };

  async function fetchIntervalTemplates() {
    try {
      const response = await databases.listDocuments(
        AppwriteIds.databaseId,
        AppwriteIds.collection.intervalTemplate
      );
      intervalTemplates.value = response.documents.map(
        (d: Models.Document): IntervalTemplate => {
          return {
            ...d,
            timeTuples: arrayToTimeTuples(d.timeTuple),
          } as IntervalTemplate;
        }
      );
    } catch (error) {
      console.error('Failed to fetch timeblock templates', 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 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 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);
    }
  };
  const createIntervalTemplate = async (template: IntervalTemplate) => {
    try {
      const response = await databases.createDocument(
        AppwriteIds.databaseId,
        AppwriteIds.collection.intervalTemplate,
        ID.unique(),
        { name: template.name, timeTuple: template.timeTuples.flat(2) }
      );
      intervalTemplates.value.push(response as IntervalTemplate);
    } catch (e) {
      console.error('Error updating IntervalTemplate: ' + e);
    }
  };
  const deleteIntervalTemplate = async (id: string) => {
    try {
      await databases.deleteDocument(
        AppwriteIds.databaseId,
        AppwriteIds.collection.intervalTemplate,
        id
      );
      intervalTemplates.value = intervalTemplates.value.filter(
        (template) => template.$id !== id
      );
    } catch (e) {
      console.error('Error deleting IntervalTemplate: ' + e);
    }
  };
  const updateIntervalTemplate = async (
    template: IntervalTemplate,
    id: string
  ) => {
    try {
      const response = await databases.updateDocument(
        AppwriteIds.databaseId,
        AppwriteIds.collection.intervalTemplate,
        id,
        {
          name: template.name,
          timeTuple: template.timeTuples.flat(2),
        }
      );
      intervalTemplates.value = intervalTemplates.value.map((b) =>
        b.$id !== id
          ? b
          : ({
              ...response,
              timeTuples: arrayToTimeTuples(response.timeTuple),
            } as IntervalTemplate)
      );
    } catch (e) {
      console.error('Error updating IntervalTemplate: ' + e);
    }
  };

  return {
    getIntervals,
    getIntervalTemplates,
    fetchIntervals,
    fetchIntervalTemplates,
    createInterval,
    updateInterval,
    deleteInterval,
    createIntervalTemplate,
    deleteIntervalTemplate,
    updateIntervalTemplate,
  };
});
