import { selectAccount } from 'pl-fe/selectors';
import toast from 'pl-fe/toast';
import { isLoggedIn } from 'pl-fe/utils/auth';

import { getClient } from '../api';

import { importFetchedAccounts } from './importer';

import type { Account, List, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';

const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST' as const;
const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS' as const;
const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL' as const;

const LISTS_FETCH_REQUEST = 'LISTS_FETCH_REQUEST' as const;
const LISTS_FETCH_SUCCESS = 'LISTS_FETCH_SUCCESS' as const;
const LISTS_FETCH_FAIL = 'LISTS_FETCH_FAIL' as const;

const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE' as const;
const LIST_EDITOR_RESET = 'LIST_EDITOR_RESET' as const;
const LIST_EDITOR_SETUP = 'LIST_EDITOR_SETUP' as const;

const LIST_CREATE_REQUEST = 'LIST_CREATE_REQUEST' as const;
const LIST_CREATE_SUCCESS = 'LIST_CREATE_SUCCESS' as const;
const LIST_CREATE_FAIL = 'LIST_CREATE_FAIL' as const;

const LIST_UPDATE_REQUEST = 'LIST_UPDATE_REQUEST' as const;
const LIST_UPDATE_SUCCESS = 'LIST_UPDATE_SUCCESS' as const;
const LIST_UPDATE_FAIL = 'LIST_UPDATE_FAIL' as const;

const LIST_DELETE_REQUEST = 'LIST_DELETE_REQUEST' as const;
const LIST_DELETE_SUCCESS = 'LIST_DELETE_SUCCESS' as const;
const LIST_DELETE_FAIL = 'LIST_DELETE_FAIL' as const;

const LIST_ACCOUNTS_FETCH_REQUEST = 'LIST_ACCOUNTS_FETCH_REQUEST' as const;
const LIST_ACCOUNTS_FETCH_SUCCESS = 'LIST_ACCOUNTS_FETCH_SUCCESS' as const;
const LIST_ACCOUNTS_FETCH_FAIL = 'LIST_ACCOUNTS_FETCH_FAIL' as const;

const LIST_EDITOR_SUGGESTIONS_CHANGE = 'LIST_EDITOR_SUGGESTIONS_CHANGE' as const;
const LIST_EDITOR_SUGGESTIONS_READY = 'LIST_EDITOR_SUGGESTIONS_READY' as const;
const LIST_EDITOR_SUGGESTIONS_CLEAR = 'LIST_EDITOR_SUGGESTIONS_CLEAR' as const;

const LIST_EDITOR_ADD_REQUEST = 'LIST_EDITOR_ADD_REQUEST' as const;
const LIST_EDITOR_ADD_SUCCESS = 'LIST_EDITOR_ADD_SUCCESS' as const;
const LIST_EDITOR_ADD_FAIL = 'LIST_EDITOR_ADD_FAIL' as const;

const LIST_EDITOR_REMOVE_REQUEST = 'LIST_EDITOR_REMOVE_REQUEST' as const;
const LIST_EDITOR_REMOVE_SUCCESS = 'LIST_EDITOR_REMOVE_SUCCESS' as const;
const LIST_EDITOR_REMOVE_FAIL = 'LIST_EDITOR_REMOVE_FAIL' as const;

const LIST_ADDER_RESET = 'LIST_ADDER_RESET' as const;
const LIST_ADDER_SETUP = 'LIST_ADDER_SETUP' as const;

const LIST_ADDER_LISTS_FETCH_REQUEST = 'LIST_ADDER_LISTS_FETCH_REQUEST' as const;
const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS' as const;
const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL' as const;

const fetchList = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  if (getState().lists.get(listId)) {
    return;
  }

  dispatch(fetchListRequest(listId));

  return getClient(getState()).lists.getList(listId)
    .then((data) => dispatch(fetchListSuccess(data)))
    .catch(err => dispatch(fetchListFail(listId, err)));
};

