import * as store from '../store/store';
import * as React from 'react';
import { createContext, useEffect } from 'react';
import { useClientContext } from '../hooks/useClientContext';
import { Record } from '../interfaces/Record';
import { recordsAction } from '../store/actions';
import { subscriptionsAction } from '../store/actions';

type SubscribeType = (collectionName: string) => Promise<void>;
type UnsubscribeType = (collectionName?: string) => Promise<void>;
type FetchType = (collectionName: string, queryParams?: any) => Promise<void>;
type CreateType = (collectionName: string, record: {}) => Promise<void | Record | undefined | any>;
type UpdateType = (
  collectionName: string,
  recordId: string,
  record: {}
) => Promise<void | Record | undefined | any>;
type DeleteType = (collectionName: string, recordId: string) => Promise<void | boolean | undefined>;

interface ContentActions {
  subscribe: SubscribeType;
  unsubscribe: UnsubscribeType;
  fetch: FetchType;
  create: CreateType;
  update: UpdateType;
  delete: DeleteType;
}

export const ContentContext = createContext<ContentActions>({} as ContentActions);

export type ContentProviderProps = {
  children: React.ReactNode;
  collections?: string[];
};

interface MessageData {
  action: string;
  record: Record;
}

export const ContentProvider = (props: ContentProviderProps) => {
  const client = useClientContext();
  const dispatch = store.useAppDispatch;

  function tempErrorHandler(error: any) {
    // TODO: Handle error
    // IDEA: Create new ErrorContext and Update it with error
    if (error?.originalError?.name !== 'AbortError') {
      console.log('Error in content provider', JSON.stringify(error));
    }
    return error;
  }
  const actions: ContentActions = {
    subscribe: async (collectionName: string) => {
      try {
        await client?.realtime
          .subscribe(collectionName, (event: MessageData) => {
            switch (event.action) {
              case 'create':
                dispatch(recordsAction.addRecord(collectionName, event.record));
                break;
              case 'update':
                dispatch(recordsAction.updateRecord(collectionName, event.record));
                break;
              case 'delete':
                dispatch(recordsAction.deleteRecord(collectionName, event.record));
                break;
              default:
                break;
            }
          })
          .then(() => {
            dispatch(subscriptionsAction.addSubscription(collectionName));
          });
      } catch (error: any) {
        const errors = JSON.parse(JSON.stringify(error));
        throw errors;
      }
    },
    unsubscribe: async (collectionName?: string) => {
      try {
        if (collectionName) {
          await client?.realtime.unsubscribe(collectionName).then(() => {
            dispatch(subscriptionsAction.deleteSubscription(collectionName));
          });
        } else {
          await client?.realtime.unsubscribe().then(() => {
            dispatch(subscriptionsAction.setSubscriptions([]));
          });
        }
      } catch (error: any) {
        const errors = JSON.parse(JSON.stringify(error));
        throw errors;
      }
    },
    fetch: async (collectionName: string, queryParams?: any) => {
      console.log(queryParams);
      try {
        await client
          ?.collection(collectionName)
          .getFullList(200, queryParams)
          .then((records) => {
            dispatch(recordsAction.setRecords(collectionName, records as Record[]));
          });
      } catch (error: any) {
        const errors = JSON.parse(JSON.stringify(error));
        throw errors;
      }
    },
    create: async (collectionName: string, record: {}) => {
      try {
        return await client?.collection(collectionName).create(record);
      } catch (error: any) {
        const errors = JSON.parse(JSON.stringify(error));
        throw errors;
      }
    },
    update: async (collectionName: string, recordId: string, record: {}) => {
      try {
        return await client?.collection(collectionName).update(recordId, record);
      } catch (error: any) {
        const errors = JSON.parse(JSON.stringify(error));
        throw errors;
      }
      // .catch(tempErrorHandler);
    },
    delete: async (collectionName: string, recordId: string) => {
      try {
        return await client?.collection(collectionName).delete(recordId);
      } catch (error: any) {
        const errors = JSON.parse(JSON.stringify(error));
        throw errors;
      }
    },
  };

  useEffect(() => {
    if (props.collections) {
      props.collections.forEach(async (collectionName) => {
        await actions.fetch(collectionName);
        await actions.subscribe(collectionName);
      });
    }
    return () => {
      (async () => {
        await actions.unsubscribe();
      })();
    };
  }, [props.collections]);

  return <ContentContext.Provider value={actions}>{props.children}</ContentContext.Provider>;
};
