import { Dialog, QSelect, Notify, QNotifyCreateOptions } from 'quasar';
import GlobalDialog from 'components/GlobalDialog.vue';
import { ComposerTranslation, useI18n } from 'vue-i18n';
import { defineStore } from 'pinia';
import { Ref, ref } from 'vue';
import { getRole } from 'src/services/keycloak';
import { AxiosInstance } from 'axios';

export const baseUrl = import.meta.env.VITE_API_BASE_URL;

export function dialog(opts: {
  title: string;
  message: string;
  color?: string;
  icon?: string;
  persistent?: boolean;
  actionText?: string;
  cancelText?: string;
  enablei18n?: boolean;
  action?: (...args: unknown[]) => unknown;
  cancel?: (...args: unknown[]) => unknown;
}) {
  Dialog.create({
    component: GlobalDialog,
    componentProps: opts,
  });
}

export function dialogWarningClose(
  t: ComposerTranslation,
  opts: {
    action?: (...args: unknown[]) => unknown;
    cancel?: (...args: unknown[]) => unknown;
  },
) {
  dialog({
    color: 'warning',
    icon: 'mdi-alert',
    title: t('form.warning.title'),
    actionText: t('dialog.action.ok'),
    message: t('dialog.message.warningClose'),
    action: async () => {
      if (opts.action) opts.action();
    },
    cancel: () => {
      if (opts.cancel) opts.cancel();
    },
  });
}

export function notify(
  type: 'create',
  message?: string,
  opts?: QNotifyCreateOptions | string,
) {
  if (type === 'create') {
    Notify.create({
      icon: 'mdi-check-circle',
      iconColor: 'white',
      iconSize: '42px',
      color: 'positive',
      message: message,
      position: 'bottom',
      timeout: 1000,
      group: false,
      actions: [
        {
          icon: 'mdi-close',
          color: 'white',
          round: true,
          handler: () => {},
        },
      ],
    });
  }
  if (opts) {
    Notify.create(opts);
  }
}

export function moveItemUp(items: unknown[], index: number) {
  if (index > 0) {
    const temp = items[index];
    items[index] = items[index - 1];
    items[index - 1] = temp;
  }
}

export function moveItemDown(items: unknown[], index: number) {
  if (index < items.length - 1) {
    const temp = items[index];
    items[index] = items[index + 1];
    items[index + 1] = temp;
  }
}

export function deleteItem(items: unknown[], index: number) {
  if (!items) return;
  if (index >= 0 && index < items.length) {
    items.splice(index, 1);
  }
}

export function formatNumberDecimal(num: number, point: number): string {
  return num.toLocaleString('eng', {
    minimumFractionDigits: point,
    maximumFractionDigits: point,
  });
}

export function selectFilterOptionRefMod(
  source: Ref<Record<string, unknown>[]>,
  destination: Ref<Record<string, unknown>[]>,
  filterField?: string,
) {
  destination.value = source.value;

  const filter = ((value, update) => {
    if (value === '') update(() => (destination.value = source.value));
    else
      update(
        () => {
          destination.value = source.value.filter((v) => {
            const label = v[filterField || 'label'];

            if (typeof label !== 'string') {
              throw new Error('Label must be of type string.');
            }

            return (
              label.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1
            );
          });
        },
        (ref) => {
          if (value !== '' && destination.value.length > 0) {
            ref.setOptionIndex(-1);
            ref.moveOptionSelection(1, true);
          }
        },
      );
  }) satisfies QSelect['onFilter'];

  return filter;
}

export function selectOptionFilter(
  list: Record<string, unknown>[],
  filterField?: string,
  prepare?: (
    ...args: unknown[]
  ) => Record<string, unknown>[] | Promise<Record<string, unknown>[]>,
) {
  const options = ref<typeof list>([]);

  (async () => {
    const data = await prepare?.();
    if (data) options.value = list = data;
  })();

  const filter = ((value, update) => {
    if (value === '') update(() => (options.value = list));
    else
      update(() => {
        options.value = list.filter((v) => {
          const label = v[filterField || 'label'];

          if (typeof label !== 'string') {
            throw new Error('Label must be of type string.');
          }

          return (
            label.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1
          );
        });
      });
  }) satisfies QSelect['onFilter'];

  return { options, filter };
}

export function checkTabBeforeAdd(data: unknown[], except?: string[]) {
  if (!data) return;
  const lastData = data[data.length - 1];

  if (typeof lastData !== 'object') return false;

  let canAdd = false;
  for (const [key, val] of Object.entries(
    lastData as Record<string, unknown>,
  )) {
    if (except?.includes(key)) continue;
    canAdd = val !== '' && val !== null;
    if (canAdd) break;
  }
  return canAdd;
}

const useUtilsStore = defineStore('utilsStore', () => {
  const currentTitle = ref<{
    title: string;
    path: {
      text: string;
      i18n?: boolean;
      argsi18n?: Record<string, string>;
      handler?: () => unknown;
    }[];
  }>({
    title: '',
    path: [
      {
        text: '',
        handler: () => {},
      },
    ],
  });

  return {
    currentTitle,
  };
});

