import { createSyncStore } from '@warkypublic/zustandsyncstore';
import { produce } from 'immer';

import type { FormerProps, FormerState } from './Former.types';
import { newUUID } from '@warkypublic/artemis-kit';

const { Provider: FormerProvider, useStore: useFormerStore } = createSyncStore<
  FormerState<any> & Partial<FormerProps<any>>,
  FormerProps<any>
>(
  (set, get) => ({
    getState: (key) => {
      const current = get();
      return current?.[key];
    },
    load: async (reset?: boolean) => {
      try {
        set({ loading: true });
        const keyName = get()?.apiKeyField || 'id';
        const keyValue = (get().values as any)?.[keyName] ?? (get().primeData as any)?.[keyName];
        if (get().onAPICall && keyValue !== undefined) {
          let data = await get().onAPICall!(
            'read',
            get().request || 'insert',
            get().values,
            keyValue
          );
          if (get().afterGet) {
            data = await get().afterGet!({ ...data });
          }
          set({ loading: false, values: data });
          get().onChange?.(data);
        }
        if (reset && get().getFormMethods) {
          const formMethods = get().getFormMethods!();
          formMethods.reset();
        }
      } catch (e) {
        set({ error: (e as Error)?.message ?? e, loading: false });
      }
      set({ loading: false });
    },

    onChange: (values) => {
      set({ values });
    },
    request: 'insert',
    reset: async () => {
      const state = get();
      if (state.getFormMethods) {
        if (state.request !== 'insert') {
          await state.load(true);
        }

        const formMethods = state.getFormMethods!();
        formMethods.reset({ ...state.values, ...state.primeData });
      }
    },
    save: async (e?: React.BaseSyntheticEvent<object, any, any> | undefined) => {
      try {
        const keepOpen = get().keepOpen ?? false;
        set({ loading: true });
        if (get().getFormMethods) {
          const formMethods = get().getFormMethods!();

          let data = formMethods.getValues();

          if (get().beforeSave) {
            const newData = await get().beforeSave!(data);
            data = newData;
          }

          let exit = false;
          const handler = formMethods.handleSubmit(
            (newdata) => {
              data = newdata;
            },
            (errors) => {
              set({ error: errors.root?.message || 'Validation errors', loading: false });
              exit = true;
            }
          );

          await handler(e);

          //console.log('Former.store.tsx save called', success, e, data, get().getFormMethods);
          if (exit) {
            set({ loading: false });
            return undefined;
          }

          if (get().request === 'delete' && !get().deleteConfirmed) {
            const confirmed = (await get().onConfirmDelete?.(data)) ?? false;
            if (!confirmed) {
              set({ loading: false });
              return undefined;
            }
          }

          if (get().onAPICall) {
            const keyName = get()?.apiKeyField || 'id';
            const keyValue =
              (get().values as any)?.[keyName] ?? (get().primeData as any)?.[keyName];
            const savedData = await get().onAPICall!(
              'mutate',
              get().request || 'insert',
              data,
              keyValue
            );
            if (get().afterSave) {
              await get().afterSave!(savedData);
            }
            set({ loading: false, values: savedData });
            get().onChange?.(savedData);
            if (!keepOpen) {
              get().onClose?.(savedData);
            }
            return savedData;
          }

          set({ loading: false, values: data });
          get().onChange?.(data);
          if (!keepOpen) {
            get().onClose?.(data);
          }

          return data;
        }
      } catch (e) {
        set({ error: (e as Error)?.message ?? e, loading: false });
      }

      return undefined;
    },
    setRequest: (request) => {
      set({ request });
    },
    setState: (key, value) => {
      set(
        produce((state) => {
          state[key] = value;
        })
      );
    },
    setStateFN: (key, value) => {
      const p = new Promise<void>((resolve, reject) => {
        set(
          produce((state) => {
            if (typeof value === 'function') {
              state[key] = (value as (value: unknown) => unknown)(state[key]);
            } else {
              reject(new Error(`Not a function ${value}`));
              throw Error(`Not a function ${value}`);
            }
          })
        );
        resolve();
      });

      return p;
    },
    validate: async () => {
      if (get().getFormMethods) {
        const formMethods = get().getFormMethods!();
        const isValid = await formMethods.trigger();
        return isValid;
      }
      return true;
    },
    values: undefined,
  }),
  ({ onConfirmDelete, primeData, request, values, id }) => {
    let _onConfirmDelete = onConfirmDelete;
    if (!onConfirmDelete) {
      _onConfirmDelete = async () => {
        return confirm('Are you sure you want to delete this item?');
      };
    }

    return {
      onConfirmDelete: _onConfirmDelete,
      primeData,
      request: request || 'insert',
      values: { ...primeData, ...values },
      id: !id ? newUUID() : id,
    };
  }
);

export { FormerProvider };
export { useFormerStore };
