import {
  useCallback, useEffect, useMemo, useRef, useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useToggle } from '@openedx/paragon';
import { camelCaseObject } from '@edx/frontend-platform/utils';

import { RequestStatus } from '../data/constants';
import { useClipboard } from '../generic/clipboard';
import { useEventListener } from '../generic/hooks';
import { COURSE_BLOCK_NAMES, iframeMessageTypes } from '../constants';
import { messageTypes, PUBLISH_TYPES } from './constants';
import {
  createNewCourseXBlock,
  deleteUnitItemQuery,
  duplicateUnitItemQuery,
  editCourseItemQuery,
  editCourseUnitVisibilityAndData,
  fetchCourseSectionVerticalData,
  fetchCourseVerticalChildrenData,
  getCourseOutlineInfoQuery,
  patchUnitItemQuery,
} from './data/thunk';
import {
  getCanEdit,
  getCourseOutlineInfo,
  getCourseSectionVertical,
  getCourseUnitData,
  getCourseVerticalChildren,
  getErrorMessage,
  getIsLoading,
  getMovedXBlockParams,
  getSavingStatus,
  getSequenceStatus,
  getStaticFileNotices,
} from './data/selectors';
import {
  changeEditTitleFormOpen,
  updateMovedXBlockParams,
  updateQueryPendingStatus,
} from './data/slice';
import { useIframe } from '../generic/hooks/context/hooks';

