/**
 * This boot file registers interceptors for axios
 */
import { useMainStore, api } from '@flaschengeist/api';
import { AxiosError } from 'axios';
import { boot } from 'quasar/wrappers';
import config from 'src/config';
import { clone } from '@flaschengeist/api';

/**
 * Minify data sent to backend server
 *
 * Drop unneeded entities which can be identified by ID.
 *
 * @param obj Object to minify
 * @param cloned If this entity is already cloned (JSON En+Decoded)
 * @returns Minified object (some types are converted, like a Date object is now a ISO string)
 */
function minify(entity: unknown, cloned = false) {
  if (!cloned) entity = clone(entity);

  if (typeof entity === 'object') {
    const obj = entity as { [index: string]: unknown };

    for (const prop in obj) {
      if (obj.hasOwnProperty(prop) && !!obj[prop]) {
        if (Array.isArray(obj[prop])) {
          // eslint-disable-next-line @typescript-eslint/no-unsafe-return
          obj[prop] = (<Array<unknown>>obj[prop]).map((v) => minify(v, true));
        } else if (
          typeof obj[prop] === 'object' &&
          Object.keys(<object>obj[prop]).includes('id') &&
          typeof (<{ id: unknown }>obj[prop])['id'] === 'number' &&
          !isNaN((<{ id: number }>obj[prop])['id'])
        ) {
          obj[prop] = (<{ id: unknown }>obj[prop])['id'];
        }
      }
    }
    return obj;
  }
  return entity;
}

export default boot(({ router }) => {
  // Persisted value is read in plugins.ts boot file!
  if (api.defaults.baseURL === undefined) api.defaults.baseURL = config.baseURL;

  /***
   * Intercept requests
   *   - insert Token if available
   *   - minify JSON requests
   */
  api.interceptors.request.use((config) => {
    const store = useMainStore();
    if (store.session?.token) {
      config.headers = Object.assign(config.headers || {}, {
        Authorization: `Bearer ${store.session.token}`,
      });
    }
    // Minify JSON requests
    if (
      !!config.data &&
      (config.headers === undefined ||
        config.headers['Content-Type'] === undefined ||
        config.headers['Content-Type'] === 'application/json')
    )
      config.data = minify(config.data);
    return config;
  });

  /***
   * Intercept responses
   *   - filter 401 --> handleLoggedOut
   *   - filter timeout or 502-504 --> backendOffline
   */
  api.interceptors.response.use(
    (response) => response,
    async (error) => {
      const store = useMainStore();

      if (error) {
        const e = <AxiosError>error;
        const current = router.currentRoute.value;
        if (
          e.code === 'ECONNABORTED' ||
          (e.response && e.response.status >= 502 && e.response.status <= 504)
        ) {
          let next = current.path;
          if ((current.name == 'login' || current.name == 'offline') && current.query.redirect)
            next = <string>current.query.redirect;
          await router.push({
            name: 'offline',
            query: { redirect: next },
          });
        } else if (e.response && e.response.status == 401) {
          void store.handleLoggedOut();
          if (current.name !== 'login') {
            await router.push({
              name: 'login',
              params: { logout: 'logout' },
              query: { redirect: current.path },
            });
          }
        }
      }
      throw error;
    }
  );
});