const fetchListRequest = (listId: string) => ({
  type: LIST_FETCH_REQUEST,
  listId,
});

const fetchListSuccess = (list: List) => ({
  type: LIST_FETCH_SUCCESS,
  list,
});

const fetchListFail = (listId: string, error: unknown) => ({
  type: LIST_FETCH_FAIL,
  listId,
  error,
});

const fetchLists = () => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(fetchListsRequest());

  return getClient(getState()).lists.getLists()
    .then((data) => dispatch(fetchListsSuccess(data)))
    .catch(err => dispatch(fetchListsFail(err)));
};

const fetchListsRequest = () => ({
  type: LISTS_FETCH_REQUEST,
});

const fetchListsSuccess = (lists: Array<List>) => ({
  type: LISTS_FETCH_SUCCESS,
  lists,
});

const fetchListsFail = (error: unknown) => ({
  type: LISTS_FETCH_FAIL,
  error,
});

const submitListEditor = (shouldReset?: boolean) => (dispatch: AppDispatch, getState: () => RootState) => {
  const listId = getState().listEditor.listId!;
  const title = getState().listEditor.title;

  if (listId === null) {
    dispatch(createList(title, shouldReset));
  } else {
    dispatch(updateList(listId, title, shouldReset));
  }
};

const setupListEditor = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  dispatch({
    type: LIST_EDITOR_SETUP,
    list: getState().lists.get(String(listId)),
  });

  dispatch(fetchListAccounts(listId));
};

const changeListEditorTitle = (value: string) => ({
  type: LIST_EDITOR_TITLE_CHANGE,
  value,
});

const createList = (title: string, shouldReset?: boolean) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(createListRequest());

  return getClient(getState()).lists.createList({ title }).then((data) => {
    dispatch(createListSuccess(data));

    if (shouldReset) {
      dispatch(resetListEditor());
    }
  }).catch(err => dispatch(createListFail(err)));
};

const createListRequest = () => ({
  type: LIST_CREATE_REQUEST,
});

const createListSuccess = (list: List) => ({
  type: LIST_CREATE_SUCCESS,
  list,
});

const createListFail = (error: unknown) => ({
  type: LIST_CREATE_FAIL,
  error,
});

const updateList = (listId: string, title: string, shouldReset?: boolean) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(updateListRequest(listId));

  return getClient(getState()).lists.updateList(listId, { title }).then((data) => {
    dispatch(updateListSuccess(data));

    if (shouldReset) {
      dispatch(resetListEditor());
    }
  }).catch(err => dispatch(updateListFail(listId, err)));
};

const updateListRequest = (listId: string) => ({
  type: LIST_UPDATE_REQUEST,
  listId,
});

const updateListSuccess = (list: List) => ({
  type: LIST_UPDATE_SUCCESS,
  list,
});

const updateListFail = (listId: string, error: unknown) => ({
  type: LIST_UPDATE_FAIL,
  listId,
  error,
});

const resetListEditor = () => ({
  type: LIST_EDITOR_RESET,
});

const deleteList = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(deleteListRequest(listId));

  return getClient(getState()).lists.deleteList(listId)
    .then(() => dispatch(deleteListSuccess(listId)))
    .catch(err => dispatch(deleteListFail(listId, err)));
};

const deleteListRequest = (listId: string) => ({
  type: LIST_DELETE_REQUEST,
  listId,
});

const deleteListSuccess = (listId: string) => ({
  type: LIST_DELETE_SUCCESS,
  listId,
});

const deleteListFail = (listId: string, error: unknown) => ({
  type: LIST_DELETE_FAIL,
  listId,
  error,
});

const fetchListAccounts = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(fetchListAccountsRequest(listId));

  return getClient(getState()).lists.getListAccounts(listId).then(({ items, next }) => {
    dispatch(importFetchedAccounts(items));
    dispatch(fetchListAccountsSuccess(listId, items, next));
  }).catch(err => dispatch(fetchListAccountsFail(listId, err)));
};