export const useCourseUnit = ({ courseId, blockId }) => {
  const dispatch = useDispatch();
  const [searchParams] = useSearchParams();
  const { sendMessageToIframe } = useIframe();
  const [addComponentTemplateData, setAddComponentTemplateData] = useState({});
  const [isMoveModalOpen, openMoveModal, closeMoveModal] = useToggle(false);

  const courseUnit = useSelector(getCourseUnitData);
  const savingStatus = useSelector(getSavingStatus);
  const isLoading = useSelector(getIsLoading);
  const errorMessage = useSelector(getErrorMessage);
  const sequenceStatus = useSelector(getSequenceStatus);
  const { draftPreviewLink, publishedPreviewLink, xblockInfo = {} } = useSelector(getCourseSectionVertical);
  const courseVerticalChildren = useSelector(getCourseVerticalChildren);
  const staticFileNotices = useSelector(getStaticFileNotices);
  const navigate = useNavigate();
  const isTitleEditFormOpen = useSelector(state => state.courseUnit.isTitleEditFormOpen);
  const canEdit = useSelector(getCanEdit);
  const courseOutlineInfo = useSelector(getCourseOutlineInfo);
  const movedXBlockParams = useSelector(getMovedXBlockParams);
  const { currentlyVisibleToStudents } = courseUnit;
  const { sharedClipboardData, showPasteXBlock, showPasteUnit } = useClipboard(canEdit);
  const { canPasteComponent } = courseVerticalChildren;
  const { displayName: unitTitle, category: unitCategory } = xblockInfo;
  const sequenceId = courseUnit.ancestorInfo?.ancestors[0].id;
  const isUnitVerticalType = unitCategory === COURSE_BLOCK_NAMES.vertical.id;
  const isUnitLibraryType = unitCategory === COURSE_BLOCK_NAMES.libraryContent.id;
  const isSplitTestType = unitCategory === COURSE_BLOCK_NAMES.splitTest.id;

  const headerNavigationsActions = {
    handleViewLive: () => {
      window.open(publishedPreviewLink, '_blank');
    },
    handlePreview: () => {
      window.open(draftPreviewLink, '_blank');
    },
    handleEdit: () => {
      sendMessageToIframe(messageTypes.editXBlock, { id: courseUnit.id }, window);
    },
  };

  const handleTitleEdit = () => {
    dispatch(changeEditTitleFormOpen(!isTitleEditFormOpen));
  };

  const handleConfigureSubmit = (id, isVisible, groupAccess, isDiscussionEnabled, closeModalFn) => {
    dispatch(editCourseUnitVisibilityAndData(
      id,
      PUBLISH_TYPES.republish,
      isVisible,
      groupAccess,
      isDiscussionEnabled,
      () => sendMessageToIframe(messageTypes.completeManageXBlockAccess, { locator: id }),
      blockId,
    ));
    if (typeof closeModalFn === 'function') {
      closeModalFn();
    }
  };

  const handleTitleEditSubmit = (displayName) => {
    if (unitTitle !== displayName) {
      dispatch(editCourseItemQuery(blockId, displayName, sequenceId));
    }

    handleTitleEdit();
  };

  const handleNavigate = (id) => {
    if (sequenceId) {
      const path = `/course/${courseId}/container/${blockId}/${id}`;
      const options = { replace: true };
      if (searchParams.size) {
        navigate({
          pathname: path,
          search: `?${searchParams}`,
        }, options);
      } else {
        navigate(path, options);
      }
    }
  };

  const handleCreateNewCourseXBlock = (body, callback) => (
    dispatch(createNewCourseXBlock(body, callback, blockId, sendMessageToIframe))
  );

  const unitXBlockActions = {
    handleDelete: (XBlockId) => {
      dispatch(deleteUnitItemQuery(blockId, XBlockId, sendMessageToIframe));
    },
    handleDuplicate: (XBlockId) => {
      dispatch(duplicateUnitItemQuery(
        blockId,
        XBlockId,
        (courseKey, locator) => sendMessageToIframe(messageTypes.completeXBlockDuplicating, { courseKey, locator }),
      ));
    },
  };

  const handleRollbackMovedXBlock = () => {
    const {
      sourceLocator, targetParentLocator, title, currentParentLocator,
    } = movedXBlockParams;
    dispatch(patchUnitItemQuery({
      sourceLocator,
      targetParentLocator,
      title,
      currentParentLocator,
      isMoving: false,
      callbackFn: () => {
        sendMessageToIframe(messageTypes.rollbackMovedXBlock, { locator: sourceLocator });
        window.scrollTo({ top: 0, behavior: 'smooth' });
      },
    }));
  };

  const handleCloseXBlockMovedAlert = () => {
    dispatch(updateMovedXBlockParams({ isSuccess: false }));
  };

  const handleNavigateToTargetUnit = () => {
    navigate(`/course/${courseId}/container/${movedXBlockParams.targetParentLocator}`);
  };

  const receiveMessage = useCallback(({ data }) => {
    const { payload, type } = data;

    if (type === messageTypes.handleViewXBlockContent) {
      const { usageId } = payload;
      navigate(`/course/${courseId}/container/${usageId}/${sequenceId}`);
    }

    if (type === messageTypes.handleViewGroupConfigurations) {
      const { usageId } = payload;
      const groupId = usageId.split('#').pop();
      navigate(`/course/${courseId}/group_configurations#${groupId}`);
    }

    if (type === messageTypes.showComponentTemplates) {
      setAddComponentTemplateData(camelCaseObject(payload));
    }
  }, [courseId, sequenceId]);

  useEventListener('message', receiveMessage);

  useEffect(() => {
    if (savingStatus === RequestStatus.SUCCESSFUL) {
      dispatch(updateQueryPendingStatus(true));
    }
  }, [savingStatus]);

  useEffect(() => {
    dispatch(fetchCourseSectionVerticalData(blockId, sequenceId));
    dispatch(fetchCourseVerticalChildrenData(blockId, isSplitTestType));
    handleNavigate(sequenceId);
    dispatch(updateMovedXBlockParams({ isSuccess: false }));
  }, [courseId, blockId, sequenceId]);

  useEffect(() => {
    if (isSplitTestType) {
      dispatch(fetchCourseVerticalChildrenData(blockId, isSplitTestType));
    }
  }, [isSplitTestType, blockId]);

  useEffect(() => {
    if (isMoveModalOpen && !Object.keys(courseOutlineInfo).length) {
      dispatch(getCourseOutlineInfoQuery(courseId));
    }
  }, [isMoveModalOpen]);

  return {
    sequenceId,
    courseUnit,
    unitTitle,
    unitCategory,
    errorMessage,
    sequenceStatus,
    savingStatus,
    staticFileNotices,
    currentlyVisibleToStudents,
    isLoading,
    isTitleEditFormOpen,
    isUnitVerticalType,
    isUnitLibraryType,
    isSplitTestType,
    sharedClipboardData,
    showPasteXBlock,
    showPasteUnit,
    unitXBlockActions,
    headerNavigationsActions,
    handleTitleEdit,
    handleTitleEditSubmit,
    handleCreateNewCourseXBlock,
    handleConfigureSubmit,
    courseVerticalChildren,
    canPasteComponent,
    isMoveModalOpen,
    openMoveModal,
    closeMoveModal,
    handleRollbackMovedXBlock,
    handleCloseXBlockMovedAlert,
    movedXBlockParams,
    handleNavigateToTargetUnit,
    addComponentTemplateData,
    setAddComponentTemplateData,
  };
};