export function isRoleInclude(role2check: string[]): boolean {
  const roles = getRole() ?? [];
  const isIncluded = role2check.some((r) => roles.includes(r));

  return isIncluded;
}

export function resetScrollBar(elementId: string) {
  const element = document.getElementById(elementId);

  if (element) {
    element.scrollTop = 0;
  }
}

export function manageAttachment(api: AxiosInstance, base: string) {
  return {
    listAttachment: async (opts: { parentId: string }) => {
      const res = await api.get<string[]>(
        `/${base}/${opts.parentId}/attachment`,
      );
      if (res.status >= 400) return false;
      return res.data;
    },
    getAttachment: async (opts: {
      parentId: string;
      name: string;
      download?: boolean;
    }) => {
      const url = `/${base}/${opts.parentId}/attachment/${opts.name}`;
      const res = await api.get<string>(url);
      if (opts.download) {
        fetch(res.data)
          .then(async (res) => await res.blob())
          .then((blob) => {
            const a = document.createElement('a');
            a.download = opts.name;
            a.href = window.URL.createObjectURL(blob);
            a.click();
            a.remove();
          });
      }
      return res.data;
    },
    putAttachment: async (opts: {
      parentId: string;
      name: string;
      file: File;
    }) => {
      const res = await api.put(
        `/${base}/${opts.parentId}/attachment/${opts.name}`,
        opts.file,
        {
          headers: { 'Content-Type': opts.file.type },
          onUploadProgress: (e) => console.log(e),
        },
      );
      if (res.status < 400) return true;
      return false;
    },
    delAttachment: async (opts: { parentId: string; name: string }) => {
      const res = await api.delete(
        `/${base}/${opts.parentId}/attachment/${opts.name}`,
      );
      if (res.status < 400) return true;
      return false;
    },
  };
}

export function manageFile<T extends string>(api: AxiosInstance, base: string) {
  return {
    listFile: async (opts: { group: T; parentId: string }) => {
      const res = await api.get<string[]>(
        `/${base}/${opts.parentId}/file-${opts.group}`,
      );
      if (res.status >= 400) return false;
      return res.data;
    },
    getFile: async (opts: {
      group: T;
      parentId: string;
      fileId: string;
      download?: boolean;
    }) => {
      const url = `/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`;
      const res = await api.get<string>(url);
      if (opts.download) {
        fetch(res.data)
          .then(async (res) => await res.blob())
          .then((blob) => {
            const a = document.createElement('a');
            a.download = opts.fileId;
            a.href = window.URL.createObjectURL(blob);
            a.click();
            a.remove();
          });
      }
      return res.data;
    },
    putFile: async (opts: {
      group: T;
      parentId: string;
      fileId: string;
      file: File;
    }) => {
      const res = await api.put(
        `/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
        opts.file,
        {
          headers: { 'Content-Type': opts.file.type },
          onUploadProgress: (e) => console.log(e),
        },
      );
      if (res.status < 400) return true;
      return false;
    },
    delFile: async (opts: { group: T; parentId: string; fileId: string }) => {
      const res = await api.delete(
        `/${base}/${opts.parentId}/file-${opts.group}/${opts.fileId}`,
      );
      if (res.status < 400) return true;
      return false;
    },
  };
}

export function manageMeta<T extends string>(api: AxiosInstance, base: string) {
  return {
    postMeta: async (opts: {
      group: T;
      parentId: string;
      meta: any;
      file: File;
    }) => {
      const url = `${base}/${opts.parentId}/${opts.group}`;
      const res = await api.post<typeof opts.meta>(url, opts.meta);

      if (res.status < 400) {
        await manageFile(api, base).putFile({
          parentId: opts.parentId,
          group: opts.group,
          fileId: res.data.id,
          file: opts.file,
        });

        return res.data;
      }
    },
    getMetaList: async (opts: { group: T; parentId: string }) => {
      const url = `${base}/${opts.parentId}/${opts.group}`;
      const res = await api.get(url);
      return res.data;
    },
    getMeta: async (opts: { group: T; parentId: string; metaId: string }) => {
      const url = `${base}/${opts.parentId}/${opts.group}/${opts.metaId}`;
      const res = await api.get<string>(url);
      return res.data;
    },
    putMeta: async (opts: {
      group: T;
      parentId: string;
      metaId: string;
      meta: any;
      file?: File;
    }) => {
      const res = await api.put<typeof opts.meta>(
        `${base}/${opts.parentId}/${opts.group}/${opts.metaId}`,
        opts.meta,
      );
      if (res.status < 400) {
        if (opts.file !== undefined)
          await manageFile(api, base).putFile({
            parentId: opts.parentId,
            group: opts.group,
            fileId: res.data.id,
            file: opts.file,
          });
        return res.data;
      }
      return false;
    },
    delMeta: async (opts: { group: T; parentId: string; metaId: string }) => {
      const res = await api.delete(
        `${base}/${opts.parentId}/${opts.group}/${opts.metaId}`,
      );
      if (res.status < 400) return true;
      return false;
    },
  };
}
export async function waitAll<T extends Promise<any>[]>(arr: T) {
  return await Promise.all(arr);
}

export default useUtilsStore;
