import { queryClient } from '@/queries/client';
import { scheduledStatusesQueryOptions } from '@/queries/statuses/scheduled-statuses';
import { useContextStore } from '@/stores/contexts';
import { useModalsStore } from '@/stores/modals';
import { usePendingStatusesStore } from '@/stores/pending-statuses';
import { useSettingsStore } from '@/stores/settings';
import { isLoggedIn } from '@/utils/auth';
import { shouldHaveCard } from '@/utils/status';

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

import { setComposeToStatus } from './compose';
import { importEntities } from './importer';
import { deleteFromTimelines } from './timelines';

import type { Status } from '@/normalizers/status';
import type { AppDispatch, RootState } from '@/store';
import type {
  CreateStatusParams,
  Status as BaseStatus,
  ScheduledStatus,
  StatusSource,
  Poll,
} from 'pl-api';
import type { IntlShape } from 'react-intl';

const STATUS_CREATE_REQUEST = 'STATUS_CREATE_REQUEST' as const;
const STATUS_CREATE_SUCCESS = 'STATUS_CREATE_SUCCESS' as const;
const STATUS_CREATE_FAIL = 'STATUS_CREATE_FAIL' as const;

const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST' as const;
const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS' as const;
const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL' as const;

const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST' as const;
const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS' as const;
const STATUS_DELETE_FAIL = 'STATUS_DELETE_FAIL' as const;

const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS' as const;

const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS' as const;

const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS' as const;

const STATUS_UNFILTER = 'STATUS_UNFILTER' as const;

const createStatus =
  (
    params: CreateStatusParams,
    idempotencyKey: string,
    editedId: string | null,
    redacting = false,
  ) =>
  (dispatch: AppDispatch, getState: () => RootState) => {
    if (!params.preview) {
      usePendingStatusesStore.getState().actions.importStatus(params, idempotencyKey);
      useContextStore.getState().actions.importPendingStatus(params.in_reply_to_id, idempotencyKey);
      dispatch<StatusesAction>({
        type: STATUS_CREATE_REQUEST,
        params,
        idempotencyKey,
        editing: !!editedId,
        redacting,
      });
    }

    const client = getClient(getState());

    return (
      editedId === null
        ? client.statuses.createStatus(params)
        : redacting
          ? client.admin.statuses.redactStatus(editedId, params)
          : client.statuses.editStatus(editedId, params)
    )
      .then((status) => {
        if (params.preview) return status;

        // The backend might still be processing the rich media attachment
        const expectsCard = status.scheduled_at === null && !status.card && shouldHaveCard(status);

        if (status.scheduled_at === null) {
          dispatch(
            importEntities(
              { statuses: [{ ...status, expectsCard }] },
              { idempotencyKey, withParents: true },
            ),
          );
        } else {
          queryClient.invalidateQueries(scheduledStatusesQueryOptions);
        }

        dispatch<StatusesAction>({
          type: STATUS_CREATE_SUCCESS,
          status,
          params,
          idempotencyKey,
          editing: !!editedId,
        });

        useContextStore
          .getState()
          .actions.deletePendingStatus(
            'in_reply_to_id' in status ? status.in_reply_to_id : null,
            idempotencyKey,
          );

        // Poll the backend for the updated card
        if (expectsCard) {
          const delay = 1000;

          const poll = (retries = 5) => {
            return getClient(getState())
              .statuses.getStatus(status.id)
              .then((response) => {
                if (response.card) {
                  dispatch(importEntities({ statuses: [response] }));
                } else if (retries > 0 && response) {
                  setTimeout(() => poll(retries - 1), delay);
                }
              })
              .catch(console.error);
          };

          setTimeout(() => poll(), delay);
        }

        return status;
      })
      .catch((error) => {
        usePendingStatusesStore.getState().actions.deleteStatus(idempotencyKey);
        useContextStore
          .getState()
          .actions.deletePendingStatus(params.in_reply_to_id, idempotencyKey);
        dispatch<StatusesAction>({
          type: STATUS_CREATE_FAIL,
          error,
          params,
          idempotencyKey,
          editing: !!editedId,
        });
        throw error;
      });
  };

