import { initAmplitude } from '@ata/analytics';
import { useAuth } from '@ata/auth/composables';
import { createAuth } from '@ata/auth/plugins';
import { HumanizeTime } from '@ata/dt';
import BugsnagPluginVue from '@bugsnag/plugin-vue';
import {
  clearNonPersistentLocalStorageItems,
  useHandleMutationErrors,
  useHandleMutationSuccess,
  useHandleQueryErrors,
} from '@core/utilities';
import App from '@shell/App.vue';
import { queryClientKey } from '@shell/context';
import { head } from '@shell/plugins/head';
import { router } from '@shell/plugins/router';
import { store } from '@shell/plugins/store';
import { useLoadingIndicatorStore, useUserStore } from '@shell/stores';
import { MutationCache, QueryCache, QueryClient, VueQueryPlugin } from '@tanstack/vue-query';
import { isAxiosError } from 'axios';
import { setupCalendar } from 'v-calendar';
import { type Component, createApp } from 'vue';
import { setApiAuth } from './api';

import '@unocss/reset/tailwind.css';
import 'virtual:uno.css';
import '@ata/ui/validation';
import '@shell/index.css';
import 'tippy.js/dist/tippy.css';
import 'v-calendar/style.css';
import '@egjs/vue3-flicking/dist/flicking.css';
import 'vue-multiselect/dist/vue-multiselect.min.css';

(async () => {
  const app = createApp(App as Component);

  logger.init(
    {
      appVersion: `${__APP_VERSION__}-${__APP_COMMIT_HASH__}`,
      apiKey: __BUGSNAG_API_KEY__,
      plugins: [new BugsnagPluginVue()],
      enableConsole: import.meta.env.MODE === 'development',
      releaseStage: import.meta.env.MODE,
      enabledReleaseStages: ['production'],
    },
    { vue: app.use }
  );

  app.use(i18n);
  app.use(store);

  const userStore = useUserStore();
  const loaderStore = useLoadingIndicatorStore();

  const auth = createAuth({
    serverUrl: import.meta.env.VITE_API_NODE_BASE_URL,
    authUrl: import.meta.env.VITE_LOGIN_URL,
    clientId: import.meta.env.VITE_CLIENT_ID,
    hostUrl: import.meta.env.VITE_BP_HOST_URL,
    onLogIn: (user) => {
      userStore.setUserData(user);
    },
    onLogOut: () => {
      loaderStore.register();
      userStore.$reset();
      clearNonPersistentLocalStorageItems();
    },
  });

  app.use(auth);

  // HACK: see `apps/best-pilot/core/api/useAuthorizedApi.ts`
  app.runWithContext(() => {
    setApiAuth(useAuth());
  });

  app.use(setupCalendar, {});

  const handleQueryErrors = useHandleQueryErrors(i18n.global.t);
  const handleMutationSuccess = useHandleMutationSuccess();
  const handleMutationErrors = useHandleMutationErrors(i18n.global.t);

  const queryClient = new QueryClient({
    queryCache: new QueryCache({
      onError: handleQueryErrors,
    }),
    mutationCache: new MutationCache({
      onError: handleMutationErrors,
      onSuccess: handleMutationSuccess,
    }),
    defaultOptions: {
      queries: {
        retry: (failureCount, error) => {
          if (isAxiosError(error)) {
            const status = error.response?.status;

            if (status && status >= 400 && status < 500) {
              return false;
            }
          }

          return failureCount < 2;
        },
        networkMode: 'always',
        staleTime: HumanizeTime.fromMinutes(1).toMilliseconds(),
        refetchInterval: HumanizeTime.fromMinutes(1).toMilliseconds(),
      },
      mutations: { networkMode: 'always' },
    },
  });

  app.provide(queryClientKey, queryClient);
  app.use(VueQueryPlugin, { queryClient });

  whenever(
    () => userStore.email,
    (v) => {
      logger.setUserEmail(v);
    },
    { immediate: true }
  );

  await initAmplitude({
    app: 'dashboard',
    apiKey: import.meta.env.VITE_AMPLITUDE_API_KEY,
  });

  app.use(router);
  app.use(head);

  app.mount('#app');

  document.querySelector('#loader')?.remove();
})();

// Headless UI doesn't allow to control if dialog is disabling or not scroll on the page (it just disables it)
// thats why we use this hack to unset it every time something sets it
// https://github.com/tailwindlabs/headlessui/discussions/510
// https://github.com/tailwindlabs/headlessui/issues/1199
const observe = (sel: string, opt: object, cb: (record: MutationRecord) => void) => {
  const obs = new MutationObserver((m) => [...m].forEach(cb));

  for (const el of document.querySelectorAll(sel)) {
    obs.observe(el, opt);
  }
};

observe('html', { attributesList: ['style'], attributeOldValue: true }, (m) => {
  if ((m.target as HTMLElement).style.overflow) {
    document.querySelectorAll('html')[0].setAttribute('style', '');
  }
});

// The application used to be a PWA, so we need to unregister all leftover Service Workers
if (navigator.serviceWorker as ServiceWorkerContainer | undefined) {
  (async () => {
    const registrations = await navigator.serviceWorker.getRegistrations();
    for (const registration of registrations) {
      registration.unregister();
    }
  })();
}

// Also let's not leave any IndexedDB databases behind
try {
  (async () => {
    for (const db in await window.indexedDB.databases()) {
      window.indexedDB.deleteDatabase(db);
    }
  })();
} catch {}
