import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { USER_ROLES } from "components/auth/constants";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
  GetUsersResponse,
  UsersApi,
  LicensesApi,
  GetAllocatableLicensesResponse,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { ErrorObject, createErrorObject } from "../../common/errors";

export const listUsersAsync = createAsyncThunk<
  // 正常時の戻り値の型
  GetUsersResponse,
  // 引数
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("users/listUsersAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration } = state.auth;
  const accessToken = getAccessToken(state.auth);
  const config = new Configuration(configuration);
  const usersApi = new UsersApi(config);

  try {
    const res = await usersApi.getUsers({
      headers: { authorization: `Bearer ${accessToken}` },
    });

    return { users: res.data.users };
  } catch (e) {
    // e ⇒ errorObjectに変換
    const error = createErrorObject(e);

    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: getTranslationID("common.message.internalServerError"),
      })
    );

    return thunkApi.rejectWithValue({ error });
  }
});

export const addUserAsync = createAsyncThunk<
  {
    /* Empty Object */
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("users/addUserAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration } = state.auth;
  const accessToken = getAccessToken(state.auth);
  const config = new Configuration(configuration);
  const usersApi = new UsersApi(config);
  const { addUser } = state.user.apps;
  const newUser = { ...addUser };
  // roleがAUTHOR以外の場合、不要なプロパティをundefinedにする
  if (newUser.role !== USER_ROLES.AUTHOR) {
    newUser.authorId = undefined;
    newUser.encryption = undefined;
    newUser.prompt = undefined;
    newUser.encryptionPassword = undefined;
  }

  try {
    await usersApi.signup(newUser, {
      headers: { authorization: `Bearer ${accessToken}` },
    });
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("userListPage.message.addUserSuccess"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換"z
    const error = createErrorObject(e);

    let errorMessage = getTranslationID("common.message.internalServerError");

    if (error.code === "E010301") {
      errorMessage = getTranslationID(
        "signupConfirmPage.message.emailConflictError"
      );
    }
    if (error.code === "E010302") {
      errorMessage = getTranslationID(
        "userListPage.message.authorIdConflictError"
      );
    }

    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: errorMessage,
      })
    );

    return thunkApi.rejectWithValue({ error });
  }
});

export const updateUserAsync = createAsyncThunk<
  {
    /* Empty Object */
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("users/updateUserAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration } = state.auth;
  const accessToken = getAccessToken(state.auth);
  const config = new Configuration(configuration);
  const usersApi = new UsersApi(config);
  const { updateUser } = state.user.apps;

  const authorId =
    updateUser.role === USER_ROLES.AUTHOR ? updateUser.authorId : undefined;
  const encryption =
    updateUser.role === USER_ROLES.AUTHOR ? updateUser.encryption : undefined;
  const encryptionPassword =
    updateUser.role === USER_ROLES.AUTHOR
      ? updateUser.encryptionPassword
      : undefined;
  const prompt =
    updateUser.role === USER_ROLES.AUTHOR ? updateUser.prompt : undefined;

  try {
    await usersApi.updateUser(
      {
        id: updateUser.id,
        role: updateUser.role,
        authorId,
        encryption,
        encryptionPassword,
        prompt,
        autoRenew: updateUser.autoRenew,
        notification: updateUser.notification,
      },
      {
        headers: { authorization: `Bearer ${accessToken}` },
      }
    );
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換"z
    const error = createErrorObject(e);

    let errorMessage = getTranslationID("common.message.internalServerError");

    // Roleが変更できない
    if (error.code === "E010207") {
      errorMessage = getTranslationID("userListPage.message.roleChangeError");
    }
    // AuthorIdが重複している
    if (error.code === "E010302") {
      errorMessage = getTranslationID(
        "userListPage.message.authorIdConflictError"
      );
    }

    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: errorMessage,
      })
    );

    return thunkApi.rejectWithValue({ error });
  }
});

export const getAllocatableLicensesAsync = createAsyncThunk<
  // 正常時の戻り値の型
  GetAllocatableLicensesResponse,
  // 引数
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("users/getAllocatableLicensesAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration } = state.auth;
  const accessToken = getAccessToken(state.auth);
  const config = new Configuration(configuration);
  const licensesApi = new LicensesApi(config);

  try {
    const res = await licensesApi.getAllocatableLicenses({
      headers: { authorization: `Bearer ${accessToken}` },
    });

    return res.data;
  } catch (e) {
    // e ⇒ errorObjectに変換
    const error = createErrorObject(e);

    const errorMessage = getTranslationID("common.message.internalServerError");

    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: errorMessage,
      })
    );

    return thunkApi.rejectWithValue({ error });
  }
});

export const allocateLicenseAsync = createAsyncThunk<
  // 正常時の戻り値の型
  {
    /* Empty Object */
  },
  // 引数
  {
    userId: number;
    newLicenseId: number;
  },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("users/allocateLicenseAsync", async (args, thunkApi) => {
  const { userId, newLicenseId } = args;

  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration } = state.auth;
  const accessToken = getAccessToken(state.auth);
  const config = new Configuration(configuration);
  const usersApi = new UsersApi(config);

  try {
    await usersApi.allocateLicense(
      {
        userId,
        newLicenseId,
      },
      {
        headers: { authorization: `Bearer ${accessToken}` },
      }
    );
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換
    const error = createErrorObject(e);

    let errorMessage = getTranslationID("common.message.internalServerError");

    if (error.code === "E010805") {
      errorMessage = getTranslationID(
        "allocateLicensePopupPage.message.licenseAllocationFailure"
      );
    } else if (error.code === "E010806") {
      errorMessage = getTranslationID(
        "allocateLicensePopupPage.message.licenseAllocationFailure"
      );
    }

    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: errorMessage,
      })
    );

    return thunkApi.rejectWithValue({ error });
  }
});

export const deallocateLicenseAsync = createAsyncThunk<
  // 正常時の戻り値の型
  {
    /* Empty Object */
  },
  // 引数
  {
    userId: number;
  },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("users/deallocateLicenseAsync", async (args, thunkApi) => {
  const { userId } = args;

  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration } = state.auth;
  const accessToken = getAccessToken(state.auth);
  const config = new Configuration(configuration);
  const usersApi = new UsersApi(config);

  try {
    await usersApi.deallocateLicense(
      {
        userId,
      },
      {
        headers: { authorization: `Bearer ${accessToken}` },
      }
    );
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換
    const error = createErrorObject(e);

    let errorMessage = getTranslationID("common.message.internalServerError");

    if (error.code === "E010807") {
      errorMessage = getTranslationID(
        "userListPage.message.alreadyLicenseDeallocatedError"
      );
    }

    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: errorMessage,
      })
    );

    return thunkApi.rejectWithValue({ error });
  }
});