const editStatus = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => {
  const state = getState();

  const status = state.statuses[statusId];
  const poll = status.poll_id
    ? queryClient.getQueryData<Poll>(['statuses', 'polls', status.poll_id])
    : undefined;

  dispatch<StatusesAction>({ type: STATUS_FETCH_SOURCE_REQUEST });

  return getClient(state)
    .statuses.getStatusSource(statusId)
    .then((response) => {
      dispatch<StatusesAction>({ type: STATUS_FETCH_SOURCE_SUCCESS });
      dispatch(
        setComposeToStatus(
          status,
          poll,
          response.text,
          response.spoiler_text,
          response.content_type,
          false,
        ),
      );
      useModalsStore.getState().actions.openModal('COMPOSE');
    })
    .catch((error) => {
      dispatch<StatusesAction>({ type: STATUS_FETCH_SOURCE_FAIL, error });
    });
};

const fetchStatus =
  (statusId: string, intl?: IntlShape) => (dispatch: AppDispatch, getState: () => RootState) => {
    const params =
      intl && useSettingsStore.getState().settings.autoTranslate
        ? {
            language: intl.locale,
          }
        : undefined;

    return getClient(getState())
      .statuses.getStatus(statusId, params)
      .then((status) => {
        dispatch(importEntities({ statuses: [status] }));
        return status;
      });
  };

const deleteStatus =
  (statusId: string, groupId?: string, withRedraft = false) =>
  (dispatch: AppDispatch, getState: () => RootState) => {
    if (!isLoggedIn(getState)) return null;

    const state = getState();

    const status = state.statuses[statusId];
    const poll = status.poll_id
      ? queryClient.getQueryData<Poll>(['statuses', 'polls', status.poll_id])
      : undefined;

    dispatch<StatusesAction>({ type: STATUS_DELETE_REQUEST, params: status });

    return (
      groupId
        ? getClient(state).experimental.groups.deleteGroupStatus(statusId, groupId)
        : getClient(state).statuses.deleteStatus(statusId)
    )
      .then((response) => {
        usePendingStatusesStore.getState().actions.deleteStatus(statusId);
        dispatch<StatusesAction>({ type: STATUS_DELETE_SUCCESS, statusId });
        dispatch(deleteFromTimelines(statusId));

        if (withRedraft) {
          dispatch(
            setComposeToStatus(
              status,
              poll,
              response.text ?? '',
              response.spoiler_text,
              (response as StatusSource).content_type,
              withRedraft,
            ),
          );
          useModalsStore.getState().actions.openModal('COMPOSE');
        }
      })
      .catch((error) => {
        dispatch<StatusesAction>({ type: STATUS_DELETE_FAIL, params: status, error });
      });
  };

const updateStatus = (status: BaseStatus) => (dispatch: AppDispatch) => {
  dispatch(importEntities({ statuses: [status] }));
};

const fetchContext =
  (statusId: string, intl?: IntlShape) => (dispatch: AppDispatch, getState: () => RootState) => {
    const params =
      intl && useSettingsStore.getState().settings.autoTranslate
        ? {
            language: intl.locale,
          }
        : undefined;

    return getClient(getState())
      .statuses.getContext(statusId, params)
      .then((context) => {
        const { ancestors, descendants } = context;
        const statuses = ancestors.concat(descendants);
        dispatch(importEntities({ statuses }));
        useContextStore.getState().actions.importContext(statusId, context);
        dispatch<StatusesAction>({ type: CONTEXT_FETCH_SUCCESS, statusId, ancestors, descendants });
        return context;
      })
      .catch((error) => {
        if (error.response?.status === 404) {
          dispatch(deleteFromTimelines(statusId));
        }
      });
  };

const fetchStatusWithContext = (statusId: string, intl?: IntlShape) => (dispatch: AppDispatch) =>
  Promise.all([dispatch(fetchContext(statusId, intl)), dispatch(fetchStatus(statusId, intl))]);

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

  return getClient(getState())
    .statuses.muteStatus(statusId)
    .then((status) => {
      dispatch<StatusesAction>({ type: STATUS_MUTE_SUCCESS, statusId });
    });
};

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

  return getClient(getState())
    .statuses.unmuteStatus(statusId)
    .then(() => {
      dispatch<StatusesAction>({ type: STATUS_UNMUTE_SUCCESS, statusId });
    });
};

const toggleMuteStatus = (status: Pick<Status, 'id' | 'muted'>) =>
  status.muted ? unmuteStatus(status.id) : muteStatus(status.id);

// let TRANSLATIONS_QUEUE: Set<string> = new Set();
// let TRANSLATIONS_TIMEOUT: NodeJS.Timeout | null = null;

