import { useEffect, useMemo, useRef, useState } from 'react';
import {
  type UseFormHandleSubmit,
  type UseFormReturn,
  useForm,
} from 'react-hook-form';
import { useMutation, useQuery } from 'react-query';
import { useNavigate } from 'react-router-dom';

import { type IGif } from '@giphy/js-types';
import { type Slice } from '@tiptap/pm/model';
import { type AxiosResponse } from 'axios';
import { isEqual } from 'lodash-es';
import { useDebounce } from '@material-hu/hooks/useDebounce';

import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { logEvent } from 'src/config/logging';
import {
  LARGE_POST_WARNING_THRESHOLD_B,
  MAX_POST_BODY_HTML_LENGTH,
  MAX_POST_BODY_TEXT_LENGTH,
  POLL_MIN_OPTIONS,
} from 'src/constants/posts';
import { useAuth } from 'src/contexts/JWTContext';
import { usePosts } from 'src/contexts/PostContext';
import useGeneralError from 'src/hooks/useGeneralError';
import usePermissions from 'src/hooks/usePermissions';
import NotificationAlert from 'src/pages/dashboard/feed/components/NotificationAlert';
import { feedKeys } from 'src/pages/dashboard/feed/queries';
import { feedRoutes } from 'src/pages/dashboard/feed/routes';
import { uploadAllAttachments } from 'src/services/attachments';
import { getLinkPreview } from 'src/services/previewLink';
import { EventName } from 'src/types/amplitude';
import {
  type Attachment,
  FileTypes,
  type FormFile as FormFileType,
} from 'src/types/attachments';
import { type PollState, PostState } from 'src/types/feed';
import { type GroupPost } from 'src/types/groups';
import { type MeInstance } from 'src/types/instance';
import { type Poll, type Post, type PostData, PostType } from 'src/types/posts';
import { type LinkPreviewResponse } from 'src/types/previewLink';
import { type SegmentationMapValues } from 'src/types/segmentation';
import { type MeUser, type TaggedUser } from 'src/types/user';
import {
  deleteFileAndReorderAttchaments,
  getCorrectPositionOfFile,
  getFilesTotalSize,
  getFileToPaste,
  isValidFilesSize,
} from 'src/utils/attachments';
import { bytesFrom } from 'src/utils/bytes';
import {
  clearComposerDraft,
  readComposerDraft,
  writeComposerDraft,
} from 'src/utils/composerDrafts';
import {
  buildPostData,
  extractTextFromHTML,
  getPlainTextForCharacterCount,
  getSegmentationMap,
} from 'src/utils/feed';
import { getAttachment } from 'src/utils/files';
import { useLokaliseTranslation as useTranslation } from 'src/utils/i18n';
import { UserPermissions } from 'src/utils/permissions';
import { createDefaultPollDeadline } from 'src/utils/poll';
import { getPreviewLink, getPreviewLinkHTML } from 'src/utils/previewLink';

const { CREATE_POLLS, CAN_SEGMENTATE } = UserPermissions;

export type PostForm = {
  body: string;
  bodyHtml: string;
  sendNotification: boolean;
  date: string;
  dateTimeSchedule: string;
  permissionId: number | null;
  groupId: number | null;
  authorFullName: string;
  groupTitle: string;
  toFeed: null | boolean;
};