const fetchListAccountsRequest = (listId: string) => ({
  type: LIST_ACCOUNTS_FETCH_REQUEST,
  listId,
});

const fetchListAccountsSuccess = (listId: string, accounts: Account[], next: (() => Promise<PaginatedResponse<Account>>) | null) => ({
  type: LIST_ACCOUNTS_FETCH_SUCCESS,
  listId,
  accounts,
  next,
});

const fetchListAccountsFail = (listId: string, error: unknown) => ({
  type: LIST_ACCOUNTS_FETCH_FAIL,
  listId,
  error,
});

const fetchListSuggestions = (q: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  return getClient(getState()).accounts.searchAccounts(q, { resolve: false, limit: 4, following: true }).then((data) => {
    dispatch(importFetchedAccounts(data));
    dispatch(fetchListSuggestionsReady(q, data));
  }).catch(error => toast.showAlertForError(error));
};

const fetchListSuggestionsReady = (query: string, accounts: Array<Account>) => ({
  type: LIST_EDITOR_SUGGESTIONS_READY,
  query,
  accounts,
});

const clearListSuggestions = () => ({
  type: LIST_EDITOR_SUGGESTIONS_CLEAR,
});

const changeListSuggestions = (value: string) => ({
  type: LIST_EDITOR_SUGGESTIONS_CHANGE,
  value,
});

const addToListEditor = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  dispatch(addToList(getState().listEditor.listId!, accountId));
};

const addToList = (listId: string, accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(addToListRequest(listId, accountId));

  return getClient(getState()).lists.addListAccounts(listId, [accountId])
    .then(() => dispatch(addToListSuccess(listId, accountId)))
    .catch(err => dispatch(addToListFail(listId, accountId, err)));
};

const addToListRequest = (listId: string, accountId: string) => ({
  type: LIST_EDITOR_ADD_REQUEST,
  listId,
  accountId,
});

const addToListSuccess = (listId: string, accountId: string) => ({
  type: LIST_EDITOR_ADD_SUCCESS,
  listId,
  accountId,
});

const addToListFail = (listId: string, accountId: string, error: any) => ({
  type: LIST_EDITOR_ADD_FAIL,
  listId,
  accountId,
  error,
});

const removeFromListEditor = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  dispatch(removeFromList(getState().listEditor.listId!, accountId));
};

const removeFromList = (listId: string, accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(removeFromListRequest(listId, accountId));

  return getClient(getState()).lists.deleteListAccounts(listId, [accountId])
    .then(() => dispatch(removeFromListSuccess(listId, accountId)))
    .catch(err => dispatch(removeFromListFail(listId, accountId, err)));
};

const removeFromListRequest = (listId: string, accountId: string) => ({
  type: LIST_EDITOR_REMOVE_REQUEST,
  listId,
  accountId,
});

const removeFromListSuccess = (listId: string, accountId: string) => ({
  type: LIST_EDITOR_REMOVE_SUCCESS,
  listId,
  accountId,
});

const removeFromListFail = (listId: string, accountId: string, error: unknown) => ({
  type: LIST_EDITOR_REMOVE_FAIL,
  listId,
  accountId,
  error,
});

const resetListAdder = () => ({
  type: LIST_ADDER_RESET,
});

const setupListAdder = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  dispatch({
    type: LIST_ADDER_SETUP,
    account: selectAccount(getState(), accountId),
  });
  dispatch(fetchLists());
  dispatch(fetchAccountLists(accountId));
};

const fetchAccountLists = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  if (!isLoggedIn(getState)) return;

  dispatch(fetchAccountListsRequest(accountId));

  return getClient(getState()).accounts.getAccountLists(accountId)
    .then((data) => dispatch(fetchAccountListsSuccess(accountId, data)))
    .catch(err => dispatch(fetchAccountListsFail(accountId, err)));
};