// const translateStatus = (statusId: string, targetLanguage: string, lazy?: boolean) =>
//   (dispatch: AppDispatch, getState: () => RootState) => {
//     const client = getClient(getState);
//     const features = client.features;

//     const handleTranslateMany = () => {
//       const copy = [...TRANSLATIONS_QUEUE];
//       TRANSLATIONS_QUEUE = new Set();
//       if (TRANSLATIONS_TIMEOUT) clearTimeout(TRANSLATIONS_TIMEOUT);

//       return client.statuses.translateStatuses(copy, targetLanguage).then((response) => {
//         response.forEach((translation) => {
//           dispatch<StatusesAction>({
//             type: STATUS_TRANSLATE_SUCCESS,
//             statusId: translation.id,
//             translation: translation,
//           });

//           copy
//             .filter((statusId) => !response.some(({ id }) => id === statusId))
//             .forEach((statusId) => dispatch<StatusesAction>({
//               type: STATUS_TRANSLATE_FAIL,
//               statusId,
//             }));
//         });
//       }).catch(error => {
//         dispatch<StatusesAction>({
//           type: STATUS_TRANSLATE_FAIL,
//           statusId,
//           error,
//         });
//       });
//     };

//     if (features.lazyTranslations && lazy) {
//       TRANSLATIONS_QUEUE.add(statusId);

//       if (TRANSLATIONS_TIMEOUT) clearTimeout(TRANSLATIONS_TIMEOUT);
//       TRANSLATIONS_TIMEOUT = setTimeout(() => handleTranslateMany(), 3000);
//     } else if (features.lazyTranslations && TRANSLATIONS_QUEUE.size) {
//       TRANSLATIONS_QUEUE.add(statusId);

//       handleTranslateMany();
//     }
//   };

const unfilterStatus = (statusId: string) => ({
  type: STATUS_UNFILTER,
  statusId,
});

type StatusesAction =
  | {
      type: typeof STATUS_CREATE_REQUEST;
      params: CreateStatusParams;
      idempotencyKey: string;
      editing: boolean;
      redacting: boolean;
    }
  | {
      type: typeof STATUS_CREATE_SUCCESS;
      status: BaseStatus | ScheduledStatus;
      params: CreateStatusParams;
      idempotencyKey: string;
      editing: boolean;
    }
  | {
      type: typeof STATUS_CREATE_FAIL;
      error: unknown;
      params: CreateStatusParams;
      idempotencyKey: string;
      editing: boolean;
    }
  | { type: typeof STATUS_FETCH_SOURCE_REQUEST }
  | { type: typeof STATUS_FETCH_SOURCE_SUCCESS }
  | { type: typeof STATUS_FETCH_SOURCE_FAIL; error: unknown }
  | { type: typeof STATUS_DELETE_REQUEST; params: Pick<Status, 'in_reply_to_id' | 'quote_id'> }
  | { type: typeof STATUS_DELETE_SUCCESS; statusId: string }
  | {
      type: typeof STATUS_DELETE_FAIL;
      params: Pick<Status, 'in_reply_to_id' | 'quote_id'>;
      error: unknown;
    }
  | {
      type: typeof CONTEXT_FETCH_SUCCESS;
      statusId: string;
      ancestors: Array<BaseStatus>;
      descendants: Array<BaseStatus>;
    }
  | { type: typeof STATUS_MUTE_SUCCESS; statusId: string }
  | { type: typeof STATUS_UNMUTE_SUCCESS; statusId: string }
  | ReturnType<typeof unfilterStatus>;

export {
  STATUS_CREATE_REQUEST,
  STATUS_CREATE_SUCCESS,
  STATUS_CREATE_FAIL,
  STATUS_FETCH_SOURCE_REQUEST,
  STATUS_FETCH_SOURCE_SUCCESS,
  STATUS_FETCH_SOURCE_FAIL,
  STATUS_DELETE_REQUEST,
  STATUS_DELETE_SUCCESS,
  STATUS_DELETE_FAIL,
  CONTEXT_FETCH_SUCCESS,
  STATUS_MUTE_SUCCESS,
  STATUS_UNMUTE_SUCCESS,
  STATUS_UNFILTER,
  createStatus,
  editStatus,
  fetchStatus,
  deleteStatus,
  updateStatus,
  fetchContext,
  fetchStatusWithContext,
  muteStatus,
  unmuteStatus,
  toggleMuteStatus,
  unfilterStatus,
  type StatusesAction,
};