export type UsePostingReturn = {
  instance: MeInstance | null;
  user: MeUser | null;
  openError: boolean;
  closeErrorDialog: () => void;
  form: UseFormReturn<PostForm>;
  handleSubmit: UseFormHandleSubmit<PostForm>;
  submit: (values: any) => Promise<void>;
  segmentationItems: Map<number, SegmentationMapValues[]>;
  handleSegmentateChange: (
    newSegmentation: Map<number, SegmentationMapValues[]>,
  ) => void;
  canSegmentate: boolean;
  imagesAttach: Attachment[];
  filesAttach: Attachment[];
  videosAttach: Attachment[];
  audiosAttach: Attachment[];
  taggedUsers: TaggedUser[];
  isSubmitting: boolean;
  handleChangeTaggedUsers: (updater: any) => void;
  handlePaste: (
    e: React.ClipboardEvent<HTMLInputElement>,
    slice: Slice,
  ) => void;
  handleAddEmoji: (emoji: string) => void;
  showPreviewLink: boolean;
  urls: string[];
  metadata: any;
  isLoadingPreviewLink: boolean;
  handleRemovePreviewLink: () => void;
  hasAttachments: boolean;
  gif: IGif | undefined;
  handleDelete: (file: Attachment, index: number) => void;
  handleRemoveGif: () => void;
  canAddPoll: boolean;
  withPoll: boolean;
  setWithPoll: (value: boolean) => void;
  handlePollUpdate: (options: string[], isSave?: boolean) => void;
  pollOptionsForEdit: string[];
  pollVotersVisible: boolean;
  handlePollVotersVisibilityChange: (visible: boolean) => void;
  pollDeadlineEnabled: boolean;
  pollEndsAt: Date | null;
  handlePollDeadlineChange: (
    deadlineEnabled: boolean,
    endsAt: Date | null,
  ) => void;
  handleAddFiles: (newFiles: FormFileType[]) => void;
  handleAddGif: (newGif: IGif) => void;
  handleAddMultimedia: (multimedia: FormFileType[]) => void;
  attachmentsDisabled: boolean;
  images: FormFileType[];
  videos: FormFileType[];
  files: FormFileType[];
  audios: FormFileType[];
  handleEditCancel: () => void;
  openCancelDialog: boolean;
  setOpenCancelDialog: (value: boolean) => void;
  handleOnDiscard: () => void;
  isValidPost: () => boolean;
  disabledSubmitButton: boolean;
  handleCheckNotificationAndSubmit: (canSendPush: boolean) => void;
  handleSchedulePost: (
    dateTime: Date,
    sendNotification: boolean,
    dontSubmit?: boolean,
  ) => void;
  handleChangePositionMultimedia: (attachments: FormFileType[]) => void;
  notificationAlertModal: React.JSX.Element;
  canRemovePoll: boolean;
  shouldClearPollWhenCancelling: boolean;
  pollState: PollState;
};

export type GenericUsePostingProps<PostBodyType> = {
  isEdit?: boolean;
  defaultBody?: string;
  defaultBodyHtml?: string;
  defaultLinkPreviews?: Record<string, LinkPreviewResponse | null>;
  defaultImages?: FormFileType[];
  defaultGif?: IGif;
  defaultVideos?: FormFileType[];
  defaultFiles?: FormFileType[];
  defaultAudios?: FormFileType[];
  deafultBodyAttributes?: TaggedUser[];
  defaultGroupTitle?: string;
  segmentation?: any;
  state?: string;
  poll?: Poll;
  request: (body: PostData) => Promise<AxiosResponse<PostBodyType>>;
  onSuccess?: (post: PostBodyType) => any;
  onError?: (error: unknown) => void;
  onScheduledPostView?: () => void;
  isGroups?: boolean;
  defaultPermissionId?: string;
  defaultGroupId?: string;
  defaultDateTimeSchedule?: string;
  defaultSendNotification?: boolean;
  isDraft?: boolean;
  isKeyUpdate?: boolean;
  showScheduledPostsViewButton?: boolean;
  draftKey?: string | null;
  /**
   * Optional opaque payload merged into the request body before submitting.
   * Used by the share-post flow to inject `metadata.SHARE_POST_ID` /
   * `SHARE_GROUP_POST_ID` without `usePosting` knowing about share semantics.
   */
  metadataPayload?: PostData['metadata'];
};