const fetchAccountListsRequest = (listId: string) => ({
  type: LIST_ADDER_LISTS_FETCH_REQUEST,
  listId,
});

const fetchAccountListsSuccess = (listId: string, lists: Array<List>) => ({
  type: LIST_ADDER_LISTS_FETCH_SUCCESS,
  listId,
  lists,
});

const fetchAccountListsFail = (listId: string, err: unknown) => ({
  type: LIST_ADDER_LISTS_FETCH_FAIL,
  listId,
  err,
});

const addToListAdder = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  dispatch(addToList(listId, getState().listAdder.accountId!));
};

const removeFromListAdder = (listId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  dispatch(removeFromList(listId, getState().listAdder.accountId!));
};

export {
  LIST_FETCH_REQUEST,
  LIST_FETCH_SUCCESS,
  LIST_FETCH_FAIL,
  LISTS_FETCH_REQUEST,
  LISTS_FETCH_SUCCESS,
  LISTS_FETCH_FAIL,
  LIST_EDITOR_TITLE_CHANGE,
  LIST_EDITOR_RESET,
  LIST_EDITOR_SETUP,
  LIST_CREATE_REQUEST,
  LIST_CREATE_SUCCESS,
  LIST_CREATE_FAIL,
  LIST_UPDATE_REQUEST,
  LIST_UPDATE_SUCCESS,
  LIST_UPDATE_FAIL,
  LIST_DELETE_REQUEST,
  LIST_DELETE_SUCCESS,
  LIST_DELETE_FAIL,
  LIST_ACCOUNTS_FETCH_REQUEST,
  LIST_ACCOUNTS_FETCH_SUCCESS,
  LIST_ACCOUNTS_FETCH_FAIL,
  LIST_EDITOR_SUGGESTIONS_CHANGE,
  LIST_EDITOR_SUGGESTIONS_READY,
  LIST_EDITOR_SUGGESTIONS_CLEAR,
  LIST_EDITOR_ADD_REQUEST,
  LIST_EDITOR_ADD_SUCCESS,
  LIST_EDITOR_ADD_FAIL,
  LIST_EDITOR_REMOVE_REQUEST,
  LIST_EDITOR_REMOVE_SUCCESS,
  LIST_EDITOR_REMOVE_FAIL,
  LIST_ADDER_RESET,
  LIST_ADDER_SETUP,
  LIST_ADDER_LISTS_FETCH_REQUEST,
  LIST_ADDER_LISTS_FETCH_SUCCESS,
  LIST_ADDER_LISTS_FETCH_FAIL,
  fetchList,
  fetchListRequest,
  fetchListSuccess,
  fetchListFail,
  fetchLists,
  fetchListsRequest,
  fetchListsSuccess,
  fetchListsFail,
  submitListEditor,
  setupListEditor,
  changeListEditorTitle,
  createList,
  createListRequest,
  createListSuccess,
  createListFail,
  updateList,
  updateListRequest,
  updateListSuccess,
  updateListFail,
  resetListEditor,
  deleteList,
  deleteListRequest,
  deleteListSuccess,
  deleteListFail,
  fetchListAccounts,
  fetchListAccountsRequest,
  fetchListAccountsSuccess,
  fetchListAccountsFail,
  fetchListSuggestions,
  fetchListSuggestionsReady,
  clearListSuggestions,
  changeListSuggestions,
  addToListEditor,
  addToList,
  addToListRequest,
  addToListSuccess,
  addToListFail,
  removeFromListEditor,
  removeFromList,
  removeFromListRequest,
  removeFromListSuccess,
  removeFromListFail,
  resetListAdder,
  setupListAdder,
  fetchAccountLists,
  fetchAccountListsRequest,
  fetchAccountListsSuccess,
  fetchAccountListsFail,
  addToListAdder,
  removeFromListAdder,
};
