import { cloneDeep, get, isEqual, isNil, set, snakeCase, uniq, uniqueId } from 'lodash';
import { useQuasar } from 'quasar';
import AppNotificationDialog from 'src/components/dialogs/AppNotificationDialog.vue';
import { ContextActionCode } from 'src/components/layouts/consts';
import { LayoutContext, LayoutType, Popup, PopupAction } from 'src/models';
import CaptacionDocumentRequest from 'src/pages/captaciones/CaptacionDocumentRequest.vue';
import MeetDialog from 'src/widgets/meets/MeetDialog.vue';
import { useLayout } from 'src/widgets/useLayout';
import { computed, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { fetchUniq } from './fetching/fetchUniq';

// TODO: pasar esto por parametro en forma mas limpia
const contextParams = {
  '$captacion.documentos': {
    module: 'captacion',
    queryFn: (id: number) => `w2w-query/captacion(${id})->archivos->tipo_documento(id=archivos.id_tipo)`,
    params: {
      captacion: ['id'],
      archivos: ['id', 'aprobada', 'fecha_subida'],
      tipo_documento: ['id', 'codigo'],
    },
    mapper: (data: any) => data.archivos,
  },
  '$captacion.incidencias': {
    module: 'captacion',
    queryFn: (id: number) => `w2w-query/incidencias(id_captacion=${id})`,
    params: { incidencias: ['id', 'estado'] },
  },
  '$captacion.tie': {
    module: 'captacion',
    queryFn: (id: number) => `w2w-query/ties(id_captacion=${id})`,
    params: { ties: ['id', 'estado'] },
  },
};
const keys = [...Object.keys(contextParams), '$captacion'];

interface ContextVar {
  [key: string]: any;
}

export function usePopup(layoutContext: LayoutContext, layoutType: LayoutType) {
  const router = useRouter();
  const { popups, layoutParams } = useLayout(layoutContext, layoutType);
  const store = useStore();
  const asyncContext = ref<ContextVar>({});
  const cancelTokenKey = uniqueId();

  const fetchAsyncContext = async ({ module, queryFn, params, mapper }: any) => {
    const id = store.getters[`${module}/data`].ID_CAPTACION;
    if (isNil(id)) return Promise.resolve();
    const query = queryFn(id);
    return fetchUniq(query, params, cancelTokenKey).then((data) => (mapper ? mapper(data) : data));
  };

  const asyncVars = computed(() =>
    (popups.value || []).flatMap((popup) => {
      const patterns = keys.join('|');
      const regex = new RegExp(`\\${patterns}`, 'g');
      return popup.condition.replaceAll('?', '').match(regex) || [];
    })
  );

  watch(
    popups,
    () => {
      const _asyncContext = {};
      Promise.allSettled(
        uniq(asyncVars.value).map(async (key: string) => {
          const queryParams = contextParams[key];
          return fetchAsyncContext(queryParams).then((data) => {
            set(_asyncContext, key, data);
          });
        })
      ).then(() => {
        asyncContext.value = _asyncContext;
      });
    },
    { deep: true, immediate: true }
  );

  const currentPopup = computed(
    () =>
      popups.value.find((popup) => {
        const _asyncContext = cloneDeep(asyncContext.value);
        if (asyncVars.value.some((key: string) => get(_asyncContext, key) === undefined)) return false;
        const conditionFn = createFunctionFromString(popup.condition, _asyncContext);
        const result = conditionFn(layoutParams.value);
        return result;
      }) || null
  );

  const { dialog } = useQuasar();
  const getPopupFn = ({ event, payload }: PopupAction) => {
    if (!event) return () => {};
    return (
      {
        close: () => {},
        navigate: () => router.push(payload),
        [ContextActionCode.NEW_MEET]: () =>
          dialog({
            component: MeetDialog,
            componentProps: {
              now: false,
            },
          }),
        [ContextActionCode.REQUEST_DOCUMENTATION]: () =>
          dialog({
            component: CaptacionDocumentRequest,
          }),
      }[event] || (() => {})
    );
  };

  function openDialog(component: string) {
    const name = snakeCase(component);
    const componentName = {
      new_meet: MeetDialog,
      captacion_document_request: CaptacionDocumentRequest,
    }[name];

    dialog({
      component: componentName,
      componentProps: {
        now: false,
      },
    });
  }

  const isPopupViewed = (popup: Popup) => {
    const viewedPopups = JSON.parse(window.sessionStorage.getItem('viewedPopups') || '[]');
    const popupId = `${popup.idLayoutPopup}_${layoutContext}_${layoutParams.value.id}`;
    const isViewed = viewedPopups.includes(popupId);
    if (!isViewed) {
      viewedPopups.push(popupId);
      window.sessionStorage.setItem('viewedPopups', JSON.stringify(viewedPopups));
    }
    return isViewed;
  };

  watch(
    currentPopup,
    (value, prev) => {
      if (!value || isEqual(value, prev)) return;
      const isViewed = isPopupViewed(value);
      if (isViewed && value.showOnce) return;
      dialog({
        component: AppNotificationDialog,
        componentProps: {
          data: {
            title: value.text.title,
            message: value.text.body,
            actions: value.actions.map((action) => ({
              label: action.label,
              fn: getPopupFn(action),
            })),
          },
        },
      });
    },
    {
      immediate: true,
    }
  );
}

function createFunctionFromString(funcStr: string, context: object) {
  const contextKeys = Object.keys(context);
  const contextValues = Object.values(context);
  const func = new Function(...contextKeys, 'varObj', `return (${funcStr})(varObj);`);
  return function (varObj: any) {
    return func(...contextValues, varObj);
  };
}
