/* eslint-disable no-magic-numbers */
/* eslint-disable no-param-reassign */
import { pluginSchema } from '@/models/pluginSchema';
import { axiosInstance } from '@/services/api/utils/axiosInstance';
import type { MinervaPlugin } from '@/types/models';
import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
import { getErrorMessage } from '@/utils/getErrorMessage';
import { ThunkConfig } from '@/types/store';
import { getPluginHashWithoutPrefix } from '@/utils/plugins/getPluginHashWithoutPrefix';
import { apiPath } from '../apiPath';
import {
  PLUGIN_FETCHING_ALL_ERROR_PREFIX,
  PLUGIN_INIT_FETCHING_ERROR_PREFIX,
  PLUGIN_REGISTER_ERROR_PREFIX,
} from './plugins.constants';

type RegisterPlugin = {
  hash: string;
  pluginUrl: string;
  pluginName: string;
  pluginVersion: string;
  isPublic: boolean;
  extendedPluginName: string;
};

export const registerPlugin = createAsyncThunk<
  MinervaPlugin | undefined,
  RegisterPlugin,
  ThunkConfig
>(
  'plugins/registerPlugin',
  async (
    { hash, isPublic, pluginName, pluginUrl, pluginVersion, extendedPluginName },
    { rejectWithValue },
  ) => {
    try {
      const hashWihtoutPrefix = getPluginHashWithoutPrefix(hash);

      const payload = {
        hash: hashWihtoutPrefix,
        url: pluginUrl,
        name: pluginName,
        version: pluginVersion,
        isPublic: isPublic.toString(),
      } as const;

      const response = await axiosInstance.post<MinervaPlugin>(
        apiPath.registerPluign(),
        new URLSearchParams(payload),
        {
          withCredentials: true,
        },
      );

      const isDataValid = validateDataUsingZodSchema(response.data, pluginSchema);

      if (isDataValid) {
        return {
          ...response.data,
          hash,
          name: extendedPluginName,
        };
      }

      return undefined;
    } catch (error) {
      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_REGISTER_ERROR_PREFIX });
      return rejectWithValue(errorMessage);
    }
  },
);

type GetInitPluginsProps = {
  pluginsId: string[];
  setHashedPlugin: ({
    pluginUrl,
    pluginScript,
  }: {
    pluginUrl: string;
    pluginScript: string;
  }) => void;
};

export const getInitPlugins = createAsyncThunk<void, GetInitPluginsProps, ThunkConfig>(
  'plugins/getInitPlugins',
  // eslint-disable-next-line consistent-return
  async ({ pluginsId, setHashedPlugin }, { rejectWithValue }) => {
    try {
      /* eslint-disable no-restricted-syntax, no-await-in-loop */

      for (const pluginId of pluginsId) {
        const hash = getPluginHashWithoutPrefix(pluginId);

        const res = await axiosInstance<MinervaPlugin>(apiPath.getPlugin(hash));

        const isDataValid = validateDataUsingZodSchema(res.data, pluginSchema);

        if (isDataValid) {
          const { urls } = res.data;
          const scriptRes = await axios(urls[0]);
          let pluginScript = scriptRes.data;
          setHashedPlugin({ pluginUrl: urls[0], pluginScript });

          pluginScript += `//# sourceURL=${urls[0]}`;

          /* eslint-disable no-new-func */
          const loadPlugin = new Function(pluginScript);

          loadPlugin();
        }
      }
    } catch (error) {
      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_INIT_FETCHING_ERROR_PREFIX });
      return rejectWithValue(errorMessage);
    }
  },
);

export const getAllPlugins = createAsyncThunk<MinervaPlugin[], void, ThunkConfig>(
  'plugins/getAllPlugins',
  async (_, { rejectWithValue }) => {
    try {
      const response = await axiosInstance.get<MinervaPlugin[]>(apiPath.getAllPlugins());

      const isPluginDataValid = (pluginData: MinervaPlugin): boolean =>
        validateDataUsingZodSchema(pluginData, pluginSchema);
      const validPlugins = response.data.filter(isPluginDataValid);

      return validPlugins;
    } catch (error) {
      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_FETCHING_ALL_ERROR_PREFIX });
      return rejectWithValue(errorMessage);
    }
  },
);