import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import {
  TasksResponse,
  TasksApi,
  UsersApi,
  AccountsApi,
  GetTypistsResponse,
  GetTypistGroupsResponse,
  Assignee,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { ErrorObject, createErrorObject } from "../../common/errors";
import {
  DIRECTION,
  DirectionType,
  SORTABLE_COLUMN,
  SortableColumnType,
} from "./constants";

export const listTasksAsync = createAsyncThunk<
  TasksResponse,
  {
    // パラメータ
    limit: number;
    offset: number;
    filter?: string;
    direction: DirectionType;
    paramName: SortableColumnType;
  },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("dictations/listTasksAsync", async (args, thunkApi) => {
  const { limit, offset, filter, direction, paramName } = args;

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

  try {
    const res = await tasksApi.getTasks(
      limit,
      offset,
      filter,
      direction,
      paramName,
      {
        headers: { authorization: `Bearer ${accessToken}` },
      }
    );

    return res.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 getSortColumnAsync = createAsyncThunk<
  {
    direction: DirectionType;
    paramName: SortableColumnType;
  },
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("dictations/getSortColumnAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration, accessToken } = state.auth;
  const config = new Configuration(configuration);
  const usersApi = new UsersApi(config);

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

    const { direction, paramName } = sort.data;

    if (
      Object.values<string>(DIRECTION).includes(direction) &&
      Object.values<string>(SORTABLE_COLUMN).includes(paramName)
    ) {
      return {
        direction: direction as DirectionType,
        paramName: paramName as SortableColumnType,
      };
    }
    throw new Error(
      `invalid param. direction=${direction}, paramName=${paramName}`
    );
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);
    thunkApi.dispatch(
      openSnackbar({
        level: "error",
        message: getTranslationID("common.message.internalServerError"),
      })
    );
    return thunkApi.rejectWithValue({ error });
  }
});

export const listTypistsAsync = createAsyncThunk<
  GetTypistsResponse,
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("dictations/listTypistsAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration, accessToken } = state.auth;
  const config = new Configuration(configuration);
  const accountsApi = new AccountsApi(config);

  try {
    const typists = await accountsApi.getTypists({
      headers: { authorization: `Bearer ${accessToken}` },
    });

    return typists.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 listTypistGroupsAsync = createAsyncThunk<
  GetTypistGroupsResponse,
  void,
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("dictations/listTypistGroupsAsync", async (args, thunkApi) => {
  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration, accessToken } = state.auth;
  const config = new Configuration(configuration);
  const accountsApi = new AccountsApi(config);

  try {
    const typistGroup = await accountsApi.getTypistGroups({
      headers: { authorization: `Bearer ${accessToken}` },
    });

    return typistGroup.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 updateAssigneeAsync = createAsyncThunk<
  {
    /** empty */
  },
  {
    audioFileId: number;
    assignees: Assignee[];
  },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("dictations/updateAssigneeAsync", async (args, thunkApi) => {
  const { audioFileId, assignees } = args;

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

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

    // ステータスがUploaded以外、タスクが存在しない場合
    if (error.code === "E010601") {
      thunkApi.dispatch(
        openSnackbar({
          level: "error",
          message: getTranslationID("dictationPage.message.taskNotEditable"),
        })
      );
      return thunkApi.rejectWithValue({ error });
    }

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

export const playbackAsync = createAsyncThunk<
  {
    /** empty */
  },
  {
    direction: DirectionType;
    paramName: SortableColumnType;
    audioFileId: number;
    isTypist: boolean;
  },
  {
    // rejectした時の返却値の型
    rejectValue: {
      error: ErrorObject;
    };
  }
>("dictations/playbackAsync", async (args, thunkApi) => {
  const { audioFileId, direction, paramName, isTypist } = args;

  // apiのConfigurationを取得する
  const { getState } = thunkApi;
  const state = getState() as RootState;
  const { configuration, accessToken } = state.auth;
  const config = new Configuration(configuration);
  const tasksApi = new TasksApi(config);
  const usersApi = new UsersApi(config);
  try {
    // ユーザーがタイピストである場合に、ソート条件を保存する
    if (isTypist) {
      await usersApi.updateSortCriteria(
        { direction, paramName },
        {
          headers: { authorization: `Bearer ${accessToken}` },
        }
      );
    }
    await tasksApi.checkout(audioFileId, {
      headers: { authorization: `Bearer ${accessToken}` },
    });
    thunkApi.dispatch(
      openSnackbar({
        level: "info",
        message: getTranslationID("common.message.success"),
      })
    );
    return {};
  } catch (e) {
    // e ⇒ errorObjectに変換"
    const error = createErrorObject(e);

    // ステータスが[Uploaded,Inprogress,Pending]以外、またはタスクが存在しない場合
    if (error.code === "E010601") {
      thunkApi.dispatch(
        openSnackbar({
          level: "error",
          message: getTranslationID(
            "dictationPage.message.taskToPlaybackNoExists"
          ),
        })
      );
      return thunkApi.rejectWithValue({ error });
    }
    // タスクをチェックアウトする権限がない
    if (error.code === "E010602") {
      thunkApi.dispatch(
        openSnackbar({
          level: "error",
          message: getTranslationID(
            "dictationPage.message.noPlaybackAuthorization"
          ),
        })
      );
      return thunkApi.rejectWithValue({ error });
    }

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