const usePosting = <PostBodyType extends Post | GroupPost = Post>(
  props: GenericUsePostingProps<PostBodyType>,
): UsePostingReturn => {
  const {
    isEdit = false,
    defaultBody = '',
    defaultBodyHtml = '',
    deafultBodyAttributes = [],
    defaultGroupTitle = '',
    defaultLinkPreviews = {},
    defaultImages = [],
    defaultGif,
    defaultVideos = [],
    defaultFiles = [],
    defaultAudios = [],
    segmentation,
    state,
    poll,
    request,
    onSuccess = () => null,
    onError = () => null,
    onScheduledPostView = () => null,
    isGroups = false,
    defaultPermissionId = null,
    defaultGroupId = null,
    defaultDateTimeSchedule = null,
    defaultSendNotification = false,
    isDraft = false,
    isKeyUpdate = false,
    showScheduledPostsViewButton = true,
    draftKey = null,
    metadataPayload,
  } = props;

  const [images, setImages] = useState<FormFileType[]>(defaultImages);
  const [gif, setGif] = useState(defaultGif);
  const [videos, setVideos] = useState<FormFileType[]>(defaultVideos);
  const [files, setFiles] = useState<FormFileType[]>(defaultFiles);
  const [audios, setAudios] = useState<FormFileType[]>(defaultAudios);
  const [linkPreviews, setLinkPreviews] =
    useState<Record<string, LinkPreviewResponse | null>>(defaultLinkPreviews);
  const [urls, setUrls] = useState<string[]>([]);
  const [withPoll, setWithPoll] = useState(false);
  const [pollOptions, setPollOptions] = useState<string[]>([]);
  const [pollVotersVisible, setPollVotersVisible] = useState(false);
  const [pollWasSaved, setPollWasSaved] = useState(false);
  const [pollDeadlineEnabled, setPollDeadlineEnabled] = useState(false);
  const [pollEndsAt, setPollEndsAt] = useState<Date | null>(null);
  const [originalPollOptions, setOriginalPollOptions] = useState<string[]>([]);
  const [originalWithPoll, setOriginalWithPoll] = useState(false);
  const [originalPollVotersVisible, setOriginalPollVotersVisible] =
    useState(true);
  const [originalPollEndsAt, setOriginalPollEndsAt] = useState<Date | null>(
    null,
  );
  const [taggedUsers, setTaggedUsers] = useState(deafultBodyAttributes);
  const [segmentationItems, setSegmentationItems] = useState<
    Map<number, SegmentationMapValues[]>
  >(getSegmentationMap(segmentation));
  const [openError, setOpenError] = useState(false);
  const [openCancelDialog, setOpenCancelDialog] = useState(false);
  const [openNotificationAlert, setOpenNotificationAlert] = useState(false);
  const [lastCursor, setLastCursor] = useState<number | null>(null);
  const hydratedDraftKeyRef = useRef<string | null>(null);
  // Avoid clearing a hydrated draft while the debounced value hasn't caught up yet.
  const suppressClearOnMountRef = useRef<boolean>(false);

  // Initialize poll state when editing existing post with poll
  useEffect(() => {
    if (isEdit && poll) {
      const canEditPoll = poll.totalAnswerCount === 0;
      const pollOptionsArray = poll.pollOptions.map(option => option.option);
      const votersVisible = !(poll.pollConfiguration?.isAnonymous ?? false); // Default to true (visible) for existing polls without this field
      const pollEndDate = poll.pollConfiguration?.endsAt
        ? new Date(poll.pollConfiguration.endsAt)
        : null;
      const hasDeadline = !!poll.pollConfiguration?.endsAt;

      if (canEditPoll) {
        setWithPoll(true);
        setPollOptions(pollOptionsArray);
        setPollVotersVisible(votersVisible);
        setPollDeadlineEnabled(hasDeadline);
        setPollEndsAt(pollEndDate);
        setOriginalPollOptions(pollOptionsArray);
        setOriginalWithPoll(true);
        setOriginalPollVotersVisible(votersVisible);
        setOriginalPollEndsAt(pollEndDate);
        setPollWasSaved(true);
      } else {
        // Set original state for non-editable polls but still show them
        setWithPoll(true); // Show the poll even if it can't be edited
        setPollOptions(pollOptionsArray);
        setPollVotersVisible(votersVisible);
        setPollDeadlineEnabled(hasDeadline);
        setPollEndsAt(pollEndDate);
        setOriginalWithPoll(true);
        setOriginalPollOptions(pollOptionsArray);
        setOriginalPollVotersVisible(votersVisible);
        setOriginalPollEndsAt(pollEndDate);
        setPollWasSaved(true);
      }
    } else {
      // No poll in original post
      setOriginalWithPoll(false);
      setOriginalPollOptions([]);
      setOriginalPollVotersVisible(false);
      setOriginalPollEndsAt(null);
    }
  }, [isEdit, poll]);

  const { t } = useTranslation('post');
  const navigate = useNavigate();
  const { enqueueSnackbar, closeSnackbar } = useHuSnackbar();
  const abortController = useRef(new AbortController());
  const { setIsPosting, setIsGroupsPosting } = usePosts();

  const postMutation = useMutation(
    // hasRichText just here to be grabbed by onSuccess
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    ({ body, hasRichText }: { body: PostData; hasRichText: boolean }) =>
      request(body),
    {
      onSuccess: (response, { hasRichText }) => {
        if (!isDraft && withPoll) {
          logEvent(EventName.POST_POLL_CREATE, {
            postId: response.data?.id,
            pollId: response.data?.poll?.id,
          });
        }

        if (taggedUsers?.length) {
          taggedUsers.forEach(elem => {
            logEvent(EventName.POST_MENTION, {
              postId: response.data?.id,
              userId: elem.type?.user.id,
            });
          });
        }

        const isScheduledPost = response.data.state === PostState.SCHEDULED;
        if (!isDraft && isScheduledPost) {
          let title = t('schedule_snackbar_success');
          if (isEdit) {
            title = t('edit_schedule_post_snackbar_success');
          }

          // Only show button that opens the drawer if approval is not required
          // When approval is required, the scheduled post won't be visible until approved
          const showViewButton = showScheduledPostsViewButton;

          enqueueSnackbar({
            title,
            variant: 'success',
            ...(showViewButton && {
              cancelAction: {
                text: t('view'),
                onClick: () => {
                  onScheduledPostView();
                },
              },
            }),
          });
        }

        if (draftKey) {
          clearComposerDraft(draftKey);
        }

        onSuccess(response.data);
        if (!isDraft) {
          const isFeedPost = response.data.type === PostType.FeedType;
          const event = isFeedPost
            ? EventName.FEED_POST_FORMATTED
            : EventName.GROUPS_POST_FORMATTED;

          if (hasRichText && bodyHtml !== defaultBodyHtml) {
            logEvent(event, {
              postId: response.data?.id,
              userId: response.data?.user?.id,
              groupId: !isFeedPost
                ? (response.data as GroupPost).groupId
                : undefined,
            });
          }
        }

        setTimeout(() => {
          resetForm();
        }, 1);
      },
      onError,
    },
  );

  const { hasAll: canAddPoll } = usePermissions([CREATE_POLLS]);
  const { hasAll: userCanSegmentate } = usePermissions([CAN_SEGMENTATE]);
  const showGeneralError = useGeneralError();
  const { instance, user } = useAuth();

  const form = useForm<PostForm>({
    defaultValues: {
      body: defaultBody,
      bodyHtml: defaultBodyHtml,
      sendNotification: defaultSendNotification,
      date: '',
      dateTimeSchedule: defaultDateTimeSchedule || '',
      // For drafts
      permissionId: defaultPermissionId ? Number(defaultPermissionId) : null,
      groupId: defaultGroupId ? Number(defaultGroupId) : null,
      toFeed: false,
      authorFullName: '',
      groupTitle: defaultGroupTitle,
    },
  });

  const {
    handleSubmit,
    watch,
    reset,
    setValue,
    setFocus,
    formState: { isSubmitting: isSubmittingForm },
  } = form;

  const body = watch('body');
  const bodyHtml = watch('bodyHtml');
  const debouncedBody = useDebounce(bodyHtml ?? body);

  const isSubmitting = isSubmittingForm || postMutation.isLoading;

  useEffect(() => {
    if (isEdit || !draftKey) {
      return;
    }

    if (hydratedDraftKeyRef.current === draftKey) {
      return;
    }

    hydratedDraftKeyRef.current = draftKey;
    const draftBodyHtml = readComposerDraft(draftKey);
    if (!draftBodyHtml) {
      return;
    }

    const currentBody = form.getValues('body');
    const currentBodyHtml = form.getValues('bodyHtml');
    const hasInitialValue =
      !!defaultBody || !!defaultBodyHtml || !!currentBody || !!currentBodyHtml;

    if (hasInitialValue) {
      return;
    }

    // Mark that we should not clear the draft until the debounced value reflects this hydration.
    suppressClearOnMountRef.current = true;
    setValue('bodyHtml', draftBodyHtml);
  }, [isEdit, draftKey, form, setValue, defaultBody, defaultBodyHtml]);

  useEffect(() => {
    if (isEdit || !draftKey) {
      return;
    }

    const draftBody = debouncedBody ?? '';
    const { plainText } = extractTextFromHTML(draftBody);
    const hasContent = !!plainText.trim() || !!draftBody.trim();
    const liveBodyValue =
      form.getValues('bodyHtml') || form.getValues('body') || '';
    const { plainText: livePlainText } = extractTextFromHTML(liveBodyValue);
    const liveHasContent = !!livePlainText.trim() || !!liveBodyValue.trim();

    // If we just hydrated a saved draft and debounce hasn't caught up yet,
    // skip clearing to avoid deleting the existing draft on mount.
    if (suppressClearOnMountRef.current && !hasContent && liveHasContent) {
      return;
    }
    if (suppressClearOnMountRef.current && (hasContent || !liveHasContent)) {
      suppressClearOnMountRef.current = false;
    }

    if (!hasContent) {
      clearComposerDraft(draftKey);
      return;
    }

    writeComposerDraft(draftKey, draftBody);
  }, [isEdit, draftKey, debouncedBody, form]);

  const hasDifference = () => {
    return (
      gif !== defaultGif ||
      body !== defaultBody ||
      bodyHtml !== defaultBodyHtml ||
      withPoll !== originalWithPoll ||
      pollVotersVisible !== originalPollVotersVisible ||
      pollEndsAt !== originalPollEndsAt ||
      !isEqual(images, defaultImages) ||
      !isEqual(videos, defaultVideos) ||
      !isEqual(files, defaultFiles) ||
      !isEqual(linkPreviews, defaultLinkPreviews) ||
      !isEqual(taggedUsers, deafultBodyAttributes) ||
      !isEqual(segmentationItems, getSegmentationMap(segmentation)) ||
      !isEqual(pollOptions, originalPollOptions)
    );
  };

  const hasDefaultLinkPreviews =
    !!defaultLinkPreviews && Object.values(defaultLinkPreviews)?.length > 0;
  const originalUrls = bodyHtml
    ? getPreviewLinkHTML(defaultBodyHtml)
    : getPreviewLink(defaultBody);

  const showPreviewLink =
    isEdit && originalUrls[0] === urls[0] ? hasDefaultLinkPreviews : !!urls[0];

  const { isLoading: isLoadingPreviewLink, data: metadata } = useQuery(
    feedKeys.linkPreview.detail(urls[0]),
    () => {
      addPreviewLink(null, urls[0]);
      return user ? getLinkPreview(urls[0], user.language) : null;
    },
    {
      onSuccess: response => {
        if (response?.data) {
          const { data } = response;
          addPreviewLink(data, urls[0]);
        }
      },
      onError: error => {
        showGeneralError(error, t('warning_link_preview'), false);
        setUrls([]);
      },
      enabled: showPreviewLink,
    },
  );

  useEffect(() => {
    const getPossibleLinks = bodyHtml
      ? getPreviewLinkHTML(debouncedBody)
      : getPreviewLink(debouncedBody);
    setUrls(getPossibleLinks);
  }, [debouncedBody]);

  useEffect(() => {
    if (urls.length === 0) {
      setLinkPreviews({});
    }
  }, [urls.length]);

  useEffect(() => {
    if (body !== '') {
      const input = document.getElementById('postAddInput') as HTMLInputElement;
      setFocus('body');
      if (lastCursor) {
        input.setSelectionRange(lastCursor, lastCursor);
        setLastCursor(null);
      }
    }
  }, [body]);

  const resetAttachments = () => {
    setImages([]);
    setFiles([]);
    setVideos([]);
    setGif(undefined);
  };

  const resetForm = () => {
    reset();
    resetAttachments();
    setPollOptions([]);
    setWithPoll(false);
    setPollWasSaved(false);
    setOriginalPollOptions([]);
    setOriginalWithPoll(false);
    setPollVotersVisible(false);
    setOriginalPollVotersVisible(false);
    setPollDeadlineEnabled(false);
    setPollEndsAt(null);
    setOriginalPollEndsAt(null);
    setUrls([]);
    setSegmentationItems(new Map());
  };

  const handleSchedulePost = (
    dateTime: Date,
    sendNotificationValue: boolean,
    dontSubmit = false,
  ) => {
    setValue('sendNotification', sendNotificationValue);
    setValue('dateTimeSchedule', dateTime.toISOString());
    if (!dontSubmit) {
      handleSubmit(submit)();
    }
  };

  const setSendNotification = (value: boolean) => {
    setValue('sendNotification', value);
  };

  const handleSendNotificationAndSubmit = (value: boolean) => {
    setSendNotification(value);
    handleSubmit(submit)();
  };

  const currentAttachments: FormFileType[] = [
    ...(files?.length > 0 ? files : []),
    ...(videos?.length > 0 ? videos : []),
    ...(images?.length > 0 ? images : []),
    ...(audios?.length > 0 ? audios : []),
  ];

  const submit = async (values: PostForm) => {
    const currentAbortController = new AbortController();
    abortController.current = currentAbortController;
    let snackbarKey;
    try {
      if (!isValidAttachments()) {
        openErrorDialog();
        return;
      }

      if (isGroups) {
        setIsGroupsPosting(true);
      } else {
        setIsPosting(true);
      }

      const { plainText, mentions, hasRichText } = extractTextFromHTML(
        values.bodyHtml,
      );
      const validMentions: TaggedUser[] = mentions
        .filter(mention => mention.type.user.id !== null)
        .map(mention => ({
          ...mention,
          type: {
            user: {
              id: mention.type.user.id as number,
            },
          },
        }));
      const bodyTextLengthToValidate = values.bodyHtml
        ? getPlainTextForCharacterCount(values.bodyHtml).length
        : values.body.length;

      if (bodyTextLengthToValidate > MAX_POST_BODY_TEXT_LENGTH) {
        enqueueSnackbar({
          title: t('validations:max_length', {
            count: MAX_POST_BODY_TEXT_LENGTH,
          }),
          variant: 'error',
        });
        return;
      }

      if (values.bodyHtml.length > MAX_POST_BODY_HTML_LENGTH) {
        enqueueSnackbar({
          title: `${t('material_hu_only:top_bar_rich_text_editor.html')}: ${t(
            'validations:max_length',
            {
              count: MAX_POST_BODY_HTML_LENGTH,
            },
          )}`,
          variant: 'error',
        });
        return;
      }

      // Check if post is too large
      const bytes = getFilesTotalSize(currentAttachments);
      if (bytes > LARGE_POST_WARNING_THRESHOLD_B) {
        snackbarKey = enqueueSnackbar({
          title: t('creating_large_post_title'),
          description: t('creating_large_post_description'),
          variant: 'info',
          autoHideDuration: 999_999,
          cancelAction: {
            text: t('cancel'),
            onClick: () => {
              currentAbortController.abort();
            },
          },
        });
      }

      const [uploadFiles, uploadImages, uploadVideos, uploadAudios] =
        await uploadAllAttachments(files, images, videos, audios, {
          signal: currentAbortController.signal,
        });

      const formData = buildPostData({
        body: values.bodyHtml ? plainText : values.body,
        bodyHtml: values.bodyHtml,
        taggedUsers: values.bodyHtml ? validMentions : taggedUsers,
        segmentationItems,
        dateTimeSchedule: values.dateTimeSchedule || undefined,
        uploadImages,
        uploadFiles,
        uploadVideos,
        uploadAudios,
        sendNotification: values.sendNotification,
        gif,
        withPoll,
        pollOptions,
        pollVotersVisible,
        pollEndsAt:
          pollDeadlineEnabled && pollEndsAt ? pollEndsAt.toISOString() : null,
        linkPreviews,
      });
      const { poll, ...baseFormData } = formData;
      const postData: PostData = {
        ...baseFormData,
        sendNotification: values.sendNotification,
        publicationDatetime: formData.publicationDatetime ?? undefined,
        ...(poll && {
          poll: {
            ...poll,
            pollOptions: poll.pollOptions ?? [],
          },
        }),
      };

      const bodyWithMetadata: PostData = metadataPayload
        ? { ...postData, metadata: metadataPayload }
        : postData;

      postMutation.mutate({ body: bodyWithMetadata, hasRichText });
    } catch (error: unknown) {
      if ((error as any)?.code === 'ERR_CANCELED') {
        enqueueSnackbar({
          title: t('canceled_post'),
          variant: 'info',
        });
      } else {
        onError(error);
      }
    } finally {
      setIsPosting(false);
      setIsGroupsPosting(false);
      if (snackbarKey) {
        closeSnackbar(snackbarKey);
      }
    }
  };

  const openErrorDialog = () => setOpenError(true);
  const closeErrorDialog = () => setOpenError(false);

  const handleAddFiles = (newFiles: FormFileType[]) => {
    const audioFiles = newFiles.filter(f => f.mime?.startsWith('audio/'));
    const videoFiles = newFiles.filter(f => f.mime?.startsWith('video/'));
    const imageFiles = newFiles.filter(f => f.mime?.startsWith('image/'));
    const otherFiles = newFiles.filter(
      f =>
        !f.mime?.startsWith('audio/') &&
        !f.mime?.startsWith('video/') &&
        !f.mime?.startsWith('image/'),
    );

    if (audioFiles.length > 0) {
      setAudios(prev => [
        ...prev,
        ...audioFiles.map(f => ({ ...f, type: FileTypes.AUDIO })),
      ]);
    }
    if (videoFiles.length > 0) {
      const currentImages = [...images];
      const currentVideos = [...videos];
      const newVideoFiles = videoFiles.map((f, index) => {
        const position = getCorrectPositionOfFile(
          currentImages,
          currentVideos,
          index,
        );
        currentVideos.push({ position, ...f, type: FileTypes.VIDEO });
        return { position, ...f, type: FileTypes.VIDEO };
      });
      setVideos(prev => [...prev, ...newVideoFiles]);
    }
    if (imageFiles.length > 0) {
      const currentImages = [...images];
      const currentVideos = [...videos];
      const newImageFiles = imageFiles.map((f, index) => {
        const position = getCorrectPositionOfFile(
          currentImages,
          currentVideos,
          index,
        );
        currentImages.push({ position, ...f, type: FileTypes.IMAGE });
        return { position, ...f, type: FileTypes.IMAGE };
      });
      setImages(prev => [...prev, ...newImageFiles]);
    }
    if (otherFiles.length > 0) {
      setFiles(prev => [...prev, ...otherFiles]);
    }
  };

  const handleAddMultimedia = (multimedia: FormFileType[]) => {
    const image = [...images];
    const video = [...videos];
    const audio = [...audios];
    multimedia.forEach((elem: FormFileType, index: number) => {
      const position = getCorrectPositionOfFile(image, video, index);
      if (elem.type === FileTypes.IMAGE) {
        image.push({ position, ...elem });
      } else if (elem.type === FileTypes.VIDEO) {
        video.push({ position, ...elem });
      } else if (elem.type === FileTypes.AUDIO) {
        audio.push(elem);
      }
    });
    setImages([...image]);
    setVideos([...video]);
    setAudios([...audio]);
  };

  const handleChangePositionMultimedia = (attachments: FormFileType[]) => {
    const updatedImages = images.map(image => {
      const reorderedIndex = attachments.findIndex(
        (attachment: FormFileType) => attachment.name === image.name,
      );
      return { ...image, position: reorderedIndex };
    });

    const updatedVideos = videos.map(video => {
      const reorderedIndex = attachments.findIndex(
        (attachment: FormFileType) => attachment.name === video.name,
      );
      return { ...video, position: reorderedIndex };
    });

    setImages([...updatedImages]);
    setVideos([...updatedVideos]);
  };

  const handleAddGif = (newGif: IGif) => setGif(newGif);

  const handleRemoveGif = () => setGif(undefined);

  const handleDelete = (attachment: Attachment, index: number) => {
    if (attachment.type === FileTypes.FILE) {
      const newFiles = [...files];
      newFiles.splice(index, 1);
      setFiles(newFiles);
    } else if (attachment.type === FileTypes.AUDIO) {
      const newAudios = [...audios];
      newAudios.splice(index, 1);
      setAudios(newAudios);
    } else {
      const { newImages, newVideos } = deleteFileAndReorderAttchaments(
        images,
        videos,
        attachment,
      );
      setImages(newImages);
      setVideos(newVideos);
    }
  };

  const handleSegmentateChange = (
    newSegmentation: Map<number, SegmentationMapValues[]>,
  ) => {
    setSegmentationItems(newSegmentation);
  };

  const handlePollUpdate = (
    options: string[],
    isSave: boolean = false,
  ): void => {
    setPollOptions(options);
    // Mark poll as saved when save is called explicitly
    if (
      isSave &&
      options.length > 0 &&
      options.some(option => option.trim().length > 0)
    ) {
      setPollWasSaved(true);
    }
  };

  const handlePollVotersVisibilityChange = (visible: boolean): void => {
    setPollVotersVisible(visible);
  };

  const handlePollDeadlineChange = (
    deadlineEnabled: boolean,
    endsAt: Date | null,
  ): void => {
    setPollDeadlineEnabled(deadlineEnabled);

    // If deadline is being enabled and no date is set, set default to one day from now
    if (deadlineEnabled && !endsAt) {
      const oneDayFromNow = createDefaultPollDeadline();
      setPollEndsAt(oneDayFromNow);
    } else {
      setPollEndsAt(endsAt);
    }
  };

  const canRemovePoll = !originalWithPoll;

  const shouldClearPollWhenCancelling = !pollWasSaved;

  const handleSetWithPoll = (value: boolean) => {
    if (!value && originalWithPoll) {
      return; // Don't allow removing existing polls
    }
    setWithPoll(value);
  };

  const pollState = useMemo((): PollState => {
    if (isEdit && poll && poll.totalAnswerCount > 0) {
      // Poll has votes - disable everything
      return {
        form: {
          disabled: true,
          message: t('cannot_edit_poll_with_votes'),
        },
        cancel: {
          disabled: true,
          message: t('cannot_edit_poll_with_votes'),
        },
      };
    }
    return {
      form: {
        disabled: false,
        message: '',
      },
      cancel: {
        disabled: false,
        message: '',
      },
    };
  }, [isEdit, poll, t]);

  const handleEditCancel = () => {
    if (!hasDifference()) {
      if (state === PostState.SCHEDULED) {
        navigate(feedRoutes.feed(), { state: { id: 'schedulePost' } });
      } else {
        navigate(-1);
      }
    } else {
      setOpenCancelDialog(true);
    }
  };

  const handleOnDiscard = () => {
    if (state === PostState.SCHEDULED) {
      navigate(feedRoutes.feed(), { state: { id: 'schedulePost' } });
    } else {
      navigate(-1);
    }
    setOpenCancelDialog(false);
  };

  const isValidPost = (): boolean => {
    const hasBody = bodyHtml?.length > 0 || body?.length > 0;

    if (withPoll) {
      return (
        pollOptions.length >= POLL_MIN_OPTIONS &&
        pollOptions.every(option => option?.length > 0) &&
        hasBody
      );
    }

    return hasBody || hasAttachments;
  };

  const isValidAttachments = (): boolean => {
    return (
      !!instance &&
      isValidFilesSize(
        currentAttachments,
        bytesFrom(instance.maxPostSizeInMB, 'MB'),
      )
    );
  };

  const imagesAttach = useMemo(() => images.map(getAttachment), [images]);
  const videosAttach = useMemo(() => videos.map(getAttachment), [videos]);
  const filesAttach = useMemo(() => files.map(getAttachment), [files]);
  const audiosAttach = useMemo(() => audios.map(getAttachment), [audios]);

  const hasAttachments =
    imagesAttach?.length > 0 ||
    videosAttach?.length > 0 ||
    filesAttach?.length > 0 ||
    audiosAttach?.length > 0 ||
    !!gif;

  const handleChangeTaggedUsers = (
    updater: (prev: TaggedUser[]) => TaggedUser[],
  ) => setTaggedUsers(updater);

  const handleAddImage = (imageParam: FormFileType) => {
    setImages(prevImages => [
      ...prevImages,
      { position: getCorrectPositionOfFile(prevImages, videos), ...imageParam },
    ]);
  };

  const handlePaste = async (event: React.ClipboardEvent<HTMLInputElement>) => {
    event.preventDefault();

    const newFile = await getFileToPaste(event);
    if (newFile) {
      handleAddImage(newFile);
    }
    return true;
  };

  const handleOnCloseNotificationAlert = () => setOpenNotificationAlert(false);

  const handleCheckNotificationAndSubmit = (canSendPush: boolean) => {
    if (!isEdit && canSendPush) {
      setOpenNotificationAlert(true);
    } else {
      handleSubmit(submit)();
    }
  };

  const handleAddEmoji = (emoji: string) => {
    const input = document.getElementById('postAddInput') as HTMLInputElement;

    const start = Math.min(input.selectionStart || 0, input.selectionEnd || 0);
    const end = Math.max(input.selectionStart || 0, input.selectionEnd || 0);

    const textStart = body.slice(0, start);
    const textEnd = body.slice(end);

    setValue('body', textStart + emoji + textEnd);
    setLastCursor(start + 1 + emoji.length);
  };

  const addPreviewLink = (
    newPreviewLink: LinkPreviewResponse | null,
    url: string,
  ) => setLinkPreviews({ [url]: newPreviewLink });

  const handleRemovePreviewLink = () => {
    setUrls(prev => [...prev.filter(elem => elem !== urls[0])]);
    setLinkPreviews({});
  };

  const attachmentsDisabled = isSubmitting || !isValidAttachments() || !!gif;

  const disabledSubmitButton =
    !isValidPost() || isSubmitting || (isEdit && !hasDifference());

  const notificationAlertModal = (
    <NotificationAlert
      open={openNotificationAlert}
      onClose={handleOnCloseNotificationAlert}
      onSendNotification={handleSendNotificationAndSubmit}
      isGroups={isGroups}
    />
  );
  const pollOptionsForEdit = withPoll ? pollOptions : [];

  return {
    instance,
    user,
    openError,
    closeErrorDialog,
    form,
    handleSubmit,
    submit,
    segmentationItems,
    handleSegmentateChange,
    canSegmentate: userCanSegmentate && !isKeyUpdate,
    imagesAttach,
    filesAttach,
    videosAttach,
    audiosAttach,
    taggedUsers,
    isSubmitting,
    handleChangeTaggedUsers,
    handlePaste,
    handleAddEmoji,
    showPreviewLink,
    urls,
    metadata,
    isLoadingPreviewLink,
    handleRemovePreviewLink,
    hasAttachments,
    gif,
    handleDelete,
    handleRemoveGif,
    canAddPoll,
    withPoll,
    setWithPoll: handleSetWithPoll,
    handlePollUpdate,
    pollOptionsForEdit,
    pollVotersVisible,
    handlePollVotersVisibilityChange,
    pollDeadlineEnabled,
    pollEndsAt,
    handlePollDeadlineChange,
    handleAddFiles,
    handleAddGif,
    handleAddMultimedia,
    attachmentsDisabled,
    images,
    videos,
    files,
    audios,
    handleEditCancel,
    openCancelDialog,
    setOpenCancelDialog,
    handleOnDiscard,
    isValidPost,
    disabledSubmitButton,
    handleCheckNotificationAndSubmit,
    handleSchedulePost,
    handleChangePositionMultimedia,
    notificationAlertModal,
    canRemovePoll,
    shouldClearPollWhenCancelling,
    pollState,
  };
};

export default usePosting;
