import { createAsyncThunk } from "@reduxjs/toolkit";
import {
  AccountsApi,
  Author,
  Configuration,
  GetWorkflowsResponse,
  TemplateFile,
  TemplatesApi,
  Typist,
  TypistGroup,
  WorkflowTypist,
  WorkflowsApi,
  Worktype,
} from "api";
import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";

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

  try {
    const { data } = await workflowsApi.getWorkflows({
      headers: { authorization: `Bearer ${accessToken}` },
    });

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

export const createWorkflowAsync = createAsyncThunk<
  {
    /* Empty Object */
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/createWorkflowAsync", 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 workflowsApi = new WorkflowsApi(config);
  const { selectedAssignees, authorId, templateId, worktypeId } =
    state.workflow.apps;

  try {
    if (authorId === undefined) {
      throw new Error("authorId is not found");
    }
    // 選択されたタイピストを取得し、リクエスト用の型に変換する
    const typists = selectedAssignees.map(
      (item): WorkflowTypist => ({
        typistId: item.typistUserId,
        typistGroupId: item.typistGroupId,
      })
    );
    await workflowsApi.createWorkflows(
      {
        authorId,
        typists,
        templateId,
        worktypeId,
      },
      {
        headers: { authorization: `Bearer ${accessToken}` },
      }
    );
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);
    const { code, statusCode } = error;
    // AuthorIDとWorktypeIDが一致するものが既に存在する場合
    if (code === "E013001") {
      thunkApi.dispatch(
        openSnackbar({
          level: "error",
          message: getTranslationID(
            "workflowPage.message.workflowConflictError"
          ),
        })
      );
      return thunkApi.rejectWithValue({ error });
    }
    // パラメータが存在しない場合
    if (statusCode === 400) {
      thunkApi.dispatch(
        openSnackbar({
          level: "error",
          message: getTranslationID("workflowPage.message.saveFailedError"),
        })
      );
      return thunkApi.rejectWithValue({ error });
    }
    // その他のエラー
    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: getTranslationID("common.message.internalServerError"),
      })
    );
    return thunkApi.rejectWithValue({ error });
  }
});

export const updateWorkflowAsync = createAsyncThunk<
  {
    /* Empty Object */
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/updateWorkflowAsync", 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 workflowsApi = new WorkflowsApi(config);
  const {
    selectedWorkflow,
    selectedAssignees,
    authorId,
    templateId,
    worktypeId,
  } = state.workflow.apps;

  try {
    if (selectedWorkflow === undefined) {
      throw new Error("selectedWorkflow is not found");
    }

    if (authorId === undefined) {
      throw new Error("authorId is not found");
    }
    // 選択されたタイピストを取得し、リクエスト用の型に変換する
    const typists = selectedAssignees.map(
      (item): WorkflowTypist => ({
        typistId: item.typistUserId,
        typistGroupId: item.typistGroupId,
      })
    );
    await workflowsApi.updateWorkflow(
      selectedWorkflow.id,
      {
        authorId,
        typists,
        templateId,
        worktypeId,
      },
      {
        headers: { authorization: `Bearer ${accessToken}` },
      }
    );
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);
    const { code, statusCode } = error;

    if (statusCode === 400) {
      // AuthorIDとWorktypeIDが一致するものが既に存在する場合
      if (code === "E013001") {
        thunkApi.dispatch(
          openSnackbar({
            level: "error",
            message: getTranslationID(
              "workflowPage.message.workflowConflictError"
            ),
          })
        );
        return thunkApi.rejectWithValue({ error });
      }

      // パラメータが存在しない場合
      thunkApi.dispatch(
        openSnackbar({
          level: "error",
          message: getTranslationID("workflowPage.message.saveFailedError"),
        })
      );
      return thunkApi.rejectWithValue({ error });
    }
    // その他のエラー
    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: getTranslationID("common.message.internalServerError"),
      })
    );
    return thunkApi.rejectWithValue({ error });
  }
});

export const getworkflowRelationsAsync = createAsyncThunk<
  {
    authors: Author[];
    typists: Typist[];
    typistGroups: TypistGroup[];
    templates: TemplateFile[];
    worktypes: Worktype[];
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/getworkflowRelationsAsync", 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 templatesApi = new TemplatesApi(config);

  try {
    const { authors } = (
      await accountsApi.getAuthors({
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;
    const { typists } = (
      await accountsApi.getTypists({
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;
    const { typistGroups } = (
      await accountsApi.getTypistGroups({
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;
    const { templates } = (
      await templatesApi.getTemplates({
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;
    const { worktypes } = (
      await accountsApi.getWorktypes({
        headers: { authorization: `Bearer ${accessToken}` },
      })
    ).data;

    return {
      authors,
      typists,
      typistGroups,
      templates,
      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 deleteWorkflowAsync = createAsyncThunk<
  {
    /* Empty Object */
  },
  { workflowId: number },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("workflow/deleteWorkflowAsync", async (args, thunkApi) => {
  const { workflowId } = 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 workflowsApi = new WorkflowsApi(config);

  try {
    await workflowsApi.deleteWorkflow(workflowId, {
      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.code === "E013002") {
      thunkApi.dispatch(
        openSnackbar({
          level: "info",
          message: getTranslationID("common.message.success"),
        })
      );
      return {};
    }

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