/**
 * Custom hook to determine the layout grid configuration based on unit category and type.
 *
 * @param {string} unitCategory - The category of the unit. This may influence future layout logic.
 * @param {boolean} isUnitLibraryType - A flag indicating whether the unit is of library content type.
 * @returns {Object} - An object representing the layout configuration for different screen sizes.
 *                     The configuration includes keys like 'lg', 'md', 'sm', 'xs', and 'xl',
 *                     each specifying an array of layout spans.
 */
export const useLayoutGrid = (unitCategory, isUnitLibraryType) => (
  useMemo(() => {
    const layouts = {
      fullWidth: {
        lg: [{ span: 12 }, { span: 0 }],
        md: [{ span: 12 }, { span: 0 }],
        sm: [{ span: 12 }, { span: 0 }],
        xs: [{ span: 12 }, { span: 0 }],
        xl: [{ span: 12 }, { span: 0 }],
      },
      default: {
        lg: [{ span: 8 }, { span: 4 }],
        md: [{ span: 8 }, { span: 4 }],
        sm: [{ span: 8 }, { span: 3 }],
        xs: [{ span: 9 }, { span: 3 }],
        xl: [{ span: 9 }, { span: 3 }],
      },
    };

    return isUnitLibraryType ? layouts.fullWidth : layouts.default;
  }, [unitCategory])
);

/**
 * Custom hook that restores the scroll position from `localStorage` after a page reload.
 * It listens for a `plugin.resize` message event and scrolls the window to the saved position
 * after a 1-second delay, provided no new resize messages are received during that time.
 *
 * @param {string} [storageKey='createXBlockLastYPosition'] -
 * The key used to store the last scroll position in `localStorage`.
 */
export const useScrollToLastPosition = (storageKey = 'createXBlockLastYPosition') => {
  const timeoutRef = useRef(null);
  const [hasLastPosition, setHasLastPosition] = useState(() => !!localStorage.getItem(storageKey));

  const scrollToLastPosition = useCallback(() => {
    const lastYPosition = localStorage.getItem(storageKey);
    if (!lastYPosition) {
      setHasLastPosition(false);
      return;
    }

    const yPosition = parseInt(lastYPosition, 10);
    if (!Number.isNaN(yPosition)) {
      window.scrollTo({ top: yPosition, behavior: 'smooth' });
      localStorage.removeItem(storageKey);
      setHasLastPosition(false);
    }
  }, [storageKey]);

  const handleMessage = useCallback((event) => {
    if (event.data?.type === iframeMessageTypes.resize) {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }

      timeoutRef.current = setTimeout(scrollToLastPosition, 1000);
    }
  }, [scrollToLastPosition]);

  useEffect(() => {
    if (!hasLastPosition) {
      return undefined;
    }

    window.addEventListener('message', handleMessage);

    return () => {
      window.removeEventListener('message', handleMessage);
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, [hasLastPosition, handleMessage]);

  return null;
};
