import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
  AccountsApi,
  GetOptionItemsResponse,
  GetWorktypesResponse,
} from "../../../api/api";
import { Configuration } from "../../../api/configuration";
import { ErrorObject, createErrorObject } from "../../../common/errors";

export const listWorktypesAsync = createAsyncThunk<
  GetWorktypesResponse,
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/listWorktypesAsync", 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 accountsApi = new AccountsApi(config);

  try {
    const worktypes = (
      await accountsApi.getWorktypes({
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;

    return worktypes;
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);
    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: getTranslationID("common.message.internalServerError"),
      })
    );
    return thunkApi.rejectWithValue({ error });
  }
});

export const addWorktypeAsync = createAsyncThunk<
  {
    // return empty
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/addWorktypeAsync", 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 accountsApi = new AccountsApi(config);
  // stateからworktypeIdとdescriptionを取得する
  const { worktypeId, description } = state.worktype.apps;

  try {
    await accountsApi.createWorktype(
      {
        worktypeId,
        description,
      },
      {
        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");

    // 既に同じworktypeIdが存在する場合
    if (error.code === "E011001") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.alreadyWorktypeIdExistError"
      );
    }
    // worktypeIdが上限に達している場合
    if (error.code === "E011002") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.worktypeIDLimitError"
      );
    }
    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: errorMessage,
      })
    );
    return thunkApi.rejectWithValue({ error });
  }
});

export const editWorktypeAsync = createAsyncThunk<
  {
    // return empty
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/editWorktypeAsync", 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 accountsApi = new AccountsApi(config);
  // stateからselectedId,worktypeId,descriptionを取得する
  const { selectedId, worktypeId, description } = state.worktype.apps;

  try {
    await accountsApi.updateWorktype(
      selectedId,
      {
        worktypeId,
        description,
      },
      {
        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");

    // 既に同じworktypeIdが存在する場合
    if (error.code === "E011001") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.alreadyWorktypeIdExistError"
      );
    }
    // Worktypeの保存に失敗した場合
    if (error.code === "E011003") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.updateWorktypeFailedError"
      );
    }

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

export const getOptionItemsAsync = createAsyncThunk<
  GetOptionItemsResponse,
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/getOptionItemsAsync", 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 accountsApi = new AccountsApi(config);
  // stateからselectedIdを取得する
  const { selectedId } = state.worktype.apps;

  try {
    const optionItems = (
      await accountsApi.getOptionItems(selectedId, {
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;

    return optionItems;
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);

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

    // WorktypeIDが存在しない場合
    if (error.code === "E011003") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.worktypeIdAlreadyDeletedError"
      );
    }

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

export const editOptionItemsAsync = createAsyncThunk<
  {
    // return empty
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/editOptionItemsAsync", 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 accountsApi = new AccountsApi(config);
  // stateからselectedId,optionItemsを取得する
  const { selectedId, optionItems } = state.worktype.apps;

  if (!optionItems) {
    throw new Error("optionItems is undefined");
  }

  try {
    await accountsApi.updateOptionItems(
      selectedId,
      {
        optionItems,
      },
      {
        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");

    // OptionItemの保存に失敗した場合
    if (error.code === "E011003") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.optionItemSaveFailedError"
      );
    }

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

export const updateActiveWorktypeAsync = createAsyncThunk<
  {
    // return empty
  },
  { id?: number | undefined },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/updateActiveWorktypeAsync", 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 accountsApi = new AccountsApi(config);
  const { id } = args;

  try {
    await accountsApi.activeWorktype(
      { id },
      {
        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");

    // ActiveWorktypeの保存に失敗した場合
    if (error.code === "E011003") {
      errorMessage = getTranslationID(
        "worktypeIdSetting.message.updateActiveWorktypeFailedError"
      );
    }

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

export const deleteWorktypeAsync = createAsyncThunk<
  {
    /* Empty Object */
  },
  { worktypeId: number },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/deleteWorktypeAsync", async (args, thunkApi) => {
  const { worktypeId } = 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 accountsApi = new AccountsApi(config);

  try {
    await accountsApi.deleteWorktype(worktypeId, {
      headers: { authorization: `Bearer ${accessToken}` },
    });

    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);

    if (error.statusCode === 400) {
      if (error.code === "E011003") {
        // ワークタイプが削除済みの場合は成功扱いとする
        thunkApi.dispatch(
          openSnackbar({
            level: "info",
            message: getTranslationID("common.message.success"),
          })
        );
        return {};
      }

      if (error.code === "E011004") {
        // ワークタイプがワークフローで使用中の場合は削除できない
        thunkApi.dispatch(
          openSnackbar({
            level: "error",
            message: getTranslationID(
              "worktypeIdSetting.message.worktypeInUseError"
            ),
          })
        );
        return {};
      }
    }

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